Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f9011a167 | ||
|
|
ae004c8a6c | ||
|
|
ccbb16fe76 | ||
|
|
b77ce4a010 | ||
|
|
f72f709e34 | ||
|
|
37ceb25f8c | ||
|
|
0141d8f954 | ||
|
|
47fe791006 | ||
|
|
0cbaba73e4 | ||
|
|
aaac491dc7 | ||
|
|
fc4203e963 | ||
|
|
8430e9612e | ||
|
|
de81934619 | ||
|
|
5aedcbee55 | ||
|
|
7bc8d7e991 | ||
|
|
df183f5882 | ||
|
|
27d9fad292 | ||
|
|
bed798d440 | ||
|
|
e5a8a4e34d | ||
|
|
de446db434 | ||
|
|
81546412bf | ||
|
|
459530fba3 | ||
|
|
42766b42c0 | ||
|
|
38b3563350 | ||
|
|
37159914cf | ||
|
|
268bf68532 | ||
|
|
b02c7e01d5 | ||
|
|
45d10a3bc4 | ||
|
|
9250b20c1d | ||
|
|
d9da311141 | ||
|
|
89a52dad21 | ||
|
|
2a1acc1294 | ||
|
|
8302990f84 | ||
|
|
5a03b61d63 | ||
|
|
6b6f239936 | ||
|
|
9c2748e8e9 | ||
|
|
bb5cc33737 | ||
|
|
5616a64827 | ||
|
|
33530ad8c8 | ||
|
|
fe14839f1b | ||
|
|
15123c1aa5 | ||
|
|
71ee9c72fe | ||
|
|
5503539856 | ||
|
|
6548f01efa | ||
|
|
518a4484d6 | ||
|
|
3535a25a59 | ||
|
|
06cc6741e6 | ||
|
|
ed1478e1ba | ||
|
|
854c05d673 | ||
|
|
bcf5fb18c5 | ||
|
|
3f822e814b | ||
|
|
4fd25c7215 | ||
|
|
f42bed2a55 | ||
|
|
469c205033 | ||
|
|
31551f3b9e | ||
|
|
69925af17c | ||
|
|
2502868ebc |
@@ -0,0 +1,42 @@
|
|||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
|
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-outside-of-docker
|
||||||
|
{
|
||||||
|
"name": "Docker outside of Docker",
|
||||||
|
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/base:bullseye",
|
||||||
|
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/docker-from-docker:1": {
|
||||||
|
"version": "latest",
|
||||||
|
"enableNonRootDocker": "true",
|
||||||
|
"moby": "true"
|
||||||
|
},
|
||||||
|
"ghcr.io/devcontainers/features/node:1": {},
|
||||||
|
"ghcr.io/devcontainers-contrib/features/npm-package:1": {},
|
||||||
|
"ghcr.io/devcontainers-contrib/features/jest:2": {},
|
||||||
|
"ghcr.io/devcontainers-contrib/features/prisma:2": {},
|
||||||
|
"ghcr.io/guiyomh/features/vim:0": {}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Use this environment variable if you need to bind mount your local source code into a new container.
|
||||||
|
"remoteEnv": {
|
||||||
|
"LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}"
|
||||||
|
},
|
||||||
|
|
||||||
|
"hostRequirements": {
|
||||||
|
"cpus": 4,
|
||||||
|
"memory": "8gb"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
"postCreateCommand": "./deploy/install.sh"
|
||||||
|
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
// "forwardPorts": [],
|
||||||
|
|
||||||
|
// Configure tool-specific properties.
|
||||||
|
// "customizations": {},
|
||||||
|
|
||||||
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
|
// "remoteUser": "root"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
🚀 Welcome to Cal.com! Try typing 'yarn dx' to get a quick dev envionment.
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
<!-- PROJECT LOGO -->
|
<!-- PROJECT LOGO -->
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/calcom/cal.com">
|
<a href="https://github.com/calcom/cal.com">
|
||||||
<img src="https://user-images.githubusercontent.com/8019099/133430653-24422d2a-3c8d-4052-9ad6-0580597151ee.png" alt="Logo">
|
<img src="https://user-images.githubusercontent.com/8019099/210054112-5955e812-a76e-4160-9ddd-58f2c72f1cce.png" alt="Logo">
|
||||||
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<h3 align="center">Cal.com (formerly Calendso)</h3>
|
<h3 align="center">Cal.com (formerly Calendso)</h3>
|
||||||
@@ -37,7 +36,7 @@
|
|||||||
<a href="https://hub.docker.com/r/calendso/calendso"><img src="https://img.shields.io/docker/pulls/calendso/calendso"></a>
|
<a href="https://hub.docker.com/r/calendso/calendso"><img src="https://img.shields.io/docker/pulls/calendso/calendso"></a>
|
||||||
<a href="https://twitter.com/calcom"><img src="https://img.shields.io/twitter/follow/calcom?style=flat"></a>
|
<a href="https://twitter.com/calcom"><img src="https://img.shields.io/twitter/follow/calcom?style=flat"></a>
|
||||||
<a href="https://twitch.tv/calcomtv"><img src="https://img.shields.io/twitch/status/calcomtv?style=flat"></a>
|
<a href="https://twitch.tv/calcomtv"><img src="https://img.shields.io/twitch/status/calcomtv?style=flat"></a>
|
||||||
<a href="https://github.com/orgs/calcom/projects/9"><img src="https://img.shields.io/badge/Help%20Wanted-Contribute-blue"></a>
|
<a href="https://github.com/calcom/cal.com/issues?q=is:issue+is:open+label:%22%F0%9F%99%8B%F0%9F%8F%BB%E2%80%8D%E2%99%82%EF%B8%8Fhelp+wanted%22"><img src="https://img.shields.io/badge/Help%20Wanted-Contribute-blue"></a>
|
||||||
<a href="https://cal.com/figma"><img src="https://img.shields.io/badge/Figma-Design%20System-blueviolet"></a>
|
<a href="https://cal.com/figma"><img src="https://img.shields.io/badge/Figma-Design%20System-blueviolet"></a>
|
||||||
<a href="https://calendso.slack.com/archives/C02BY67GMMW"><img src="https://img.shields.io/badge/translations-contribute-brightgreen" /></a>
|
<a href="https://calendso.slack.com/archives/C02BY67GMMW"><img src="https://img.shields.io/badge/translations-contribute-brightgreen" /></a>
|
||||||
<a href="https://www.contributor-covenant.org/version/1/4/code-of-conduct/ "><img src="https://img.shields.io/badge/Contributor%20Covenant-1.4-purple" /></a>
|
<a href="https://www.contributor-covenant.org/version/1/4/code-of-conduct/ "><img src="https://img.shields.io/badge/Contributor%20Covenant-1.4-purple" /></a>
|
||||||
@@ -294,6 +293,10 @@ Currently Vercel Pro Plan is required to be able to Deploy this application with
|
|||||||
|
|
||||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fcalcom%2Fcal.com&env=DATABASE_URL,NEXT_PUBLIC_WEBAPP_URL,NEXTAUTH_URL,NEXTAUTH_SECRET,CRON_API_KEY,CALENDSO_ENCRYPTION_KEY&envDescription=See%20all%20available%20env%20vars&envLink=https%3A%2F%2Fgithub.com%2Fcalcom%2Fcal.com%2Fblob%2Fmain%2F.env.example&project-name=cal&repo-name=cal.com&build-command=cd%20../..%20%26%26%20yarn%20build&root-directory=apps%2Fweb%2F)
|
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fcalcom%2Fcal.com&env=DATABASE_URL,NEXT_PUBLIC_WEBAPP_URL,NEXTAUTH_URL,NEXTAUTH_SECRET,CRON_API_KEY,CALENDSO_ENCRYPTION_KEY&envDescription=See%20all%20available%20env%20vars&envLink=https%3A%2F%2Fgithub.com%2Fcalcom%2Fcal.com%2Fblob%2Fmain%2F.env.example&project-name=cal&repo-name=cal.com&build-command=cd%20../..%20%26%26%20yarn%20build&root-directory=apps%2Fweb%2F)
|
||||||
|
|
||||||
|
### Render
|
||||||
|
|
||||||
|
[](https://render.com/deploy?repo=https://github.com/calcom/docker)
|
||||||
|
|
||||||
<!-- ROADMAP -->
|
<!-- ROADMAP -->
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
@@ -310,7 +313,7 @@ Please see our [contributing guide](/CONTRIBUTING.md).
|
|||||||
|
|
||||||
### Good First Issues
|
### Good First Issues
|
||||||
|
|
||||||
We have a list of [help wanted](https://github.com/orgs/calcom/projects/1/views/25) that contain small features and bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process.
|
We have a list of [help wanted](https://github.com/calcom/cal.com/issues?q=is:issue+is:open+label:%22%F0%9F%99%8B%F0%9F%8F%BB%E2%80%8D%E2%99%82%EF%B8%8Fhelp+wanted%22) that contain small features and bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process.
|
||||||
|
|
||||||
## Integrations
|
## Integrations
|
||||||
|
|
||||||
@@ -354,7 +357,6 @@ following
|
|||||||
5. Use **Application (client) ID** as the **MS_GRAPH_CLIENT_ID** attribute value in .env
|
5. Use **Application (client) ID** as the **MS_GRAPH_CLIENT_ID** attribute value in .env
|
||||||
6. Click **Certificates & secrets** create a new client secret and use the value as the **MS_GRAPH_CLIENT_SECRET** attribute
|
6. Click **Certificates & secrets** create a new client secret and use the value as the **MS_GRAPH_CLIENT_SECRET** attribute
|
||||||
|
|
||||||
|
|
||||||
### Obtaining Zoom Client ID and Secret
|
### Obtaining Zoom Client ID and Secret
|
||||||
|
|
||||||
1. Open [Zoom Marketplace](https://marketplace.zoom.us/) and sign in with your Zoom account.
|
1. Open [Zoom Marketplace](https://marketplace.zoom.us/) and sign in with your Zoom account.
|
||||||
|
|||||||
+1
-1
Submodule apps/api updated: 604d937661...9c23a8e5ab
@@ -14,14 +14,13 @@ Cal.com uses [TailwindCSS](https://tailwindcss.com) as a replacement for traditi
|
|||||||
|
|
||||||
CSS files should be stored in the `styles` directory within the codebase.
|
CSS files should be stored in the `styles` directory within the codebase.
|
||||||
|
|
||||||
Within the `styles` directory, you will find two CSS files, `fonts.css` and `global.css`. We suggest not to add to these files, and instead create new CSS files and import them into the application. This helps reduce conflicts when pulling in changes that we've made to either of the existing CSS files.
|
Within the `styles` directory, you will find a single CSS file, `global.css`. We suggest not to add to this file, and instead create new CSS files and import them into the application. This helps reduce conflicts when pulling in changes that we've made to either of the existing CSS files.
|
||||||
|
|
||||||
## Adding new stylesheets
|
## Adding new stylesheets
|
||||||
Firstly, create the CSS file inside the `styles` directory.
|
Firstly, create the CSS file inside the `styles` directory.
|
||||||
|
|
||||||
Then, open the `pages/_app.tsx` file, and you will see the following two lines:
|
Then, open the `pages/_app.tsx` file, and you will see the following two lines:
|
||||||
```javascript
|
```javascript
|
||||||
import "../styles/fonts.css";
|
|
||||||
import "../styles/globals.css";
|
import "../styles/globals.css";
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
14.17
|
v16
|
||||||
|
|||||||
@@ -639,7 +639,7 @@ const ChooseEmbedTypesDialogContent = () => {
|
|||||||
<div className="flex items-start">
|
<div className="flex items-start">
|
||||||
{embeds.map((embed, index) => (
|
{embeds.map((embed, index) => (
|
||||||
<button
|
<button
|
||||||
className="mr-2 w-1/3 border border-transparent p-3 text-left hover:rounded-md hover:border-gray-200 hover:bg-neutral-100"
|
className="w-1/3 border border-transparent p-3 text-left hover:rounded-md hover:border-gray-200 hover:bg-neutral-100 ltr:mr-2 rtl:ml-2"
|
||||||
key={index}
|
key={index}
|
||||||
data-testid={embed.type}
|
data-testid={embed.type}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -914,7 +914,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"mt-4 flex items-center justify-start",
|
"mt-4 flex items-center justify-start",
|
||||||
embedType === "floating-popup" ? "space-x-2" : "hidden"
|
embedType === "floating-popup" ? "space-x-2 rtl:space-x-reverse" : "hidden"
|
||||||
)}>
|
)}>
|
||||||
<Switch
|
<Switch
|
||||||
defaultChecked={true}
|
defaultChecked={true}
|
||||||
@@ -1010,7 +1010,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent>
|
<CollapsibleContent>
|
||||||
<div className="mt-6 text-sm">
|
<div className="mt-6 text-sm">
|
||||||
<div className="mb-4 flex items-center justify-start space-x-2">
|
<div className="mb-4 flex items-center justify-start space-x-2 rtl:space-x-reverse">
|
||||||
<Switch
|
<Switch
|
||||||
checked={previewState.hideEventTypeDetails}
|
checked={previewState.hideEventTypeDetails}
|
||||||
onCheckedChange={(checked) => {
|
onCheckedChange={(checked) => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useTranslation } from "next-i18next";
|
import { useTranslation } from "next-i18next";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
import { getDirFromLang } from "@calcom/lib/i18n";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
|
|
||||||
export function useViewerI18n() {
|
export function useViewerI18n() {
|
||||||
@@ -21,12 +22,16 @@ export function useViewerI18n() {
|
|||||||
*/
|
*/
|
||||||
const I18nLanguageHandler = (): null => {
|
const I18nLanguageHandler = (): null => {
|
||||||
const { i18n } = useTranslation("common");
|
const { i18n } = useTranslation("common");
|
||||||
const locale = useViewerI18n().data?.locale;
|
const locale = useViewerI18n().data?.locale || "en";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (locale && i18n.language && i18n.language !== locale) {
|
if (locale && i18n.language && i18n.language !== locale) {
|
||||||
if (typeof i18n.changeLanguage === "function") i18n.changeLanguage(locale);
|
if (typeof i18n.changeLanguage === "function") i18n.changeLanguage(locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dir = getDirFromLang(locale);
|
||||||
|
document.documentElement.setAttribute("lang", locale);
|
||||||
|
document.documentElement.setAttribute("dir", dir);
|
||||||
}, [locale, i18n]);
|
}, [locale, i18n]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ import { APP_NAME, COMPANY_NAME, SUPPORT_MAIL_ADDRESS } from "@calcom/lib/consta
|
|||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { App as AppType } from "@calcom/types/App";
|
import { App as AppType } from "@calcom/types/App";
|
||||||
import { Button, Icon, Shell, showToast, SkeletonButton, SkeletonText } from "@calcom/ui";
|
import { Button, Icon, Shell, showToast, SkeletonButton, SkeletonText, HeadSeo } from "@calcom/ui";
|
||||||
|
|
||||||
import HeadSeo from "@components/seo/head-seo";
|
|
||||||
|
|
||||||
const Component = ({
|
const Component = ({
|
||||||
name,
|
name,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
List,
|
List,
|
||||||
ShellSubHeading,
|
ShellSubHeading,
|
||||||
showToast,
|
showToast,
|
||||||
SkeletonLoader,
|
AppSkeletonLoader as SkeletonLoader,
|
||||||
Switch,
|
Switch,
|
||||||
} from "@calcom/ui";
|
} from "@calcom/ui";
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ function IntegrationListItem(props: {
|
|||||||
<ListItemText component="p">{props.description}</ListItemText>
|
<ListItemText component="p">{props.description}</ListItemText>
|
||||||
{/* Alert error that key stopped working. */}
|
{/* Alert error that key stopped working. */}
|
||||||
{props.invalidCredential && (
|
{props.invalidCredential && (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2 rtl:space-x-reverse">
|
||||||
<Icon.FiAlertCircle className="w-8 text-red-500 sm:w-4" />
|
<Icon.FiAlertCircle className="w-8 text-red-500 sm:w-4" />
|
||||||
<ListItemText component="p" className="whitespace-pre-wrap text-red-500">
|
<ListItemText component="p" className="whitespace-pre-wrap text-red-500">
|
||||||
{t("invalid_credential")}
|
{t("invalid_credential")}
|
||||||
|
|||||||
+1
-5
@@ -3,11 +3,7 @@ import { useRouter } from "next/router";
|
|||||||
import React, { ComponentProps } from "react";
|
import React, { ComponentProps } from "react";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { EmptyScreen, Icon } from "@calcom/ui";
|
import { EmptyScreen, Icon, Shell, HorizontalTabs, HorizontalTabItemProps } from "@calcom/ui";
|
||||||
|
|
||||||
import Shell from "../Shell";
|
|
||||||
import type { HorizontalTabItemProps } from "../navigation/tabs/HorizontalTabItem";
|
|
||||||
import HorizontalTabs from "../navigation/tabs/HorizontalTabs";
|
|
||||||
|
|
||||||
const tabs: HorizontalTabItemProps[] = [
|
const tabs: HorizontalTabItemProps[] = [
|
||||||
{
|
{
|
||||||
+3
-5
@@ -3,11 +3,9 @@ import React, { ComponentProps } from "react";
|
|||||||
import AppCategoryNavigation from "@calcom/app-store/_components/AppCategoryNavigation";
|
import AppCategoryNavigation from "@calcom/app-store/_components/AppCategoryNavigation";
|
||||||
import { InstalledAppVariants } from "@calcom/app-store/utils";
|
import { InstalledAppVariants } from "@calcom/app-store/utils";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
|
import { Icon, Shell } from "@calcom/ui";
|
||||||
import { Icon } from "../../../Icon";
|
import type { HorizontalTabItemProps } from "@calcom/ui/v2/core/navigation/tabs/HorizontalTabItem";
|
||||||
import Shell from "../Shell";
|
import type { VerticalTabItemProps } from "@calcom/ui/v2/core/navigation/tabs/VerticalTabItem";
|
||||||
import type { HorizontalTabItemProps } from "../navigation/tabs/HorizontalTabItem";
|
|
||||||
import type { VerticalTabItemProps } from "../navigation/tabs/VerticalTabItem";
|
|
||||||
|
|
||||||
const tabs: (VerticalTabItemProps | HorizontalTabItemProps)[] = [
|
const tabs: (VerticalTabItemProps | HorizontalTabItemProps)[] = [
|
||||||
{
|
{
|
||||||
+3
-3
@@ -2,9 +2,9 @@ import { useSession } from "next-auth/react";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React, { ComponentProps, useEffect } from "react";
|
import React, { ComponentProps, useEffect } from "react";
|
||||||
|
|
||||||
import ErrorBoundary from "../../../ErrorBoundary";
|
import SettingsLayout from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
import Shell from "../Shell";
|
import { ErrorBoundary, Shell } from "@calcom/ui";
|
||||||
import SettingsLayout from "./SettingsLayout";
|
|
||||||
import { UserPermissionRole } from ".prisma/client";
|
import { UserPermissionRole } from ".prisma/client";
|
||||||
|
|
||||||
export default function AdminLayout({
|
export default function AdminLayout({
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
import { getEventLocationType, locationKeyToString } from "@calcom/app-store/locations";
|
import { getEventLocationType, locationKeyToString } from "@calcom/app-store/locations";
|
||||||
import { classNames } from "@calcom/lib";
|
import { classNames } from "@calcom/lib";
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Icon, Tooltip } from "@calcom/ui";
|
import { Icon, Tooltip } from "@calcom/ui";
|
||||||
|
|
||||||
import { Props } from "./pages/AvailabilityPage";
|
import { Props } from "./pages/AvailabilityPage";
|
||||||
|
|
||||||
export function AvailableEventLocations({ locations }: { locations: Props["eventType"]["locations"] }) {
|
export function AvailableEventLocations({ locations }: { locations: Props["eventType"]["locations"] }) {
|
||||||
|
const { t } = useLocale();
|
||||||
|
|
||||||
return locations.length ? (
|
return locations.length ? (
|
||||||
<div className="dark:text-darkgray-600 mr-6 flex w-full flex-col space-y-2 break-words text-sm text-gray-600">
|
<div className="dark:text-darkgray-600 mr-6 flex w-full flex-col space-y-2 break-words text-sm text-gray-600">
|
||||||
{locations.map((location) => {
|
{locations.map((location) => {
|
||||||
@@ -16,19 +19,19 @@ export function AvailableEventLocations({ locations }: { locations: Props["event
|
|||||||
return (
|
return (
|
||||||
<div key={location.type} className="flex flex-row items-center text-sm font-medium">
|
<div key={location.type} className="flex flex-row items-center text-sm font-medium">
|
||||||
{eventLocationType.iconUrl === "/link.svg" ? (
|
{eventLocationType.iconUrl === "/link.svg" ? (
|
||||||
<Icon.FiLink className="dark:text-darkgray-600 mr-[10px] ml-[2px] h-4 w-4 opacity-70 dark:opacity-100 " />
|
<Icon.FiLink className="dark:text-darkgray-600 ml-[2px] h-4 w-4 opacity-70 ltr:mr-[10px] rtl:ml-[10px] dark:opacity-100 " />
|
||||||
) : (
|
) : (
|
||||||
<img
|
<img
|
||||||
src={eventLocationType.iconUrl}
|
src={eventLocationType.iconUrl}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"mr-[10px] ml-[2px] h-4 w-4 opacity-70 dark:opacity-100 ",
|
"ml-[2px] h-4 w-4 opacity-70 ltr:mr-[10px] rtl:ml-[10px] dark:opacity-100 ",
|
||||||
!eventLocationType.iconUrl?.includes("api") ? "dark:invert" : ""
|
!eventLocationType.iconUrl?.includes("api") ? "dark:invert" : ""
|
||||||
)}
|
)}
|
||||||
alt={`${eventLocationType.label} icon`}
|
alt={`${eventLocationType.label} icon`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Tooltip content={locationKeyToString(location)}>
|
<Tooltip content={t(locationKeyToString(location) ?? "")}>
|
||||||
<p className="truncate">{locationKeyToString(location)}</p>
|
<p className="truncate">{t(locationKeyToString(location) ?? "")}</p>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="-mb-5 grid flex-grow grid-cols-1 gap-x-2 overflow-y-auto sm:block md:h-[364px]">
|
<div className="flex-grow overflow-y-auto sm:block md:h-[364px]">
|
||||||
{slots.length > 0 &&
|
{slots.length > 0 &&
|
||||||
slots.map((slot) => {
|
slots.map((slot) => {
|
||||||
type BookingURL = {
|
type BookingURL = {
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ const BookingDescription: FC<Props> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<Icon.FiInfo
|
<Icon.FiInfo
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"mr-[10px] ml-[2px] inline-block h-4 w-4",
|
"ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]",
|
||||||
isBookingPage && "dark:text-darkgray-600 -mt-1 text-gray-500"
|
isBookingPage && "dark:text-darkgray-600 -mt-1 text-gray-500"
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -112,7 +112,7 @@ const BookingDescription: FC<Props> = (props) => {
|
|||||||
isBookingPage && "dark:text-darkgray-600 text-sm font-medium text-gray-600"
|
isBookingPage && "dark:text-darkgray-600 text-sm font-medium text-gray-600"
|
||||||
)}>
|
)}>
|
||||||
<div>
|
<div>
|
||||||
<Icon.FiCheckSquare className="mr-[10px] ml-[2px] inline-block h-4 w-4 " />
|
<Icon.FiCheckSquare className="ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px] " />
|
||||||
</div>
|
</div>
|
||||||
{requiresConfirmationText}
|
{requiresConfirmationText}
|
||||||
</div>
|
</div>
|
||||||
@@ -127,7 +127,7 @@ const BookingDescription: FC<Props> = (props) => {
|
|||||||
)}>
|
)}>
|
||||||
<Icon.FiClock
|
<Icon.FiClock
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"min-h-4 min-w-4 mr-[10px] ml-[2px] inline-block",
|
"min-h-4 min-w-4 ml-[2px] inline-block ltr:mr-[10px] rtl:ml-[10px]",
|
||||||
isBookingPage && "mt-[2px]"
|
isBookingPage && "mt-[2px]"
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useState } from "react";
|
|||||||
|
|
||||||
import { EventLocationType, getEventLocationType } from "@calcom/app-store/locations";
|
import { EventLocationType, getEventLocationType } from "@calcom/app-store/locations";
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
|
import ViewRecordingsDialog from "@calcom/features/ee/video/ViewRecordingsDialog";
|
||||||
import classNames from "@calcom/lib/classNames";
|
import classNames from "@calcom/lib/classNames";
|
||||||
import { formatTime } from "@calcom/lib/date-fns";
|
import { formatTime } from "@calcom/lib/date-fns";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
@@ -22,8 +23,9 @@ import {
|
|||||||
showToast,
|
showToast,
|
||||||
TextArea,
|
TextArea,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
ActionType,
|
||||||
|
TableActions,
|
||||||
} from "@calcom/ui";
|
} from "@calcom/ui";
|
||||||
import { ActionType, TableActions } from "@calcom/ui";
|
|
||||||
|
|
||||||
import useMeQuery from "@lib/hooks/useMeQuery";
|
import useMeQuery from "@lib/hooks/useMeQuery";
|
||||||
|
|
||||||
@@ -48,6 +50,7 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [rejectionReason, setRejectionReason] = useState<string>("");
|
const [rejectionReason, setRejectionReason] = useState<string>("");
|
||||||
const [rejectionDialogIsOpen, setRejectionDialogIsOpen] = useState(false);
|
const [rejectionDialogIsOpen, setRejectionDialogIsOpen] = useState(false);
|
||||||
|
const [viewRecordingsDialogIsOpen, setViewRecordingsDialogIsOpen] = useState<boolean>(false);
|
||||||
const mutation = trpc.viewer.bookings.confirm.useMutation({
|
const mutation = trpc.viewer.bookings.confirm.useMutation({
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if (data.status === BookingStatus.REJECTED) {
|
if (data.status === BookingStatus.REJECTED) {
|
||||||
@@ -112,6 +115,17 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const showRecordingActions: ActionType[] = [
|
||||||
|
{
|
||||||
|
id: "view_recordings",
|
||||||
|
label: t("view_recordings"),
|
||||||
|
onClick: () => {
|
||||||
|
setViewRecordingsDialogIsOpen(true);
|
||||||
|
},
|
||||||
|
disabled: mutation.isLoading,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
let bookedActions: ActionType[] = [
|
let bookedActions: ActionType[] = [
|
||||||
{
|
{
|
||||||
id: "cancel",
|
id: "cancel",
|
||||||
@@ -206,7 +220,7 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const showRecordingsButtons = booking.location === "integrations:daily" && isPast && isConfirmed;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RescheduleDialog
|
<RescheduleDialog
|
||||||
@@ -220,7 +234,14 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
isOpenDialog={isOpenSetLocationDialog}
|
isOpenDialog={isOpenSetLocationDialog}
|
||||||
setShowLocationModal={setIsOpenLocationDialog}
|
setShowLocationModal={setIsOpenLocationDialog}
|
||||||
/>
|
/>
|
||||||
|
{showRecordingsButtons && (
|
||||||
|
<ViewRecordingsDialog
|
||||||
|
booking={booking}
|
||||||
|
isOpenDialog={viewRecordingsDialogIsOpen}
|
||||||
|
setIsOpenDialog={setViewRecordingsDialogIsOpen}
|
||||||
|
timeFormat={user?.timeFormat ?? null}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{/* NOTE: Should refactor this dialog component as is being rendered multiple times */}
|
{/* NOTE: Should refactor this dialog component as is being rendered multiple times */}
|
||||||
<Dialog open={rejectionDialogIsOpen} onOpenChange={setRejectionDialogIsOpen}>
|
<Dialog open={rejectionDialogIsOpen} onOpenChange={setRejectionDialogIsOpen}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
@@ -342,7 +363,7 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
<span> </span>
|
<span> </span>
|
||||||
|
|
||||||
{!!booking?.eventType?.price && !booking.paid && (
|
{!!booking?.eventType?.price && !booking.paid && (
|
||||||
<Badge className="hidden ltr:ml-2 rtl:mr-2 sm:inline-flex" variant="orange">
|
<Badge className="hidden ltr:ml-2 ltr:mr-2 rtl:ml-2 sm:inline-flex" variant="orange">
|
||||||
{t("pending_payment")}
|
{t("pending_payment")}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
@@ -377,6 +398,7 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{isPast && isPending && !isConfirmed ? <TableActions actions={bookedActions} /> : null}
|
{isPast && isPending && !isConfirmed ? <TableActions actions={bookedActions} /> : null}
|
||||||
|
{showRecordingsButtons && <TableActions actions={showRecordingActions} />}
|
||||||
{isCancelled && booking.rescheduled && (
|
{isCancelled && booking.rescheduled && (
|
||||||
<div className="hidden h-full items-center md:flex">
|
<div className="hidden h-full items-center md:flex">
|
||||||
<RequestSentMessage />
|
<RequestSentMessage />
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { getRecurringFreq } from "@calcom/lib/recurringStrings";
|
|||||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||||
import { detectBrowserTimeFormat, setIs24hClockInLocalStorage, TimeFormat } from "@calcom/lib/timeFormat";
|
import { detectBrowserTimeFormat, setIs24hClockInLocalStorage, TimeFormat } from "@calcom/lib/timeFormat";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { Icon } from "@calcom/ui";
|
import { Icon, HeadSeo } from "@calcom/ui";
|
||||||
|
|
||||||
import { timeZone as localStorageTimeZone } from "@lib/clock";
|
import { timeZone as localStorageTimeZone } from "@lib/clock";
|
||||||
import useRouterQuery from "@lib/hooks/useRouterQuery";
|
import useRouterQuery from "@lib/hooks/useRouterQuery";
|
||||||
@@ -37,7 +37,6 @@ import Gates, { Gate, GateState } from "@components/Gates";
|
|||||||
import AvailableTimes from "@components/booking/AvailableTimes";
|
import AvailableTimes from "@components/booking/AvailableTimes";
|
||||||
import BookingDescription from "@components/booking/BookingDescription";
|
import BookingDescription from "@components/booking/BookingDescription";
|
||||||
import TimeOptions from "@components/booking/TimeOptions";
|
import TimeOptions from "@components/booking/TimeOptions";
|
||||||
import { HeadSeo } from "@components/seo/head-seo";
|
|
||||||
import PoweredByCal from "@components/ui/PoweredByCal";
|
import PoweredByCal from "@components/ui/PoweredByCal";
|
||||||
|
|
||||||
import type { AvailabilityPageProps } from "../../../pages/[user]/[type]";
|
import type { AvailabilityPageProps } from "../../../pages/[user]/[type]";
|
||||||
@@ -221,7 +220,7 @@ function TimezoneDropdown({
|
|||||||
<Popover.Root open={isTimeOptionsOpen} onOpenChange={setIsTimeOptionsOpen}>
|
<Popover.Root open={isTimeOptionsOpen} onOpenChange={setIsTimeOptionsOpen}>
|
||||||
<Popover.Trigger className="min-w-32 dark:text-darkgray-600 radix-state-open:bg-gray-200 dark:radix-state-open:bg-darkgray-200 group relative mb-2 -ml-2 !mt-2 inline-block self-start rounded-md px-2 py-2 text-left text-gray-600">
|
<Popover.Trigger className="min-w-32 dark:text-darkgray-600 radix-state-open:bg-gray-200 dark:radix-state-open:bg-darkgray-200 group relative mb-2 -ml-2 !mt-2 inline-block self-start rounded-md px-2 py-2 text-left text-gray-600">
|
||||||
<p className="flex items-center text-sm font-medium">
|
<p className="flex items-center text-sm font-medium">
|
||||||
<Icon.FiGlobe className="min-h-4 min-w-4 mr-[10px] ml-[2px] -mt-[2px] inline-block" />
|
<Icon.FiGlobe className="min-h-4 min-w-4 ml-[2px] -mt-[2px] inline-block ltr:mr-[10px] rtl:ml-[10px]" />
|
||||||
{timeZone}
|
{timeZone}
|
||||||
{isTimeOptionsOpen ? (
|
{isTimeOptionsOpen ? (
|
||||||
<Icon.FiChevronUp className="min-h-4 min-w-4 ml-1 inline-block" />
|
<Icon.FiChevronUp className="min-h-4 min-w-4 ml-1 inline-block" />
|
||||||
@@ -368,7 +367,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
|
|||||||
<BookingDescription profile={profile} eventType={eventType} rescheduleUid={rescheduleUid}>
|
<BookingDescription profile={profile} eventType={eventType} rescheduleUid={rescheduleUid}>
|
||||||
{!rescheduleUid && eventType.recurringEvent && (
|
{!rescheduleUid && eventType.recurringEvent && (
|
||||||
<div className="flex items-start text-sm font-medium">
|
<div className="flex items-start text-sm font-medium">
|
||||||
<Icon.FiRefreshCcw className="float-left mr-[10px] mt-[7px] ml-[2px] inline-block h-4 w-4 " />
|
<Icon.FiRefreshCcw className="float-left mt-[7px] ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px] " />
|
||||||
<div>
|
<div>
|
||||||
<p className="mb-1 -ml-2 inline px-2 py-1">
|
<p className="mb-1 -ml-2 inline px-2 py-1">
|
||||||
{getRecurringFreq({ t, recurringEvent: eventType.recurringEvent })}
|
{getRecurringFreq({ t, recurringEvent: eventType.recurringEvent })}
|
||||||
@@ -393,7 +392,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
|
|||||||
)}
|
)}
|
||||||
{stripeAppData.price > 0 && (
|
{stripeAppData.price > 0 && (
|
||||||
<p className="-ml-2 px-2 text-sm font-medium">
|
<p className="-ml-2 px-2 text-sm font-medium">
|
||||||
<Icon.FiCreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
|
<Icon.FiCreditCard className="ml-[2px] -mt-1 inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
|
||||||
<IntlProvider locale="en">
|
<IntlProvider locale="en">
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
value={stripeAppData.price / 100.0}
|
value={stripeAppData.price / 100.0}
|
||||||
@@ -414,7 +413,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
|
|||||||
{t("former_time")}
|
{t("former_time")}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-gray-500 line-through dark:text-darkgray-600">
|
<p className="text-gray-500 line-through dark:text-darkgray-600">
|
||||||
<CalendarIcon className="mr-[10px] -mt-1 inline-block h-4 w-4 text-gray-500" />
|
<CalendarIcon className="ltr:mr-[10px] rtl:ml-[10px] -mt-1 inline-block h-4 w-4 text-gray-500" />
|
||||||
{typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)}
|
{typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -501,7 +501,7 @@ const BookingPage = ({
|
|||||||
<BookingDescription isBookingPage profile={profile} eventType={eventType}>
|
<BookingDescription isBookingPage profile={profile} eventType={eventType}>
|
||||||
{stripeAppData.price > 0 && (
|
{stripeAppData.price > 0 && (
|
||||||
<p className="text-bookinglight -ml-2 px-2 text-sm ">
|
<p className="text-bookinglight -ml-2 px-2 text-sm ">
|
||||||
<Icon.FiCreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
|
<Icon.FiCreditCard className="ml-[2px] -mt-1 inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
|
||||||
<IntlProvider locale="en">
|
<IntlProvider locale="en">
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
value={stripeAppData.price / 100.0}
|
value={stripeAppData.price / 100.0}
|
||||||
@@ -513,7 +513,7 @@ const BookingPage = ({
|
|||||||
)}
|
)}
|
||||||
{!rescheduleUid && eventType.recurringEvent?.freq && recurringEventCount && (
|
{!rescheduleUid && eventType.recurringEvent?.freq && recurringEventCount && (
|
||||||
<div className="items-start text-sm font-medium text-gray-600 dark:text-white">
|
<div className="items-start text-sm font-medium text-gray-600 dark:text-white">
|
||||||
<Icon.FiRefreshCw className="mr-[10px] ml-[2px] inline-block h-4 w-4" />
|
<Icon.FiRefreshCw className="ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
|
||||||
<p className="-ml-2 inline-block items-center px-2">
|
<p className="-ml-2 inline-block items-center px-2">
|
||||||
{getEveryFreqFor({
|
{getEveryFreqFor({
|
||||||
t,
|
t,
|
||||||
@@ -524,7 +524,7 @@ const BookingPage = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="text-bookinghighlight flex items-start text-sm">
|
<div className="text-bookinghighlight flex items-start text-sm">
|
||||||
<Icon.FiCalendar className="mr-[10px] ml-[2px] mt-[2px] inline-block h-4 w-4" />
|
<Icon.FiCalendar className="ml-[2px] mt-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
|
||||||
<div className="text-sm font-medium">
|
<div className="text-sm font-medium">
|
||||||
{(rescheduleUid || !eventType.recurringEvent?.freq) && `${parseDate(date, i18n)}`}
|
{(rescheduleUid || !eventType.recurringEvent?.freq) && `${parseDate(date, i18n)}`}
|
||||||
{!rescheduleUid &&
|
{!rescheduleUid &&
|
||||||
@@ -552,7 +552,7 @@ const BookingPage = ({
|
|||||||
{t("former_time")}
|
{t("former_time")}
|
||||||
</p>
|
</p>
|
||||||
<p className="line-through ">
|
<p className="line-through ">
|
||||||
<Icon.FiCalendar className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
|
<Icon.FiCalendar className="ml-[2px] -mt-1 inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
|
||||||
{typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)}
|
{typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -560,7 +560,7 @@ const BookingPage = ({
|
|||||||
{!!eventType.seatsPerTimeSlot && (
|
{!!eventType.seatsPerTimeSlot && (
|
||||||
<div className="text-bookinghighlight flex items-start text-sm">
|
<div className="text-bookinghighlight flex items-start text-sm">
|
||||||
<Icon.FiUser
|
<Icon.FiUser
|
||||||
className={`mr-[10px] ml-[2px] mt-[2px] inline-block h-4 w-4 ${
|
className={`ml-[2px] mt-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px] ${
|
||||||
booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.5
|
booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.5
|
||||||
? "text-rose-600"
|
? "text-rose-600"
|
||||||
: booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.33
|
: booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.33
|
||||||
@@ -623,7 +623,7 @@ const BookingPage = ({
|
|||||||
/>
|
/>
|
||||||
{bookingForm.formState.errors.email && (
|
{bookingForm.formState.errors.email && (
|
||||||
<div className="mt-2 flex items-center text-sm text-red-700 ">
|
<div className="mt-2 flex items-center text-sm text-red-700 ">
|
||||||
<Icon.FiInfo className="mr-2 h-3 w-3" />
|
<Icon.FiInfo className="h-3 w-3 ltr:mr-2 rtl:ml-2" />
|
||||||
<p>{t("email_validation_error")}</p>
|
<p>{t("email_validation_error")}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -664,8 +664,8 @@ const BookingPage = ({
|
|||||||
value={location.type}
|
value={location.type}
|
||||||
defaultChecked={i === 0}
|
defaultChecked={i === 0}
|
||||||
/>
|
/>
|
||||||
<span className="text-sm ltr:ml-2 rtl:mr-2 dark:text-white">
|
<span className="text-sm ltr:ml-2 ltr:mr-2 rtl:ml-2 dark:text-white">
|
||||||
{locationKeyToString(location)}
|
{t(locationKeyToString(location) ?? "")}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
@@ -689,7 +689,7 @@ const BookingPage = ({
|
|||||||
{selectedLocationType === LocationType.Phone
|
{selectedLocationType === LocationType.Phone
|
||||||
? t("phone_number")
|
? t("phone_number")
|
||||||
: selectedLocationType === LocationType.AttendeeInPerson
|
: selectedLocationType === LocationType.AttendeeInPerson
|
||||||
? t("Address")
|
? t("address")
|
||||||
: ""}
|
: ""}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1">
|
<div className="mt-1">
|
||||||
@@ -716,7 +716,7 @@ const BookingPage = ({
|
|||||||
</div>
|
</div>
|
||||||
{bookingForm.formState.errors.phone && (
|
{bookingForm.formState.errors.phone && (
|
||||||
<div className="mt-2 flex items-center text-sm text-red-700 ">
|
<div className="mt-2 flex items-center text-sm text-red-700 ">
|
||||||
<Icon.FiInfo className="mr-2 h-3 w-3" />
|
<Icon.FiInfo className="h-3 w-3 ltr:mr-2 rtl:ml-2" />
|
||||||
<p>{t("invalid_number")}</p>
|
<p>{t("invalid_number")}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -800,6 +800,7 @@ const BookingPage = ({
|
|||||||
{input.options && input.type === EventTypeCustomInputType.RADIO && (
|
{input.options && input.type === EventTypeCustomInputType.RADIO && (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<Group
|
<Group
|
||||||
|
name={`customInputs.${input.id}`}
|
||||||
required={input.required}
|
required={input.required}
|
||||||
onValueChange={(e) => {
|
onValueChange={(e) => {
|
||||||
bookingForm.setValue(`customInputs.${input.id}`, e);
|
bookingForm.setValue(`customInputs.${input.id}`, e);
|
||||||
@@ -808,9 +809,9 @@ const BookingPage = ({
|
|||||||
{input.options.map((option, i) => (
|
{input.options.map((option, i) => (
|
||||||
<RadioField
|
<RadioField
|
||||||
label={option.label}
|
label={option.label}
|
||||||
key={`option.${i}.radio`}
|
key={`option.${input.id}.${i}.radio`}
|
||||||
value={option.label}
|
value={option.label}
|
||||||
id={`option.${i}.radio`}
|
id={`option.${input.id}.${i}.radio`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
@@ -833,7 +834,7 @@ const BookingPage = ({
|
|||||||
/>
|
/>
|
||||||
{bookingForm.formState.errors?.customInputs?.[input.id] && (
|
{bookingForm.formState.errors?.customInputs?.[input.id] && (
|
||||||
<div className="mt-2 flex items-center text-sm text-red-700 ">
|
<div className="mt-2 flex items-center text-sm text-red-700 ">
|
||||||
<Icon.FiInfo className="mr-2 h-3 w-3" />
|
<Icon.FiInfo className="h-3 w-3 ltr:mr-2 rtl:ml-2" />
|
||||||
<p>{t("invalid_number")}</p>
|
<p>{t("invalid_number")}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -913,7 +914,7 @@ const BookingPage = ({
|
|||||||
</div>
|
</div>
|
||||||
{bookingForm.formState.errors.smsReminderNumber && (
|
{bookingForm.formState.errors.smsReminderNumber && (
|
||||||
<div className="mt-2 flex items-center text-sm text-red-700 ">
|
<div className="mt-2 flex items-center text-sm text-red-700 ">
|
||||||
<Icon.FiInfo className="mr-2 h-3 w-3" />
|
<Icon.FiInfo className="h-3 w-3 ltr:mr-2 rtl:ml-2" />
|
||||||
<p>{t("invalid_number")}</p>
|
<p>{t("invalid_number")}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
|
|||||||
/>
|
/>
|
||||||
{selectedLocation && LocationOptions}
|
{selectedLocation && LocationOptions}
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<div className="mt-4 flex justify-end space-x-2">
|
<div className="mt-4 flex justify-end space-x-2 rtl:space-x-reverse">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowLocationModal(false);
|
setShowLocationModal(false);
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export const AvailabilityTab = ({ isTeamEvent }: { isTeamEvent: boolean }) => {
|
|||||||
<span className="w-16 sm:w-28 sm:text-left">
|
<span className="w-16 sm:w-28 sm:text-left">
|
||||||
{format(dayRange.startTime, timeFormat === 12)}
|
{format(dayRange.startTime, timeFormat === 12)}
|
||||||
</span>
|
</span>
|
||||||
<span className="ml-4">-</span>
|
<span className="ltr:ml-4 rtl:mr-4">-</span>
|
||||||
<div className="ml-6">{format(dayRange.endTime, timeFormat === 12)}</div>
|
<div className="ml-6">{format(dayRange.endTime, timeFormat === 12)}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -164,7 +164,7 @@ export const AvailabilityTab = ({ isTeamEvent }: { isTeamEvent: boolean }) => {
|
|||||||
<hr />
|
<hr />
|
||||||
<div className="flex flex-col justify-center gap-2 sm:flex-row sm:justify-between">
|
<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">
|
<span className="flex items-center justify-center text-sm text-gray-600 sm:justify-start">
|
||||||
<Icon.FiGlobe className="mr-2" />
|
<Icon.FiGlobe className="ltr:mr-2 rtl:ml-2" />
|
||||||
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
|
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ const CustomInputTypeForm: FC<Props> = (props) => {
|
|||||||
value={selectedCustomInput?.id || -1}
|
value={selectedCustomInput?.id || -1}
|
||||||
{...register("id", { valueAsNumber: true })}
|
{...register("id", { valueAsNumber: true })}
|
||||||
/>
|
/>
|
||||||
<div className="mt-5 flex justify-end space-x-2 sm:mt-4">
|
<div className="mt-5 flex justify-end space-x-2 rtl:space-x-reverse sm:mt-4">
|
||||||
<Button onClick={onCancel} type="button" color="secondary" className="ltr:mr-2">
|
<Button onClick={onCancel} type="button" color="secondary" className="ltr:mr-2 rtl:ml-2">
|
||||||
{t("cancel")}
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import type { CustomInputParsed, EventTypeSetupInfered, FormValues } from "pages/event-types/[type]";
|
import type { CustomInputParsed, EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Controller, useFormContext } from "react-hook-form";
|
import { Controller, useFormContext } from "react-hook-form";
|
||||||
import short from "short-uuid";
|
import short from "short-uuid";
|
||||||
@@ -7,10 +7,11 @@ import { v5 as uuidv5 } from "uuid";
|
|||||||
|
|
||||||
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
|
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
|
||||||
import CustomInputItem from "@calcom/features/eventtypes/components/CustomInputItem";
|
import CustomInputItem from "@calcom/features/eventtypes/components/CustomInputItem";
|
||||||
import { APP_NAME, CAL_URL } from "@calcom/lib/constants";
|
import { APP_NAME, CAL_URL, IS_SELF_HOSTED } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import {
|
import {
|
||||||
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -36,7 +37,19 @@ const generateHashedLink = (id: number) => {
|
|||||||
return uid;
|
return uid;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered, "eventType" | "team">) => {
|
const getRandomId = (length = 8) => {
|
||||||
|
return (
|
||||||
|
-1 *
|
||||||
|
parseInt(
|
||||||
|
Math.ceil(Math.random() * Date.now())
|
||||||
|
.toPrecision(length)
|
||||||
|
.toString()
|
||||||
|
.replace(".", "")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps, "eventType" | "team">) => {
|
||||||
const connectedCalendarsQuery = trpc.viewer.connectedCalendars.useQuery();
|
const connectedCalendarsQuery = trpc.viewer.connectedCalendars.useQuery();
|
||||||
const formMethods = useFormContext<FormValues>();
|
const formMethods = useFormContext<FormValues>();
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
@@ -51,7 +64,6 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
|||||||
const [selectedCustomInputModalOpen, setSelectedCustomInputModalOpen] = useState(false);
|
const [selectedCustomInputModalOpen, setSelectedCustomInputModalOpen] = useState(false);
|
||||||
const [requiresConfirmation, setRequiresConfirmation] = useState(eventType.requiresConfirmation);
|
const [requiresConfirmation, setRequiresConfirmation] = useState(eventType.requiresConfirmation);
|
||||||
const placeholderHashedLink = `${CAL_URL}/d/${hashedUrl}/${eventType.slug}`;
|
const placeholderHashedLink = `${CAL_URL}/d/${hashedUrl}/${eventType.slug}`;
|
||||||
|
|
||||||
const seatsEnabled = formMethods.getValues("seatsPerTimeSlotEnabled");
|
const seatsEnabled = formMethods.getValues("seatsPerTimeSlotEnabled");
|
||||||
|
|
||||||
const removeCustom = (index: number) => {
|
const removeCustom = (index: number) => {
|
||||||
@@ -64,6 +76,12 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
|||||||
!hashedUrl && setHashedUrl(generateHashedLink(eventType.users[0]?.id ?? team?.id));
|
!hashedUrl && setHashedUrl(generateHashedLink(eventType.users[0]?.id ?? team?.id));
|
||||||
}, [eventType.users, hashedUrl, team?.id]);
|
}, [eventType.users, hashedUrl, team?.id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (eventType.customInputs) {
|
||||||
|
setCustomInputs(eventType.customInputs.sort((a, b) => a.id - b.id));
|
||||||
|
}
|
||||||
|
}, [eventType.customInputs]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col space-y-8">
|
<div className="flex flex-col space-y-8">
|
||||||
{/**
|
{/**
|
||||||
@@ -239,13 +257,30 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
|||||||
defaultValue={eventType.successRedirectUrl || ""}
|
defaultValue={eventType.successRedirectUrl || ""}
|
||||||
{...formMethods.register("successRedirectUrl")}
|
{...formMethods.register("successRedirectUrl")}
|
||||||
/>
|
/>
|
||||||
|
<div className="mt-2 flex">
|
||||||
|
<Checkbox
|
||||||
|
description={t("disable_success_page")}
|
||||||
|
// Disable if it's not Self Hosted or if the redirect url is not set
|
||||||
|
disabled={!IS_SELF_HOSTED || !formMethods.watch("successRedirectUrl")}
|
||||||
|
{...formMethods.register("metadata.disableSuccessPage")}
|
||||||
|
/>
|
||||||
|
{/*TODO: Extract it out into a component when used more than once*/}
|
||||||
|
{!IS_SELF_HOSTED && (
|
||||||
|
<Link href="https://cal.com/pricing">
|
||||||
|
<a target="_blank">
|
||||||
|
<Badge variant="orange" className="ml-2">
|
||||||
|
Platform Only
|
||||||
|
</Badge>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SettingsToggle>
|
</SettingsToggle>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<SettingsToggle
|
<SettingsToggle
|
||||||
data-testid="hashedLinkCheck"
|
data-testid="hashedLinkCheck"
|
||||||
title={t("private_link")}
|
title={t("private_link")}
|
||||||
@@ -374,7 +409,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
|||||||
<Controller
|
<Controller
|
||||||
name="customInputs"
|
name="customInputs"
|
||||||
control={formMethods.control}
|
control={formMethods.control}
|
||||||
defaultValue={eventType.customInputs.sort((a, b) => a.id - b.id) || []}
|
defaultValue={customInputs}
|
||||||
render={() => (
|
render={() => (
|
||||||
<Dialog open={selectedCustomInputModalOpen} onOpenChange={setSelectedCustomInputModalOpen}>
|
<Dialog open={selectedCustomInputModalOpen} onOpenChange={setSelectedCustomInputModalOpen}>
|
||||||
<DialogContent
|
<DialogContent
|
||||||
@@ -386,13 +421,14 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
|||||||
selectedCustomInput={selectedCustomInput}
|
selectedCustomInput={selectedCustomInput}
|
||||||
onSubmit={(values) => {
|
onSubmit={(values) => {
|
||||||
const customInput: CustomInputParsed = {
|
const customInput: CustomInputParsed = {
|
||||||
id: -1,
|
id: getRandomId(),
|
||||||
eventTypeId: -1,
|
eventTypeId: -1,
|
||||||
label: values.label,
|
label: values.label,
|
||||||
placeholder: values.placeholder,
|
placeholder: values.placeholder,
|
||||||
required: values.required,
|
required: values.required,
|
||||||
type: values.type,
|
type: values.type,
|
||||||
options: values.options,
|
options: values.options,
|
||||||
|
hasToBeCreated: true,
|
||||||
};
|
};
|
||||||
if (selectedCustomInput) {
|
if (selectedCustomInput) {
|
||||||
selectedCustomInput.label = customInput.label;
|
selectedCustomInput.label = customInput.label;
|
||||||
@@ -400,6 +436,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
|||||||
selectedCustomInput.required = customInput.required;
|
selectedCustomInput.required = customInput.required;
|
||||||
selectedCustomInput.type = customInput.type;
|
selectedCustomInput.type = customInput.type;
|
||||||
selectedCustomInput.options = customInput.options || undefined;
|
selectedCustomInput.options = customInput.options || undefined;
|
||||||
|
selectedCustomInput.hasToBeCreated = false;
|
||||||
// Update by id
|
// Update by id
|
||||||
const inputIndex = customInputs.findIndex((input) => input.id === values.id);
|
const inputIndex = customInputs.findIndex((input) => input.id === values.id);
|
||||||
customInputs[inputIndex] = selectedCustomInput;
|
customInputs[inputIndex] = selectedCustomInput;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EventTypeSetupInfered, FormValues } from "pages/event-types/[type]";
|
import { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
import EventTypeAppContext, { GetAppData, SetAppData } from "@calcom/app-store/EventTypeAppContext";
|
import EventTypeAppContext, { GetAppData, SetAppData } from "@calcom/app-store/EventTypeAppContext";
|
||||||
@@ -9,7 +9,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||||||
import { RouterOutputs, trpc } from "@calcom/trpc/react";
|
import { RouterOutputs, trpc } from "@calcom/trpc/react";
|
||||||
import { Button, EmptyScreen, ErrorBoundary, Icon } from "@calcom/ui";
|
import { Button, EmptyScreen, ErrorBoundary, Icon } from "@calcom/ui";
|
||||||
|
|
||||||
type EventType = Pick<EventTypeSetupInfered, "eventType">["eventType"] &
|
type EventType = Pick<EventTypeSetupProps, "eventType">["eventType"] &
|
||||||
EventTypeAppCardComponentProps["eventType"];
|
EventTypeAppCardComponentProps["eventType"];
|
||||||
|
|
||||||
function AppCardWrapper({
|
function AppCardWrapper({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||||
import * as RadioGroup from "@radix-ui/react-radio-group";
|
import * as RadioGroup from "@radix-ui/react-radio-group";
|
||||||
import { EventTypeSetupInfered, FormValues } from "pages/event-types/[type]";
|
import { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||||
import { useMemo, useRef, useState } from "react";
|
import { useMemo, useRef, useState } from "react";
|
||||||
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ import { PeriodType } from "@calcom/prisma/client";
|
|||||||
import type { BookingLimit } from "@calcom/types/Calendar";
|
import type { BookingLimit } from "@calcom/types/Calendar";
|
||||||
import { Button, DateRangePicker, Icon, Input, InputField, Label, Select, SettingsToggle } from "@calcom/ui";
|
import { Button, DateRangePicker, Icon, Input, InputField, Label, Select, SettingsToggle } from "@calcom/ui";
|
||||||
|
|
||||||
export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupInfered, "eventType">) => {
|
export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventType">) => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const formMethods = useFormContext<FormValues>();
|
const formMethods = useFormContext<FormValues>();
|
||||||
const minimumBookingNoticeType = useRef<DurationType>(findDurationType(eventType.minimumBookingNotice));
|
const minimumBookingNoticeType = useRef<DurationType>(findDurationType(eventType.minimumBookingNotice));
|
||||||
@@ -197,7 +197,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupInfered, "event
|
|||||||
label={t("minimum_booking_notice")}
|
label={t("minimum_booking_notice")}
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="120"
|
placeholder="120"
|
||||||
className="mr-2 mb-0 h-[38px] rounded-[4px]"
|
className="mb-0 h-[38px] rounded-[4px] ltr:mr-2 rtl:ml-2"
|
||||||
{...formMethods.register("minimumBookingNoticeInDurationType", {
|
{...formMethods.register("minimumBookingNoticeInDurationType", {
|
||||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) =>
|
onChange: (event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
onMinimumNoticeChange(event.target.value),
|
onMinimumNoticeChange(event.target.value),
|
||||||
@@ -327,7 +327,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupInfered, "event
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{period.type === "RANGE" && (
|
{period.type === "RANGE" && (
|
||||||
<div className="inline-flex space-x-2 ltr:ml-2 rtl:mr-2 rtl:space-x-reverse">
|
<div className="inline-flex space-x-2 ltr:ml-2 ltr:mr-2 rtl:ml-2 rtl:space-x-reverse">
|
||||||
<Controller
|
<Controller
|
||||||
name="periodDates"
|
name="periodDates"
|
||||||
control={formMethods.control}
|
control={formMethods.control}
|
||||||
@@ -347,7 +347,9 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupInfered, "event
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{period.suffix ? <span className="ltr:ml-2 rtl:mr-2"> {period.suffix}</span> : null}
|
{period.suffix ? (
|
||||||
|
<span className="ltr:ml-2 ltr:mr-2 rtl:ml-2"> {period.suffix}</span>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -410,7 +412,9 @@ const BookingLimits = () => {
|
|||||||
.map(([key, bookingAmount]) => {
|
.map(([key, bookingAmount]) => {
|
||||||
const bookingLimitKey = key as BookingLimitsKey;
|
const bookingLimitKey = key as BookingLimitsKey;
|
||||||
return (
|
return (
|
||||||
<div className="mb-2 flex items-center space-x-2 text-sm" key={bookingLimitKey}>
|
<div
|
||||||
|
className="mb-2 flex items-center space-x-2 text-sm rtl:space-x-reverse"
|
||||||
|
key={bookingLimitKey}>
|
||||||
<Input
|
<Input
|
||||||
id={`${bookingLimitKey}-limit`}
|
id={`${bookingLimitKey}-limit`}
|
||||||
type="number"
|
type="number"
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { EventTypeSetupInfered } from "pages/event-types/[type]";
|
import { EventTypeSetupProps } from "pages/event-types/[type]";
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
import getStripeAppData from "@calcom/lib/getStripeAppData";
|
import getStripeAppData from "@calcom/lib/getStripeAppData";
|
||||||
|
|
||||||
import RecurringEventController from "./RecurringEventController";
|
import RecurringEventController from "./RecurringEventController";
|
||||||
|
|
||||||
export const EventRecurringTab = ({ eventType }: Pick<EventTypeSetupInfered, "eventType">) => {
|
export const EventRecurringTab = ({ eventType }: Pick<EventTypeSetupProps, "eventType">) => {
|
||||||
const stripeAppData = getStripeAppData(eventType);
|
const stripeAppData = getStripeAppData(eventType);
|
||||||
|
|
||||||
const requirePayment = stripeAppData.price > 0;
|
const requirePayment = stripeAppData.price > 0;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { isValidPhoneNumber } from "libphonenumber-js";
|
import { isValidPhoneNumber } from "libphonenumber-js";
|
||||||
import { EventTypeSetupInfered, FormValues } from "pages/event-types/[type]";
|
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, useForm, useFormContext } from "react-hook-form";
|
import { Controller, useForm, useFormContext } from "react-hook-form";
|
||||||
import { MultiValue } from "react-select";
|
import { MultiValue } from "react-select";
|
||||||
@@ -23,7 +23,7 @@ type OptionTypeBase = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const EventSetupTab = (
|
export const EventSetupTab = (
|
||||||
props: Pick<EventTypeSetupInfered, "eventType" | "locationOptions" | "team" | "teamMembers">
|
props: Pick<EventTypeSetupProps, "eventType" | "locationOptions" | "team" | "teamMembers">
|
||||||
) => {
|
) => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const formMethods = useFormContext<FormValues>();
|
const formMethods = useFormContext<FormValues>();
|
||||||
@@ -169,7 +169,7 @@ export const EventSetupTab = (
|
|||||||
alt={`${eventLocationType.label} logo`}
|
alt={`${eventLocationType.label} logo`}
|
||||||
/>
|
/>
|
||||||
<span className="truncate text-sm ltr:ml-1 rtl:mr-1">
|
<span className="truncate text-sm ltr:ml-1 rtl:mr-1">
|
||||||
{location[eventLocationType.defaultValueVariable] || eventLocationType.label}
|
{t(location[eventLocationType.defaultValueVariable] || eventLocationType.label)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
@@ -342,7 +342,7 @@ export const EventSetupTab = (
|
|||||||
saveLocation={saveLocation}
|
saveLocation={saveLocation}
|
||||||
defaultValues={formMethods.getValues("locations")}
|
defaultValues={formMethods.getValues("locations")}
|
||||||
selection={
|
selection={
|
||||||
selectedLocation ? { value: selectedLocation.value, label: selectedLocation.label } : undefined
|
selectedLocation ? { value: selectedLocation.value, label: t(selectedLocation.label) } : undefined
|
||||||
}
|
}
|
||||||
setSelectedLocation={setSelectedLocation}
|
setSelectedLocation={setSelectedLocation}
|
||||||
setEditingLocationType={setEditingLocationType}
|
setEditingLocationType={setEditingLocationType}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { SchedulingType } from "@prisma/client/";
|
import { SchedulingType } from "@prisma/client/";
|
||||||
import { EventTypeSetupInfered, FormValues } from "pages/event-types/[type]";
|
import { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { Controller, useFormContext } from "react-hook-form";
|
import { Controller, useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
import CheckedTeamSelect from "@calcom/features/eventtypes/components/CheckedTeamSelect";
|
import CheckedTeamSelect from "@calcom/features/eventtypes/components/CheckedTeamSelect";
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Avatar, Button, Icon, Label, Select } from "@calcom/ui";
|
import { Label, Select } from "@calcom/ui";
|
||||||
|
|
||||||
interface IMemberToValue {
|
interface IMemberToValue {
|
||||||
id: number | null;
|
id: number | null;
|
||||||
@@ -26,8 +26,7 @@ export const EventTeamTab = ({
|
|||||||
eventType,
|
eventType,
|
||||||
team,
|
team,
|
||||||
teamMembers,
|
teamMembers,
|
||||||
currentUserMembership,
|
}: Pick<EventTypeSetupProps, "eventType" | "teamMembers" | "team">) => {
|
||||||
}: Pick<EventTypeSetupInfered, "eventType" | "teamMembers" | "team" | "currentUserMembership">) => {
|
|
||||||
const formMethods = useFormContext<FormValues>();
|
const formMethods = useFormContext<FormValues>();
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
@@ -102,48 +101,3 @@ export const EventTeamTab = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TeamProfileCard = ({
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
hasPermsToDelete,
|
|
||||||
email,
|
|
||||||
username,
|
|
||||||
}: {
|
|
||||||
id: number;
|
|
||||||
name: string | null;
|
|
||||||
email: string;
|
|
||||||
username: string | null;
|
|
||||||
hasPermsToDelete: boolean;
|
|
||||||
}) => {
|
|
||||||
const formMethods = useFormContext<FormValues>();
|
|
||||||
const avatar = `${WEBAPP_URL}/${username}/avatar.png`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-center border border-gray-300 px-6 py-4 first:rounded-t-md last:rounded-b-md only:rounded-md">
|
|
||||||
<Avatar imageSrc={avatar} gravatarFallbackMd5="ajsdkwakhsd1231" size="md" alt="Team Image" />
|
|
||||||
<div className="flex flex-col pl-3">
|
|
||||||
<div className="">
|
|
||||||
<span className="pr-2 text-sm font-semibold leading-none text-black">{name}</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm font-normal leading-normal text-gray-700">{email}</p>
|
|
||||||
</div>
|
|
||||||
{hasPermsToDelete && (
|
|
||||||
<div className="ml-auto">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
size="icon"
|
|
||||||
color="destructive"
|
|
||||||
StartIcon={Icon.FiTrash}
|
|
||||||
onClick={() => {
|
|
||||||
formMethods.setValue(
|
|
||||||
"users",
|
|
||||||
formMethods.getValues("users").filter((user) => user !== id.toString())
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EventTypeSetupInfered, FormValues } from "pages/event-types/[type]";
|
import { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { TbWebhook } from "react-icons/tb";
|
import { TbWebhook } from "react-icons/tb";
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ import { Button, Dialog, DialogContent, EmptyScreen, Icon, showToast } from "@ca
|
|||||||
export const EventTeamWebhooksTab = ({
|
export const EventTeamWebhooksTab = ({
|
||||||
eventType,
|
eventType,
|
||||||
team,
|
team,
|
||||||
}: Pick<EventTypeSetupInfered, "eventType" | "team">) => {
|
}: Pick<EventTypeSetupProps, "eventType" | "team">) => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
const utils = trpc.useContext();
|
const utils = trpc.useContext();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { TFunction } from "next-i18next";
|
import { TFunction } from "next-i18next";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { EventTypeSetupInfered, FormValues } from "pages/event-types/[type]";
|
import { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { UseFormReturn } from "react-hook-form";
|
import { UseFormReturn } from "react-hook-form";
|
||||||
import { TbWebhook } from "react-icons/tb";
|
import { TbWebhook } from "react-icons/tb";
|
||||||
@@ -37,9 +37,9 @@ import { EmbedButton, EmbedDialog } from "@components/Embed";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
eventType: EventTypeSetupInfered["eventType"];
|
eventType: EventTypeSetupProps["eventType"];
|
||||||
currentUserMembership: EventTypeSetupInfered["currentUserMembership"];
|
currentUserMembership: EventTypeSetupProps["currentUserMembership"];
|
||||||
team: EventTypeSetupInfered["team"];
|
team: EventTypeSetupProps["team"];
|
||||||
disableBorder?: boolean;
|
disableBorder?: boolean;
|
||||||
enabledAppsNumber: number;
|
enabledAppsNumber: number;
|
||||||
installedAppsNumber: number;
|
installedAppsNumber: number;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export default function RecurringEventController({
|
|||||||
{recurringEventState && (
|
{recurringEventState && (
|
||||||
<div data-testid="recurring-event-collapsible" className="text-sm">
|
<div data-testid="recurring-event-collapsible" className="text-sm">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<p className="mr-2 text-neutral-900">{t("repeats_every")}</p>
|
<p className="text-neutral-900 ltr:mr-2 rtl:ml-2">{t("repeats_every")}</p>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
@@ -89,7 +89,7 @@ export default function RecurringEventController({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 flex items-center">
|
<div className="mt-4 flex items-center">
|
||||||
<p className="mr-2 text-neutral-900">{t("for_a_maximum_of")}</p>
|
<p className="text-neutral-900 ltr:mr-2 rtl:ml-2">{t("for_a_maximum_of")}</p>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
@@ -105,7 +105,7 @@ export default function RecurringEventController({
|
|||||||
setRecurringEventState(newVal);
|
setRecurringEventState(newVal);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<p className="mr-2 text-neutral-900">
|
<p className="text-neutral-900 ltr:mr-2 rtl:ml-2">
|
||||||
{t("events", {
|
{t("events", {
|
||||||
count: recurringEventState.count,
|
count: recurringEventState.count,
|
||||||
})}
|
})}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Toaster } from "react-hot-toast";
|
import { Toaster } from "react-hot-toast";
|
||||||
|
|
||||||
import { StepCard, Steps } from "../../..";
|
import { StepCard, Steps } from "@calcom/ui";
|
||||||
|
|
||||||
export default function WizardLayout({
|
export default function WizardLayout({
|
||||||
children,
|
children,
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
We don't have many layouts currently however, if they are relevant to a features - please put them in their relvant component folder
|
||||||
|
in `web/components` if you believe they will be reused in other apps please place them in `packages/features/[feature]`
|
||||||
|
|
||||||
|
Thanks Sean 🔥
|
||||||
@@ -82,7 +82,7 @@ const DisableTwoFactorAuthModal = ({ onDisable, onCancel }: DisableTwoFactorAuth
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||||
<Button type="submit" className="ltr:ml-2 rtl:mr-2" disabled={isDisabling}>
|
<Button type="submit" className="ltr:ml-2 ltr:mr-2 rtl:ml-2" disabled={isDisabling}>
|
||||||
{t("disable")}
|
{t("disable")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="secondary" onClick={onCancel}>
|
<Button color="secondary" onClick={onCancel}>
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
|
|||||||
<WithStep step={SetupStep.ConfirmPassword} current={step}>
|
<WithStep step={SetupStep.ConfirmPassword} current={step}>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="ltr:ml-2 rtl:mr-2"
|
className="ltr:ml-2 ltr:mr-2 rtl:ml-2"
|
||||||
onClick={handleSetup}
|
onClick={handleSetup}
|
||||||
disabled={password.length === 0 || isSubmitting}>
|
disabled={password.length === 0 || isSubmitting}>
|
||||||
{t("continue")}
|
{t("continue")}
|
||||||
@@ -186,13 +186,13 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
|
|||||||
<WithStep step={SetupStep.DisplayQrCode} current={step}>
|
<WithStep step={SetupStep.DisplayQrCode} current={step}>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="ltr:ml-2 rtl:mr-2"
|
className="ltr:ml-2 ltr:mr-2 rtl:ml-2"
|
||||||
onClick={() => setStep(SetupStep.EnterTotpCode)}>
|
onClick={() => setStep(SetupStep.EnterTotpCode)}>
|
||||||
{t("continue")}
|
{t("continue")}
|
||||||
</Button>
|
</Button>
|
||||||
</WithStep>
|
</WithStep>
|
||||||
<WithStep step={SetupStep.EnterTotpCode} current={step}>
|
<WithStep step={SetupStep.EnterTotpCode} current={step}>
|
||||||
<Button type="submit" className="ltr:ml-2 rtl:mr-2" disabled={isSubmitting}>
|
<Button type="submit" className="ltr:ml-2 ltr:mr-2 rtl:ml-2" disabled={isSubmitting}>
|
||||||
{t("enable")}
|
{t("enable")}
|
||||||
</Button>
|
</Button>
|
||||||
</WithStep>
|
</WithStep>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { ShieldCheckIcon } from "@calcom/ui";
|
import { Icon } from "@calcom/ui";
|
||||||
|
|
||||||
const TwoFactorModalHeader = ({ title, description }: { title: string; description: string }) => {
|
const TwoFactorModalHeader = ({ title, description }: { title: string; description: string }) => {
|
||||||
return (
|
return (
|
||||||
<div className="mb-4 sm:flex sm:items-start">
|
<div className="mb-4 sm:flex sm:items-start">
|
||||||
<div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10">
|
<div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10">
|
||||||
<ShieldCheckIcon className="h-6 w-6 text-white" />
|
<Icon.ShieldCheckIcon className="h-6 w-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||||
<h3 className="font-cal text-lg font-medium leading-6 text-gray-900" id="modal-title">
|
<h3 className="font-cal text-lg font-medium leading-6 text-gray-900" id="modal-title">
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export function CalendarSwitch(props: {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div className="flex space-x-2 py-1">
|
<div className="flex space-x-2 py-1 rtl:space-x-reverse">
|
||||||
<Switch
|
<Switch
|
||||||
key={props.externalId}
|
key={props.externalId}
|
||||||
name="enabled"
|
name="enabled"
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ const DisableTwoFactorAuthModal = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||||
<Button type="submit" className="ltr:ml-2 rtl:mr-2" disabled={isDisabling}>
|
<Button type="submit" className="ltr:ml-2 ltr:mr-2 rtl:ml-2" disabled={isDisabling}>
|
||||||
{t("disable")}
|
{t("disable")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="secondary" onClick={onCancel}>
|
<Button color="secondary" onClick={onCancel}>
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
|
|||||||
<WithStep step={SetupStep.ConfirmPassword} current={step}>
|
<WithStep step={SetupStep.ConfirmPassword} current={step}>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="ltr:ml-2 rtl:mr-2"
|
className="ltr:ml-2 ltr:mr-2 rtl:ml-2"
|
||||||
onClick={handleSetup}
|
onClick={handleSetup}
|
||||||
disabled={password.length === 0 || isSubmitting}>
|
disabled={password.length === 0 || isSubmitting}>
|
||||||
{t("continue")}
|
{t("continue")}
|
||||||
@@ -179,13 +179,13 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
|
|||||||
<WithStep step={SetupStep.DisplayQrCode} current={step}>
|
<WithStep step={SetupStep.DisplayQrCode} current={step}>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="ltr:ml-2 rtl:mr-2"
|
className="ltr:ml-2 ltr:mr-2 rtl:ml-2"
|
||||||
onClick={() => setStep(SetupStep.EnterTotpCode)}>
|
onClick={() => setStep(SetupStep.EnterTotpCode)}>
|
||||||
{t("continue")}
|
{t("continue")}
|
||||||
</Button>
|
</Button>
|
||||||
</WithStep>
|
</WithStep>
|
||||||
<WithStep step={SetupStep.EnterTotpCode} current={step}>
|
<WithStep step={SetupStep.EnterTotpCode} current={step}>
|
||||||
<Button type="submit" className="ltr:ml-2 rtl:mr-2" disabled={isSubmitting}>
|
<Button type="submit" className="ltr:ml-2 ltr:mr-2 rtl:ml-2" disabled={isSubmitting}>
|
||||||
{t("enable")}
|
{t("enable")}
|
||||||
</Button>
|
</Button>
|
||||||
</WithStep>
|
</WithStep>
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
import { APP_NAME, LOGO } from "@calcom/lib/constants";
|
import { APP_NAME, LOGO } from "@calcom/lib/constants";
|
||||||
|
import { Credits, HeadSeo } from "@calcom/ui";
|
||||||
|
|
||||||
import Loader from "@components/Loader";
|
import Loader from "@components/Loader";
|
||||||
import { HeadSeo } from "@components/seo/head-seo";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -23,6 +22,7 @@ export default function AuthContainer(props: React.PropsWithChildren<Props>) {
|
|||||||
// eslint-disable-next-line @next/next/no-img-element
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
<img className="mb-auto h-4" src={LOGO} alt={`${APP_NAME} Logo`} />
|
<img className="mb-auto h-4" src={LOGO} alt={`${APP_NAME} Logo`} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={classNames(props.showLogo ? "text-center" : "", "sm:mx-auto sm:w-full sm:max-w-md")}>
|
<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>}
|
{props.heading && <h2 className="font-cal text-center text-3xl text-neutral-900">{props.heading}</h2>}
|
||||||
</div>
|
</div>
|
||||||
@@ -35,7 +35,10 @@ export default function AuthContainer(props: React.PropsWithChildren<Props>) {
|
|||||||
<div className="border-1 mx-2 rounded-md border-gray-200 bg-white px-4 py-10 sm:px-10">
|
<div className="border-1 mx-2 rounded-md border-gray-200 bg-white px-4 py-10 sm:px-10">
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-8 text-center text-sm text-neutral-600">{props.footerText}</div>
|
<div className="mt-8 text-center text-sm text-neutral-600">
|
||||||
|
{props.footerText}
|
||||||
|
<Credits />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const AvatarGroup = function AvatarGroup(props: AvatarGroupProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li key={idx} className="-mr-2 inline-block">
|
<li key={idx} className="-ltr:mr-2 inline-block rtl:ml-2">
|
||||||
{item.href ? (
|
{item.href ? (
|
||||||
<Link href={item.href}>
|
<Link href={item.href}>
|
||||||
<a>{avatar}</a>
|
<a>{avatar}</a>
|
||||||
|
|||||||
@@ -11,17 +11,7 @@ import { User } from "@calcom/prisma/client";
|
|||||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||||
import { RouterOutputs, trpc } from "@calcom/trpc/react";
|
import { RouterOutputs, trpc } from "@calcom/trpc/react";
|
||||||
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||||
import {
|
import { Button, Dialog, DialogClose, DialogContent, DialogHeader, Icon, Input, Label } from "@calcom/ui";
|
||||||
Button,
|
|
||||||
Dialog,
|
|
||||||
DialogClose,
|
|
||||||
DialogContent,
|
|
||||||
DialogHeader,
|
|
||||||
Icon,
|
|
||||||
Input,
|
|
||||||
Label,
|
|
||||||
StarIconSolid,
|
|
||||||
} from "@calcom/ui";
|
|
||||||
|
|
||||||
export enum UsernameChangeStatusEnum {
|
export enum UsernameChangeStatusEnum {
|
||||||
UPGRADE = "UPGRADE",
|
UPGRADE = "UPGRADE",
|
||||||
@@ -240,7 +230,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
|||||||
isInputUsernamePremium ? "text-orange-400" : "",
|
isInputUsernamePremium ? "text-orange-400" : "",
|
||||||
usernameIsAvailable ? "" : ""
|
usernameIsAvailable ? "" : ""
|
||||||
)}>
|
)}>
|
||||||
{isInputUsernamePremium ? <StarIconSolid className="mt-[2px] w-6" /> : <></>}
|
{isInputUsernamePremium ? <Icon.StarIconSolid className="mt-[2px] w-6" /> : <></>}
|
||||||
{!isInputUsernamePremium && usernameIsAvailable ? <Icon.FiCheck className="mt-2 w-6" /> : <></>}
|
{!isInputUsernamePremium && usernameIsAvailable ? <Icon.FiCheck className="mt-2 w-6" /> : <></>}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,17 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { AppRouter } from "@calcom/trpc/server/routers/_app";
|
import { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||||
import { Button, Dialog, DialogClose, DialogContent, DialogHeader, Icon, Input, Label } from "@calcom/ui";
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
Icon,
|
||||||
|
Input,
|
||||||
|
Label,
|
||||||
|
TextField,
|
||||||
|
} from "@calcom/ui";
|
||||||
|
|
||||||
interface ICustomUsernameProps {
|
interface ICustomUsernameProps {
|
||||||
currentUsername: string | undefined;
|
currentUsername: string | undefined;
|
||||||
@@ -77,10 +87,9 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
|
|||||||
|
|
||||||
const ActionButtons = () => {
|
const ActionButtons = () => {
|
||||||
return usernameIsAvailable && currentUsername !== inputUsernameValue ? (
|
return usernameIsAvailable && currentUsername !== inputUsernameValue ? (
|
||||||
<div className="flex flex-row">
|
<div className="rlt:space-x-reverse mt-px flex flex-row space-x-2 ltr:ml-2 ltr:mr-2 rtl:ml-2 rtl:space-x-reverse">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
className="mx-2"
|
|
||||||
onClick={() => setOpenDialogSaveUsername(true)}
|
onClick={() => setOpenDialogSaveUsername(true)}
|
||||||
data-testid="update-username-btn">
|
data-testid="update-username-btn">
|
||||||
{t("update")}
|
{t("update")}
|
||||||
@@ -88,7 +97,6 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
color="minimal"
|
color="minimal"
|
||||||
className="mx-2"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (currentUsername) {
|
if (currentUsername) {
|
||||||
setInputUsernameValue(currentUsername);
|
setInputUsernameValue(currentUsername);
|
||||||
@@ -110,26 +118,20 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div>
|
|
||||||
<Label htmlFor="username">{t("username")}</Label>
|
|
||||||
</div>
|
|
||||||
<div className="mt-2 flex rounded-md">
|
<div className="mt-2 flex rounded-md">
|
||||||
<span
|
|
||||||
className={classNames(
|
|
||||||
"inline-flex items-center rounded-l-md border border-r-0 border-gray-300 bg-gray-50 px-3 text-sm text-gray-500"
|
|
||||||
)}>
|
|
||||||
{process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")}/
|
|
||||||
</span>
|
|
||||||
<div className="relative w-full">
|
<div className="relative w-full">
|
||||||
<Input
|
<TextField
|
||||||
ref={usernameRef}
|
ref={usernameRef}
|
||||||
name="username"
|
name="username"
|
||||||
value={inputUsernameValue}
|
value={inputUsernameValue}
|
||||||
|
addOnLeading={
|
||||||
|
<>{process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")}/</>
|
||||||
|
}
|
||||||
autoComplete="none"
|
autoComplete="none"
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
autoCorrect="none"
|
autoCorrect="none"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"mb-0 mt-0 h-6 rounded-md rounded-l-none",
|
"mb-0 mt-0 h-6 rounded-md ltr:rounded-l-none rtl:rounded-r-none",
|
||||||
markAsError
|
markAsError
|
||||||
? "focus:shadow-0 focus:ring-shadow-0 border-red-500 focus:border-red-500 focus:outline-none focus:ring-0"
|
? "focus:shadow-0 focus:ring-shadow-0 border-red-500 focus:border-red-500 focus:outline-none focus:ring-0"
|
||||||
: ""
|
: ""
|
||||||
@@ -141,18 +143,18 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
|
|||||||
data-testid="username-input"
|
data-testid="username-input"
|
||||||
/>
|
/>
|
||||||
{currentUsername !== inputUsernameValue && (
|
{currentUsername !== inputUsernameValue && (
|
||||||
<div className="absolute right-[2px] top-0 flex flex-row">
|
<div className="absolute right-[2px] top-6 flex flex-row">
|
||||||
<span className={classNames("mx-2 py-2")}>
|
<span className={classNames("mx-2 py-2")}>
|
||||||
{usernameIsAvailable ? <Icon.FiCheck className="mt-[2px] w-6" /> : <></>}
|
{usernameIsAvailable ? <Icon.FiCheck className="w-6" /> : <></>}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden md:inline">
|
<div className="mt-5 hidden md:inline">
|
||||||
<ActionButtons />
|
<ActionButtons />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{markAsError && <p className="mt-1 text-xs text-red-500">Username is already taken</p>}
|
{markAsError && <p className="mt-1 text-xs text-red-500">{t("username_already_taken")}</p>}
|
||||||
|
|
||||||
{usernameIsAvailable && currentUsername !== inputUsernameValue && (
|
{usernameIsAvailable && currentUsername !== inputUsernameValue && (
|
||||||
<div className="mt-2 flex justify-end md:hidden">
|
<div className="mt-2 flex justify-end md:hidden">
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ function Select<
|
|||||||
IndicatorSeparator: () => null,
|
IndicatorSeparator: () => null,
|
||||||
Input: InputComponent,
|
Input: InputComponent,
|
||||||
}}
|
}}
|
||||||
className={classNames("text-sm", className)}
|
className={classNames("border-0 text-sm", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import parser from "accept-language-parser";
|
import parser from "accept-language-parser";
|
||||||
import { IncomingMessage } from "http";
|
import { IncomingMessage } from "http";
|
||||||
|
|
||||||
import prisma from "@calcom/prisma";
|
|
||||||
import { Maybe } from "@calcom/trpc/server";
|
import { Maybe } from "@calcom/trpc/server";
|
||||||
|
|
||||||
import { getSession } from "@lib/auth";
|
import { getSession } from "@lib/auth";
|
||||||
@@ -17,6 +16,7 @@ export function getLocaleFromHeaders(req: IncomingMessage): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getOrSetUserLocaleFromHeaders = async (req: IncomingMessage): Promise<string> => {
|
export const getOrSetUserLocaleFromHeaders = async (req: IncomingMessage): Promise<string> => {
|
||||||
|
const prisma = (await import("@calcom/prisma")).default;
|
||||||
const session = await getSession({ req });
|
const session = await getSession({ req });
|
||||||
const preferredLocale = getLocaleFromHeaders(req);
|
const preferredLocale = getLocaleFromHeaders(req);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@calcom/web",
|
"name": "@calcom/web",
|
||||||
"version": "2.4.2",
|
"version": "2.4.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"analyze": "ANALYZE=true next build",
|
"analyze": "ANALYZE=true next build",
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ import { useRouter } from "next/router";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { COMPANY_NAME, DEVELOPER_DOCS, DOCS_URL, JOIN_SLACK, WEBSITE_URL } from "@calcom/lib/constants";
|
import { COMPANY_NAME, DEVELOPER_DOCS, DOCS_URL, JOIN_SLACK, WEBSITE_URL } from "@calcom/lib/constants";
|
||||||
import { Icon } from "@calcom/ui";
|
import { Icon, HeadSeo } from "@calcom/ui";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
import { useLocale } from "@lib/hooks/useLocale";
|
||||||
|
|
||||||
import { HeadSeo } from "@components/seo/head-seo";
|
|
||||||
|
|
||||||
import { ssgInit } from "@server/lib/ssg";
|
import { ssgInit } from "@server/lib/ssg";
|
||||||
|
|
||||||
export default function Custom404() {
|
export default function Custom404() {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calco
|
|||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import { baseEventTypeSelect } from "@calcom/prisma/selects";
|
import { baseEventTypeSelect } from "@calcom/prisma/selects";
|
||||||
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||||
import { BadgeCheckIcon, Icon } from "@calcom/ui";
|
import { Icon, HeadSeo } from "@calcom/ui";
|
||||||
|
|
||||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
import { EmbedProps } from "@lib/withEmbedSsr";
|
import { EmbedProps } from "@lib/withEmbedSsr";
|
||||||
@@ -37,7 +37,6 @@ import { AvatarSSR } from "@components/ui/AvatarSSR";
|
|||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
const HeadSeo = dynamic(() => import("@components/seo/head-seo"));
|
|
||||||
export default function User(props: inferSSRProps<typeof getServerSideProps> & EmbedProps) {
|
export default function User(props: inferSSRProps<typeof getServerSideProps> & EmbedProps) {
|
||||||
const { users, profile, eventTypes, isDynamicGroup, dynamicNames, dynamicUsernames, isSingleUser } = props;
|
const { users, profile, eventTypes, isDynamicGroup, dynamicNames, dynamicUsernames, isSingleUser } = props;
|
||||||
const [user] = users; //To be used when we only have a single user, not dynamic group
|
const [user] = users; //To be used when we only have a single user, not dynamic group
|
||||||
@@ -139,7 +138,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
|
|||||||
<h1 className="font-cal mb-1 text-3xl text-neutral-900 dark:text-white">
|
<h1 className="font-cal mb-1 text-3xl text-neutral-900 dark:text-white">
|
||||||
{nameOrUsername}
|
{nameOrUsername}
|
||||||
{user.verified && (
|
{user.verified && (
|
||||||
<BadgeCheckIcon className="mx-1 -mt-1 inline h-6 w-6 text-blue-500 dark:text-white" />
|
<Icon.BadgeCheckIcon className="mx-1 -mt-1 inline h-6 w-6 text-blue-500 dark:text-white" />
|
||||||
)}
|
)}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="dark:text-darkgray-600 text-s text-neutral-500">{user.bio}</p>
|
<p className="dark:text-darkgray-600 text-s text-neutral-500">{user.bio}</p>
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import { seoConfig } from "@lib/config/next-seo.config";
|
|||||||
|
|
||||||
import I18nLanguageHandler from "@components/I18nLanguageHandler";
|
import I18nLanguageHandler from "@components/I18nLanguageHandler";
|
||||||
|
|
||||||
import "../styles/fonts.css";
|
|
||||||
import "../styles/globals.css";
|
import "../styles/globals.css";
|
||||||
|
|
||||||
function MyApp(props: AppProps) {
|
function MyApp(props: AppProps) {
|
||||||
@@ -32,7 +31,6 @@ function MyApp(props: AppProps) {
|
|||||||
}
|
}
|
||||||
// Use the layout defined at the page level, if available
|
// Use the layout defined at the page level, if available
|
||||||
const getLayout = Component.getLayout ?? ((page) => page);
|
const getLayout = Component.getLayout ?? ((page) => page);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppProviders {...props}>
|
<AppProviders {...props}>
|
||||||
<DefaultSeo {...seoConfig.defaultNextSeo} />
|
<DefaultSeo {...seoConfig.defaultNextSeo} />
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import Document, { DocumentContext, Head, Html, Main, NextScript, DocumentProps } from "next/document";
|
import Document, { DocumentContext, Head, Html, Main, NextScript, DocumentProps } from "next/document";
|
||||||
import Script from "next/script";
|
import Script from "next/script";
|
||||||
|
|
||||||
|
import { getDirFromLang } from "@calcom/lib/i18n";
|
||||||
|
|
||||||
type Props = Record<string, unknown> & DocumentProps;
|
type Props = Record<string, unknown> & DocumentProps;
|
||||||
|
|
||||||
function toRunBeforeReactOnClient() {
|
function toRunBeforeReactOnClient() {
|
||||||
@@ -47,7 +49,7 @@ class MyDocument extends Document<Props> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { locale } = this.props.__NEXT_DATA__;
|
const { locale } = this.props.__NEXT_DATA__;
|
||||||
const dir = locale === "ar" || locale === "he" ? "rtl" : "ltr";
|
const dir = getDirFromLang(locale);
|
||||||
return (
|
return (
|
||||||
<Html lang={locale} dir={dir}>
|
<Html lang={locale} dir={dir}>
|
||||||
<Head>
|
<Head>
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ const providers: Provider[] = [
|
|||||||
username: user.username,
|
username: user.username,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
name: user.name,
|
name: user.name,
|
||||||
role: "USER",
|
role: "INACTIVE_ADMIN",
|
||||||
belongsToActiveTeam: hasActiveTeams,
|
belongsToActiveTeam: hasActiveTeams,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { fetcher } from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter";
|
||||||
|
import { getSession } from "@calcom/lib/auth";
|
||||||
|
import { defaultHandler, defaultResponder } from "@calcom/lib/server";
|
||||||
|
|
||||||
|
const getAccessLinkSchema = z.union([
|
||||||
|
z.object({
|
||||||
|
download_link: z.string(),
|
||||||
|
expires: z.number(),
|
||||||
|
}),
|
||||||
|
z.object({}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const requestQuery = z.object({
|
||||||
|
recordingId: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof getAccessLinkSchema> | void>
|
||||||
|
) {
|
||||||
|
const { recordingId } = requestQuery.parse(req.query);
|
||||||
|
const session = await getSession({ req });
|
||||||
|
|
||||||
|
// Check if user belong to active team
|
||||||
|
if (!session?.user?.belongsToActiveTeam) {
|
||||||
|
return res.status(403);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await fetcher(`/recordings/${recordingId}/access-link`).then(getAccessLinkSchema.parse);
|
||||||
|
|
||||||
|
if ("download_link" in response && response.download_link) {
|
||||||
|
return res.status(200).json(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(400);
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defaultHandler({
|
||||||
|
GET: Promise.resolve({ default: defaultResponder(handler) }),
|
||||||
|
});
|
||||||
@@ -7,7 +7,9 @@ import { getSession } from "@calcom/lib/auth";
|
|||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import type { AppCategories } from "@calcom/prisma/client";
|
import type { AppCategories } from "@calcom/prisma/client";
|
||||||
import { inferSSRProps } from "@calcom/types/inferSSRProps";
|
import { inferSSRProps } from "@calcom/types/inferSSRProps";
|
||||||
import { AllApps, AppsLayout, AppStoreCategories, Icon, TextField, TrendingAppsSlider } from "@calcom/ui";
|
import { AllApps, AppStoreCategories, Icon, TextField, TrendingAppsSlider } from "@calcom/ui";
|
||||||
|
|
||||||
|
import AppsLayout from "@components/apps/layouts/AppsLayout";
|
||||||
|
|
||||||
import { ssgInit } from "@server/lib/ssg";
|
import { ssgInit } from "@server/lib/ssg";
|
||||||
|
|
||||||
|
|||||||
@@ -14,16 +14,16 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
EmptyScreen,
|
EmptyScreen,
|
||||||
Icon,
|
Icon,
|
||||||
InstalledAppsLayout,
|
|
||||||
List,
|
List,
|
||||||
ShellSubHeading,
|
ShellSubHeading,
|
||||||
SkeletonLoader,
|
AppSkeletonLoader as SkeletonLoader,
|
||||||
} from "@calcom/ui";
|
} from "@calcom/ui";
|
||||||
|
|
||||||
import { QueryCell } from "@lib/QueryCell";
|
import { QueryCell } from "@lib/QueryCell";
|
||||||
|
|
||||||
import { CalendarListContainer } from "@components/apps/CalendarListContainer";
|
import { CalendarListContainer } from "@components/apps/CalendarListContainer";
|
||||||
import IntegrationListItem from "@components/apps/IntegrationListItem";
|
import IntegrationListItem from "@components/apps/IntegrationListItem";
|
||||||
|
import InstalledAppsLayout from "@components/apps/layouts/InstalledAppsLayout";
|
||||||
|
|
||||||
function ConnectOrDisconnectIntegrationButton(props: {
|
function ConnectOrDisconnectIntegrationButton(props: {
|
||||||
credentialIds: number[];
|
credentialIds: number[];
|
||||||
@@ -32,7 +32,7 @@ function ConnectOrDisconnectIntegrationButton(props: {
|
|||||||
installed?: boolean;
|
installed?: boolean;
|
||||||
invalidCredentialIds?: number[];
|
invalidCredentialIds?: number[];
|
||||||
}) {
|
}) {
|
||||||
const { type, credentialIds, isGlobal, installed, invalidCredentialIds } = props;
|
const { type, credentialIds, isGlobal, installed } = props;
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const [credentialId] = credentialIds;
|
const [credentialId] = credentialIds;
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ export default function Login({
|
|||||||
{...register("email")}
|
{...register("email")}
|
||||||
/>
|
/>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute right-0 -top-[6px] z-10">
|
<div className="absolute -top-[6px] z-10 ltr:right-0 rtl:left-0">
|
||||||
<Link href="/auth/forgot-password">
|
<Link href="/auth/forgot-password">
|
||||||
<a tabIndex={-1} className="text-sm font-medium text-gray-600">
|
<a tabIndex={-1} className="text-sm font-medium text-gray-600">
|
||||||
{t("forgot")}
|
{t("forgot")}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => void }) => {
|
|||||||
<TextField
|
<TextField
|
||||||
addOnLeading={
|
addOnLeading={
|
||||||
!longWebsiteUrl && (
|
!longWebsiteUrl && (
|
||||||
<span className="items-centerpx-3 inline-flex rounded-none text-sm text-gray-500">
|
<span className="inline-flex items-center rounded-none px-3 text-sm text-gray-500">
|
||||||
{process.env.NEXT_PUBLIC_WEBSITE_URL}/
|
{process.env.NEXT_PUBLIC_WEBSITE_URL}/
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import * as React from "react";
|
|||||||
import { useEffect, useState, useRef } from "react";
|
import { useEffect, useState, useRef } from "react";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { APP_NAME, WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { Button, showToast } from "@calcom/ui";
|
import { Button, showToast } from "@calcom/ui";
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ export default function Verify() {
|
|||||||
? "Your payment failed"
|
? "Your payment failed"
|
||||||
: sessionId
|
: sessionId
|
||||||
? "Payment successful!"
|
? "Payment successful!"
|
||||||
: "Verify your email" + " | Cal.com"}
|
: "Verify your email" + " | " + APP_NAME}
|
||||||
</title>
|
</title>
|
||||||
</Head>
|
</Head>
|
||||||
<div className="flex min-h-screen flex-col items-center justify-center px-6">
|
<div className="flex min-h-screen flex-col items-center justify-center px-6">
|
||||||
|
|||||||
@@ -35,14 +35,13 @@ import { localStorage } from "@calcom/lib/webstorage";
|
|||||||
import prisma, { baseUserSelect } from "@calcom/prisma";
|
import prisma, { baseUserSelect } from "@calcom/prisma";
|
||||||
import { Prisma } from "@calcom/prisma/client";
|
import { Prisma } from "@calcom/prisma/client";
|
||||||
import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||||
import { Button, EmailInput, Icon } from "@calcom/ui";
|
import { Button, EmailInput, Icon, HeadSeo } from "@calcom/ui";
|
||||||
|
|
||||||
import { timeZone } from "@lib/clock";
|
import { timeZone } from "@lib/clock";
|
||||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
|
|
||||||
import CancelBooking from "@components/booking/CancelBooking";
|
import CancelBooking from "@components/booking/CancelBooking";
|
||||||
import EventReservationSchema from "@components/schemas/EventReservationSchema";
|
import EventReservationSchema from "@components/schemas/EventReservationSchema";
|
||||||
import { HeadSeo } from "@components/seo/head-seo";
|
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
@@ -174,7 +173,6 @@ const querySchema = z.object({
|
|||||||
export default function Success(props: SuccessProps) {
|
export default function Success(props: SuccessProps) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
allRemainingBookings,
|
allRemainingBookings,
|
||||||
isSuccessBookingPage,
|
isSuccessBookingPage,
|
||||||
@@ -636,7 +634,7 @@ export default function Success(props: SuccessProps) {
|
|||||||
encodeURIComponent(new RRule(props.eventType.recurringEvent).toString())
|
encodeURIComponent(new RRule(props.eventType.recurringEvent).toString())
|
||||||
: "")
|
: "")
|
||||||
}>
|
}>
|
||||||
<a className="mr-2 h-10 w-10 rounded-sm border border-neutral-200 px-3 py-2 dark:border-neutral-700 dark:text-white">
|
<a className="h-10 w-10 rounded-sm border border-neutral-200 px-3 py-2 ltr:mr-2 rtl:ml-2 dark:border-neutral-700 dark:text-white">
|
||||||
<svg
|
<svg
|
||||||
className="-mt-1.5 inline-block h-4 w-4"
|
className="-mt-1.5 inline-block h-4 w-4"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@@ -1051,6 +1049,15 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||||||
customInputs: customInputSchema.array().parse(eventTypeRaw.customInputs),
|
customInputs: customInputSchema.array().parse(eventTypeRaw.customInputs),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (eventType.metadata?.disableSuccessPage && eventType.successRedirectUrl) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
destination: eventType.successRedirectUrl,
|
||||||
|
permanent: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const profile = {
|
const profile = {
|
||||||
name: eventType.team?.name || eventType.users[0]?.name || null,
|
name: eventType.team?.name || eventType.users[0]?.name || null,
|
||||||
email: eventType.team ? null : eventType.users[0].email || null,
|
email: eventType.team ? null : eventType.users[0].email || null,
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ export default function Bookings() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="pt-2 xl:pt-0">
|
<div className="pt-2 xl:pt-0">
|
||||||
<p className="mb-2 text-xs font-medium uppercase leading-4 text-gray-500">{t("all")}</p>
|
{/* <p className="mb-2 text-xs font-medium uppercase leading-4 text-gray-500">{t("all")}</p> */}
|
||||||
<div className="overflow-hidden rounded-md border border-gray-200">
|
<div className="overflow-hidden rounded-md border border-gray-200">
|
||||||
<table className="w-full max-w-full table-fixed">
|
<table className="w-full max-w-full table-fixed">
|
||||||
<tbody className="divide-y divide-gray-200 bg-white" data-testid="bookings">
|
<tbody className="divide-y divide-gray-200 bg-white" data-testid="bookings">
|
||||||
|
|||||||
@@ -1,27 +1,24 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { PeriodType, Prisma, SchedulingType } from "@prisma/client";
|
import { PeriodType, SchedulingType } from "@prisma/client";
|
||||||
import { GetServerSidePropsContext } from "next";
|
import { GetServerSidePropsContext } from "next";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { StripeData } from "@calcom/app-store/stripepayment/lib/server";
|
import { EventLocationType } from "@calcom/core/location";
|
||||||
import { getEventTypeAppData, getLocationOptions } from "@calcom/app-store/utils";
|
import { validateBookingLimitOrder } from "@calcom/lib";
|
||||||
import { EventLocationType, LocationObject } from "@calcom/core/location";
|
|
||||||
import { parseBookingLimit, parseRecurringEvent, validateBookingLimitOrder } from "@calcom/lib";
|
|
||||||
import getEnabledApps from "@calcom/lib/apps/getEnabledApps";
|
|
||||||
import { CAL_URL } from "@calcom/lib/constants";
|
import { CAL_URL } from "@calcom/lib/constants";
|
||||||
import convertToNewDurationType from "@calcom/lib/convertToNewDurationType";
|
import convertToNewDurationType from "@calcom/lib/convertToNewDurationType";
|
||||||
import findDurationType from "@calcom/lib/findDurationType";
|
import findDurationType from "@calcom/lib/findDurationType";
|
||||||
import getStripeAppData from "@calcom/lib/getStripeAppData";
|
import getEventTypeById from "@calcom/lib/getEventTypeById";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc, RouterOutputs } from "@calcom/trpc/react";
|
||||||
import type { BookingLimit, RecurringEvent } from "@calcom/types/Calendar";
|
import type { BookingLimit, RecurringEvent } from "@calcom/types/Calendar";
|
||||||
import { Form, showToast } from "@calcom/ui";
|
import { Form, showToast } from "@calcom/ui";
|
||||||
|
|
||||||
@@ -41,7 +38,6 @@ import { EventTeamWebhooksTab } from "@components/eventtype/EventTeamWebhooksTab
|
|||||||
import { EventTypeSingleLayout } from "@components/eventtype/EventTypeSingleLayout";
|
import { EventTypeSingleLayout } from "@components/eventtype/EventTypeSingleLayout";
|
||||||
import EventWorkflowsTab from "@components/eventtype/EventWorkfowsTab";
|
import EventWorkflowsTab from "@components/eventtype/EventWorkfowsTab";
|
||||||
|
|
||||||
import { getTranslation } from "@server/lib/i18n";
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
export type FormValues = {
|
export type FormValues = {
|
||||||
@@ -110,27 +106,24 @@ const querySchema = z.object({
|
|||||||
.default("setup"),
|
.default("setup"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type EventTypeSetupInfered = inferSSRProps<typeof getServerSideProps>;
|
export type EventTypeSetupProps = RouterOutputs["viewer"]["eventTypes"]["get"];
|
||||||
|
|
||||||
const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
const EventTypePage = (props: EventTypeSetupProps) => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
const utils = trpc.useContext();
|
||||||
|
const router = useRouter();
|
||||||
|
const { tabName } = querySchema.parse(router.query);
|
||||||
|
|
||||||
const { data: eventTypeApps } = trpc.viewer.apps.useQuery({
|
const { data: eventTypeApps } = trpc.viewer.apps.useQuery({
|
||||||
extendsFeature: "EventType",
|
extendsFeature: "EventType",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { eventType: dbEventType, locationOptions, team, teamMembers } = props;
|
const { eventType, locationOptions, team, teamMembers, currentUserMembership } = props;
|
||||||
// TODO: It isn't a good idea to maintain state using setEventType. If we want to connect the SSR'd data to tRPC, we should useQuery(["viewer.eventTypes.get"]) with initialData
|
|
||||||
// Due to this change, when Form is saved, there is no way to propagate that info to eventType (e.g. disabling stripe app doesn't allow recurring tab to be enabled without refresh).
|
|
||||||
const [eventType, setEventType] = useState(dbEventType);
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const { tabName } = querySchema.parse(router.query);
|
|
||||||
|
|
||||||
const [animationParentRef] = useAutoAnimate<HTMLDivElement>();
|
const [animationParentRef] = useAutoAnimate<HTMLDivElement>();
|
||||||
|
|
||||||
const updateMutation = trpc.viewer.eventTypes.update.useMutation({
|
const updateMutation = trpc.viewer.eventTypes.update.useMutation({
|
||||||
onSuccess: async ({ eventType: newEventType }) => {
|
onSuccess: async () => {
|
||||||
setEventType({ ...eventType, slug: newEventType.slug });
|
|
||||||
showToast(
|
showToast(
|
||||||
t("event_type_updated_successfully", {
|
t("event_type_updated_successfully", {
|
||||||
eventTypeTitle: eventType.title,
|
eventTypeTitle: eventType.title,
|
||||||
@@ -138,6 +131,9 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||||||
"success"
|
"success"
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
async onSettled() {
|
||||||
|
await utils.viewer.eventTypes.get.invalidate();
|
||||||
|
},
|
||||||
onError: (err) => {
|
onError: (err) => {
|
||||||
let message = "";
|
let message = "";
|
||||||
if (err instanceof HttpError) {
|
if (err instanceof HttpError) {
|
||||||
@@ -241,14 +237,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
availability: <AvailabilityTab isTeamEvent={!!team} />,
|
availability: <AvailabilityTab isTeamEvent={!!team} />,
|
||||||
team: (
|
team: <EventTeamTab eventType={eventType} teamMembers={teamMembers} team={team} />,
|
||||||
<EventTeamTab
|
|
||||||
eventType={eventType}
|
|
||||||
teamMembers={teamMembers}
|
|
||||||
team={team}
|
|
||||||
currentUserMembership={props.currentUserMembership}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
limits: <EventLimitsTab eventType={eventType} />,
|
limits: <EventLimitsTab eventType={eventType} />,
|
||||||
advanced: <EventAdvancedTab eventType={eventType} team={team} />,
|
advanced: <EventAdvancedTab eventType={eventType} team={team} />,
|
||||||
recurring: <EventRecurringTab eventType={eventType} />,
|
recurring: <EventRecurringTab eventType={eventType} />,
|
||||||
@@ -272,7 +261,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||||||
isUpdateMutationLoading={updateMutation.isLoading}
|
isUpdateMutationLoading={updateMutation.isLoading}
|
||||||
formMethods={formMethods}
|
formMethods={formMethods}
|
||||||
disableBorder={tabName === "apps" || tabName === "workflows" || tabName === "webhooks"}
|
disableBorder={tabName === "apps" || tabName === "workflows" || tabName === "webhooks"}
|
||||||
currentUserMembership={props.currentUserMembership}>
|
currentUserMembership={currentUserMembership}>
|
||||||
<Form
|
<Form
|
||||||
form={formMethods}
|
form={formMethods}
|
||||||
id="event-type-form"
|
id="event-type-form"
|
||||||
@@ -337,6 +326,18 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const EventTypePageWrapper = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
|
const { data, isLoading } = trpc.viewer.eventTypes.get.useQuery(
|
||||||
|
{ id: props.type },
|
||||||
|
{
|
||||||
|
initialData: props.initialData,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isLoading || !data) return null;
|
||||||
|
return <EventTypePage {...data} />;
|
||||||
|
};
|
||||||
|
|
||||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||||
const { req, query } = context;
|
const { req, query } = context;
|
||||||
const session = await getSession({ req });
|
const session = await getSession({ req });
|
||||||
@@ -358,244 +359,27 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const userSelect = Prisma.validator<Prisma.UserSelect>()({
|
try {
|
||||||
name: true,
|
const res = await getEventTypeById({ eventTypeId: typeParam, userId: session.user.id, prisma });
|
||||||
username: true,
|
return {
|
||||||
id: true,
|
props: {
|
||||||
avatar: true,
|
session,
|
||||||
email: true,
|
type: typeParam,
|
||||||
locale: true,
|
trpcState: ssr.dehydrate(),
|
||||||
defaultScheduleId: true,
|
initialData: {
|
||||||
});
|
eventType: res.eventType,
|
||||||
|
locationOptions: res.locationOptions,
|
||||||
const rawEventType = await prisma.eventType.findFirst({
|
team: res.team,
|
||||||
where: {
|
teamMembers: res.teamMembers,
|
||||||
AND: [
|
currentUserMembership: res.currentUserMembership,
|
||||||
{
|
|
||||||
OR: [
|
|
||||||
{
|
|
||||||
users: {
|
|
||||||
some: {
|
|
||||||
id: session.user.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
team: {
|
|
||||||
members: {
|
|
||||||
some: {
|
|
||||||
userId: session.user.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userId: session.user.id,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: typeParam,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
title: true,
|
|
||||||
slug: true,
|
|
||||||
description: true,
|
|
||||||
length: true,
|
|
||||||
hidden: true,
|
|
||||||
locations: true,
|
|
||||||
eventName: true,
|
|
||||||
customInputs: true,
|
|
||||||
timeZone: true,
|
|
||||||
periodType: true,
|
|
||||||
metadata: true,
|
|
||||||
periodDays: true,
|
|
||||||
periodStartDate: true,
|
|
||||||
periodEndDate: true,
|
|
||||||
periodCountCalendarDays: true,
|
|
||||||
requiresConfirmation: true,
|
|
||||||
recurringEvent: true,
|
|
||||||
hideCalendarNotes: true,
|
|
||||||
disableGuests: true,
|
|
||||||
minimumBookingNotice: true,
|
|
||||||
beforeEventBuffer: true,
|
|
||||||
afterEventBuffer: true,
|
|
||||||
slotInterval: true,
|
|
||||||
hashedLink: true,
|
|
||||||
bookingLimits: true,
|
|
||||||
successRedirectUrl: true,
|
|
||||||
currency: true,
|
|
||||||
team: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
slug: true,
|
|
||||||
members: {
|
|
||||||
where: {
|
|
||||||
accepted: true,
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
role: true,
|
|
||||||
user: {
|
|
||||||
select: userSelect,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
users: {
|
};
|
||||||
select: userSelect,
|
} catch (err) {
|
||||||
},
|
|
||||||
schedulingType: true,
|
|
||||||
schedule: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
userId: true,
|
|
||||||
price: true,
|
|
||||||
destinationCalendar: true,
|
|
||||||
seatsPerTimeSlot: true,
|
|
||||||
seatsShowAttendees: true,
|
|
||||||
webhooks: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
subscriberUrl: true,
|
|
||||||
payloadTemplate: true,
|
|
||||||
active: true,
|
|
||||||
eventTriggers: true,
|
|
||||||
secret: true,
|
|
||||||
eventTypeId: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
workflows: {
|
|
||||||
include: {
|
|
||||||
workflow: {
|
|
||||||
include: {
|
|
||||||
activeOn: {
|
|
||||||
select: {
|
|
||||||
eventType: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
title: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
steps: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!rawEventType) {
|
|
||||||
return {
|
return {
|
||||||
notFound: true,
|
notFound: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const credentials = await prisma.credential.findMany({
|
|
||||||
where: {
|
|
||||||
userId: session.user.id,
|
|
||||||
app: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
type: true,
|
|
||||||
key: true,
|
|
||||||
userId: true,
|
|
||||||
appId: true,
|
|
||||||
invalid: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { locations, metadata, ...restEventType } = rawEventType;
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
const newMetadata = EventTypeMetaDataSchema.parse(metadata || {})!;
|
|
||||||
const apps = newMetadata.apps || {};
|
|
||||||
const eventTypeWithParsedMetadata = { ...rawEventType, metadata: newMetadata };
|
|
||||||
newMetadata.apps = {
|
|
||||||
...apps,
|
|
||||||
stripe: {
|
|
||||||
...getStripeAppData(eventTypeWithParsedMetadata, true),
|
|
||||||
currency:
|
|
||||||
(
|
|
||||||
credentials.find((integration) => integration.type === "stripe_payment")
|
|
||||||
?.key as unknown as StripeData
|
|
||||||
)?.default_currency || "usd",
|
|
||||||
},
|
|
||||||
giphy: getEventTypeAppData(eventTypeWithParsedMetadata, "giphy", true),
|
|
||||||
rainbow: getEventTypeAppData(eventTypeWithParsedMetadata, "rainbow", true),
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: How to extract metadata schema from _EventTypeModel to be able to parse it?
|
|
||||||
// const parsedMetaData = _EventTypeModel.parse(newMetadata);
|
|
||||||
const parsedMetaData = newMetadata;
|
|
||||||
|
|
||||||
const parsedCustomInputs = (rawEventType.customInputs || []).map((input) => customInputSchema.parse(input));
|
|
||||||
|
|
||||||
const eventType = {
|
|
||||||
...restEventType,
|
|
||||||
schedule: rawEventType.schedule?.id || rawEventType.users[0]?.defaultScheduleId || null,
|
|
||||||
recurringEvent: parseRecurringEvent(restEventType.recurringEvent),
|
|
||||||
bookingLimits: parseBookingLimit(restEventType.bookingLimits),
|
|
||||||
locations: locations as unknown as LocationObject[],
|
|
||||||
metadata: parsedMetaData,
|
|
||||||
customInputs: parsedCustomInputs,
|
|
||||||
};
|
|
||||||
|
|
||||||
// backwards compat
|
|
||||||
if (eventType.users.length === 0 && !eventType.team) {
|
|
||||||
const fallbackUser = await prisma.user.findUnique({
|
|
||||||
where: {
|
|
||||||
id: session.user.id,
|
|
||||||
},
|
|
||||||
select: userSelect,
|
|
||||||
});
|
|
||||||
if (!fallbackUser) throw Error("The event type doesn't have user and no fallback user was found");
|
|
||||||
eventType.users.push(fallbackUser);
|
|
||||||
}
|
|
||||||
const currentUser = eventType.users.find((u) => u.id === session.user.id);
|
|
||||||
const t = await getTranslation(currentUser?.locale ?? "en", "common");
|
|
||||||
const integrations = await getEnabledApps(credentials);
|
|
||||||
const locationOptions = getLocationOptions(integrations, t);
|
|
||||||
|
|
||||||
const eventTypeObject = Object.assign({}, eventType, {
|
|
||||||
periodStartDate: eventType.periodStartDate?.toString() ?? null,
|
|
||||||
periodEndDate: eventType.periodEndDate?.toString() ?? null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const teamMembers = eventTypeObject.team
|
|
||||||
? eventTypeObject.team.members.map((member) => {
|
|
||||||
const user = member.user;
|
|
||||||
user.avatar = `${CAL_URL}/${user.username}/avatar.png`;
|
|
||||||
return user;
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
|
|
||||||
// Find the current users memebership so we can check role to enable/disable deletion.
|
|
||||||
// Sets to null if no membership is found - this must mean we are in a none team event type
|
|
||||||
const currentUserMembership =
|
|
||||||
eventTypeObject.team?.members.find((el) => el.user.id === session.user.id) ?? null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
session,
|
|
||||||
eventType: eventTypeObject,
|
|
||||||
locationOptions,
|
|
||||||
team: eventTypeObject.team || null,
|
|
||||||
teamMembers,
|
|
||||||
currentUserMembership,
|
|
||||||
trpcState: ssr.dehydrate(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EventTypePage;
|
export default EventTypePageWrapper;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
|
|||||||
className="hidden font-normal leading-4 text-gray-600 sm:inline"
|
className="hidden font-normal leading-4 text-gray-600 sm:inline"
|
||||||
data-testid={"event-type-slug-" + type.id}>{`/${group.profile.slug}/${type.slug}`}</small>
|
data-testid={"event-type-slug-" + type.id}>{`/${group.profile.slug}/${type.slug}`}</small>
|
||||||
{readOnly && (
|
{readOnly && (
|
||||||
<span className="rtl:mr-2inline items-center rounded-sm bg-gray-100 px-1.5 py-0.5 text-xs font-medium text-gray-800 ltr:ml-2">
|
<span className="rtl:ml-2inline items-center rounded-sm bg-gray-100 px-1.5 py-0.5 text-xs font-medium text-gray-800 ltr:ml-2 ltr:mr-2">
|
||||||
{t("readonly") as string}
|
{t("readonly") as string}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -394,7 +394,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-9 mr-5 flex sm:hidden">
|
<div className="min-w-9 mx-5 flex sm:hidden">
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<DropdownMenuTrigger asChild data-testid={"event-type-options-" + type.id}>
|
<DropdownMenuTrigger asChild data-testid={"event-type-options-" + type.id}>
|
||||||
<Button type="button" size="icon" color="secondary" StartIcon={Icon.FiMoreHorizontal} />
|
<Button type="button" size="icon" color="secondary" StartIcon={Icon.FiMoreHorizontal} />
|
||||||
@@ -531,7 +531,7 @@ const EventTypeListHeading = ({
|
|||||||
<a className="font-bold">{profile?.name || ""}</a>
|
<a className="font-bold">{profile?.name || ""}</a>
|
||||||
</Link>
|
</Link>
|
||||||
{membershipCount && teamId && (
|
{membershipCount && teamId && (
|
||||||
<span className="relative -top-px text-xs text-neutral-500 ltr:ml-2 rtl:mr-2">
|
<span className="relative -top-px text-xs text-neutral-500 ltr:ml-2 ltr:mr-2 rtl:ml-2">
|
||||||
<Link href={`/settings/teams/${teamId}/members`}>
|
<Link href={`/settings/teams/${teamId}/members`}>
|
||||||
<a>
|
<a>
|
||||||
<Badge variant="gray">
|
<Badge variant="gray">
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ import { GetServerSidePropsContext } from "next";
|
|||||||
|
|
||||||
import AdminAppsList from "@calcom/features/apps/AdminAppsList";
|
import AdminAppsList from "@calcom/features/apps/AdminAppsList";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { getAdminLayout as getLayout, Meta } from "@calcom/ui";
|
import { Meta } from "@calcom/ui";
|
||||||
|
|
||||||
|
import { getLayout } from "@components/auth/layouts/AdminLayout";
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
@@ -11,7 +13,7 @@ function AdminAppsView() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Meta title={t("apps")} description={t("admin_apps_description")} />
|
<Meta title={t("apps")} description={t("admin_apps_description")} />
|
||||||
<AdminAppsList baseURL="/settings/admin/apps" className="w-0" />
|
<AdminAppsList baseURL="/settings/admin/apps" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { getServerSideProps } from "./[category]";
|
||||||
|
export { default } from "./[category]";
|
||||||
@@ -3,7 +3,9 @@ import { signIn } from "next-auth/react";
|
|||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Button, getAdminLayout as getLayout, Meta, TextField } from "@calcom/ui";
|
import { Button, Meta, TextField } from "@calcom/ui";
|
||||||
|
|
||||||
|
import { getLayout } from "@components/auth/layouts/AdminLayout";
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
@@ -21,7 +23,7 @@ function AdminView() {
|
|||||||
const enteredUsername = usernameRef.current?.value.toLowerCase();
|
const enteredUsername = usernameRef.current?.value.toLowerCase();
|
||||||
signIn("impersonation-auth", { username: enteredUsername });
|
signIn("impersonation-auth", { username: enteredUsername });
|
||||||
}}>
|
}}>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2 rtl:space-x-reverse">
|
||||||
<TextField
|
<TextField
|
||||||
containerClassName="w-full"
|
containerClassName="w-full"
|
||||||
name={t("user_impersonation_heading")}
|
name={t("user_impersonation_heading")}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { GetServerSidePropsContext } from "next";
|
import { GetServerSidePropsContext } from "next";
|
||||||
|
|
||||||
import { getAdminLayout as getLayout, Meta } from "@calcom/ui";
|
import { Meta } from "@calcom/ui";
|
||||||
|
|
||||||
|
import { getLayout } from "@components/auth/layouts/AdminLayout";
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { GetServerSidePropsContext } from "next";
|
import { GetServerSidePropsContext } from "next";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { getAdminLayout as getLayout, Meta } from "@calcom/ui";
|
import { Meta } from "@calcom/ui";
|
||||||
|
|
||||||
|
import { getLayout } from "@components/auth/layouts/AdminLayout";
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { useRouter } from "next/router";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { HelpScout, useChat } from "react-live-chat-loader";
|
import { HelpScout, useChat } from "react-live-chat-loader";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
import { classNames } from "@calcom/lib";
|
import { classNames } from "@calcom/lib";
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { Button, getSettingsLayout as getLayout, Icon, Meta } from "@calcom/ui";
|
import { Button, Icon, Meta } from "@calcom/ui";
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { TApiKeys } from "@calcom/ee/api-keys/components/ApiKeyListItem";
|
|||||||
import LicenseRequired from "@calcom/ee/common/components/v2/LicenseRequired";
|
import LicenseRequired from "@calcom/ee/common/components/v2/LicenseRequired";
|
||||||
import ApiKeyDialogForm from "@calcom/features/ee/api-keys/components/ApiKeyDialogForm";
|
import ApiKeyDialogForm from "@calcom/features/ee/api-keys/components/ApiKeyDialogForm";
|
||||||
import ApiKeyListItem from "@calcom/features/ee/api-keys/components/ApiKeyListItem";
|
import ApiKeyListItem from "@calcom/features/ee/api-keys/components/ApiKeyListItem";
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
import { APP_NAME } from "@calcom/lib/constants";
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
@@ -13,10 +14,9 @@ import {
|
|||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
EmptyScreen,
|
EmptyScreen,
|
||||||
getSettingsLayout as getLayout,
|
|
||||||
Icon,
|
Icon,
|
||||||
Meta,
|
Meta,
|
||||||
SkeletonLoader,
|
AppSkeletonLoader as SkeletonLoader,
|
||||||
} from "@calcom/ui";
|
} from "@calcom/ui";
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { GetServerSidePropsContext } from "next";
|
|||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
import { APP_NAME } from "@calcom/lib/constants";
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
@@ -10,7 +11,6 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
ColorPicker,
|
ColorPicker,
|
||||||
Form,
|
Form,
|
||||||
getSettingsLayout as getLayout,
|
|
||||||
Meta,
|
Meta,
|
||||||
showToast,
|
showToast,
|
||||||
SkeletonButton,
|
SkeletonButton,
|
||||||
@@ -177,7 +177,9 @@ const AppearanceView = () => {
|
|||||||
<div className="flex w-full text-sm">
|
<div className="flex w-full text-sm">
|
||||||
<div className="mr-1 flex-grow">
|
<div className="mr-1 flex-grow">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<p className="mr-2 font-semibold">{t("disable_cal_branding", { appName: APP_NAME })}</p>
|
<p className="font-semibold ltr:mr-2 rtl:ml-2">
|
||||||
|
{t("disable_cal_branding", { appName: APP_NAME })}
|
||||||
|
</p>
|
||||||
<Badge variant="gray">{t("pro")}</Badge>
|
<Badge variant="gray">{t("pro")}</Badge>
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-0.5 text-gray-600">{t("removes_cal_branding", { appName: APP_NAME })}</p>
|
<p className="mt-0.5 text-gray-600">{t("removes_cal_branding", { appName: APP_NAME })}</p>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Fragment } from "react";
|
|||||||
|
|
||||||
import DisconnectIntegration from "@calcom/features/apps/components/DisconnectIntegration";
|
import DisconnectIntegration from "@calcom/features/apps/components/DisconnectIntegration";
|
||||||
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
|
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import {
|
import {
|
||||||
@@ -13,7 +14,6 @@ import {
|
|||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
EmptyScreen,
|
EmptyScreen,
|
||||||
getSettingsLayout as getLayout,
|
|
||||||
Icon,
|
Icon,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
@@ -156,7 +156,7 @@ const CalendarsView = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
<div className="flex-grow truncate pl-2">
|
<div className="flex-grow truncate pl-2">
|
||||||
<ListItemTitle component="h3" className="mb-1 space-x-2">
|
<ListItemTitle component="h3" className="mb-1 space-x-2 rtl:space-x-reverse">
|
||||||
<Link href={"/apps/" + item.integration.slug}>
|
<Link href={"/apps/" + item.integration.slug}>
|
||||||
{item.integration.name || item.integration.title}
|
{item.integration.name || item.integration.title}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { GetServerSidePropsContext } from "next";
|
import { GetServerSidePropsContext } from "next";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import {
|
import {
|
||||||
@@ -13,7 +14,6 @@ import {
|
|||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
getSettingsLayout as getLayout,
|
|
||||||
Icon,
|
Icon,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
@@ -82,7 +82,7 @@ const ConferencingLayout = () => {
|
|||||||
app.logo && <img className="h-10 w-10" src={app.logo} alt={app.title} />
|
app.logo && <img className="h-10 w-10" src={app.logo} alt={app.title} />
|
||||||
}
|
}
|
||||||
<div className="flex-grow truncate pl-2">
|
<div className="flex-grow truncate pl-2">
|
||||||
<ListItemTitle component="h3" className="mb-1 space-x-2">
|
<ListItemTitle component="h3" className="mb-1 space-x-2 rtl:space-x-reverse">
|
||||||
<h3 className="truncate text-sm font-medium text-neutral-900">{app.title}</h3>
|
<h3 className="truncate text-sm font-medium text-neutral-900">{app.title}</h3>
|
||||||
</ListItemTitle>
|
</ListItemTitle>
|
||||||
<ListItemText component="p">{app.description}</ListItemText>
|
<ListItemText component="p">{app.description}</ListItemText>
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import { useRouter } from "next/router";
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { RouterOutputs, trpc } from "@calcom/trpc/react";
|
import { RouterOutputs, trpc } from "@calcom/trpc/react";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Form,
|
Form,
|
||||||
getSettingsLayout as getLayout,
|
|
||||||
Label,
|
Label,
|
||||||
Meta,
|
Meta,
|
||||||
Select,
|
Select,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { signOut } from "next-auth/react";
|
|||||||
import { BaseSyntheticEvent, useRef, useState } from "react";
|
import { BaseSyntheticEvent, useRef, useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
import { ErrorCode } from "@calcom/lib/auth";
|
import { ErrorCode } from "@calcom/lib/auth";
|
||||||
import { APP_NAME } from "@calcom/lib/constants";
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
@@ -21,7 +22,6 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
Form,
|
Form,
|
||||||
getSettingsLayout as getLayout,
|
|
||||||
Icon,
|
Icon,
|
||||||
ImageUploader,
|
ImageUploader,
|
||||||
Label,
|
Label,
|
||||||
@@ -340,7 +340,7 @@ const ProfileForm = ({
|
|||||||
render={({ field: { value } }) => (
|
render={({ field: { value } }) => (
|
||||||
<>
|
<>
|
||||||
<Avatar alt="" imageSrc={value} gravatarFallbackMd5={emailMd5} size="lg" />
|
<Avatar alt="" imageSrc={value} gravatarFallbackMd5={emailMd5} size="lg" />
|
||||||
<div className="ml-4">
|
<div className="ltr:ml-4 rtl:mr-4">
|
||||||
<ImageUploader
|
<ImageUploader
|
||||||
target="avatar"
|
target="avatar"
|
||||||
id="avatar-upload"
|
id="avatar-upload"
|
||||||
|
|||||||
@@ -1,18 +1,10 @@
|
|||||||
import { GetServerSidePropsContext } from "next";
|
import { GetServerSidePropsContext } from "next";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import {
|
import { Button, Form, Label, Meta, showToast, Skeleton, Switch } from "@calcom/ui";
|
||||||
Button,
|
|
||||||
Form,
|
|
||||||
getSettingsLayout as getLayout,
|
|
||||||
Label,
|
|
||||||
Meta,
|
|
||||||
showToast,
|
|
||||||
Skeleton,
|
|
||||||
Switch,
|
|
||||||
} from "@calcom/ui";
|
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ import { GetServerSidePropsContext } from "next";
|
|||||||
import { Trans } from "next-i18next";
|
import { Trans } from "next-i18next";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
import { identityProviderNameMap } from "@calcom/lib/auth";
|
import { identityProviderNameMap } from "@calcom/lib/auth";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { Button, Form, getSettingsLayout as getLayout, Meta, PasswordField, showToast } from "@calcom/ui";
|
import { Button, Form, Meta, PasswordField, showToast } from "@calcom/ui";
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,10 @@
|
|||||||
import { GetServerSidePropsContext } from "next";
|
import { GetServerSidePropsContext } from "next";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import {
|
import { Badge, Meta, Switch, SkeletonButton, SkeletonContainer, SkeletonText } from "@calcom/ui";
|
||||||
Badge,
|
|
||||||
getSettingsLayout as getLayout,
|
|
||||||
Meta,
|
|
||||||
Switch,
|
|
||||||
SkeletonButton,
|
|
||||||
SkeletonContainer,
|
|
||||||
SkeletonText,
|
|
||||||
} from "@calcom/ui";
|
|
||||||
|
|
||||||
import DisableTwoFactorModal from "@components/settings/DisableTwoFactorModal";
|
import DisableTwoFactorModal from "@components/settings/DisableTwoFactorModal";
|
||||||
import EnableTwoFactorModal from "@components/settings/EnableTwoFactorModal";
|
import EnableTwoFactorModal from "@components/settings/EnableTwoFactorModal";
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import Head from "next/head";
|
|||||||
|
|
||||||
import AddNewTeamMembers from "@calcom/features/ee/teams/components/AddNewTeamMembers";
|
import AddNewTeamMembers from "@calcom/features/ee/teams/components/AddNewTeamMembers";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { WizardLayout } from "@calcom/ui";
|
|
||||||
|
import WizardLayout from "@components/layouts/WizardLayout";
|
||||||
|
|
||||||
const OnboardTeamMembersPage = () => {
|
const OnboardTeamMembersPage = () => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import Head from "next/head";
|
|||||||
|
|
||||||
import { CreateANewTeamForm } from "@calcom/features/ee/teams/components";
|
import { CreateANewTeamForm } from "@calcom/features/ee/teams/components";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { getWizardLayout as getLayout } from "@calcom/ui";
|
|
||||||
|
import { getLayout } from "@components/layouts/WizardLayout";
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import { isSAMLLoginEnabled } from "@calcom/features/ee/sso/lib/saml";
|
|||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||||
import { inferSSRProps } from "@calcom/types/inferSSRProps";
|
import { inferSSRProps } from "@calcom/types/inferSSRProps";
|
||||||
import { Alert, Button, EmailField, PasswordField, TextField } from "@calcom/ui";
|
import { Alert, Button, EmailField, PasswordField, TextField, HeadSeo } from "@calcom/ui";
|
||||||
import { HeadSeo } from "@calcom/web/components/seo/head-seo";
|
|
||||||
import { asStringOrNull } from "@calcom/web/lib/asStringOrNull";
|
import { asStringOrNull } from "@calcom/web/lib/asStringOrNull";
|
||||||
import { WEBAPP_URL } from "@calcom/web/lib/config/constants";
|
import { WEBAPP_URL } from "@calcom/web/lib/config/constants";
|
||||||
import prisma from "@calcom/web/lib/prisma";
|
import prisma from "@calcom/web/lib/prisma";
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||||||
import useTheme from "@calcom/lib/hooks/useTheme";
|
import useTheme from "@calcom/lib/hooks/useTheme";
|
||||||
import { getTeamWithMembers } from "@calcom/lib/server/queries/teams";
|
import { getTeamWithMembers } from "@calcom/lib/server/queries/teams";
|
||||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||||
import { Avatar, Button, Icon } from "@calcom/ui";
|
import { Avatar, Button, Icon, HeadSeo } from "@calcom/ui";
|
||||||
|
|
||||||
import { useToggleQuery } from "@lib/hooks/useToggleQuery";
|
import { useToggleQuery } from "@lib/hooks/useToggleQuery";
|
||||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
|
|
||||||
import { HeadSeo } from "@components/seo/head-seo";
|
|
||||||
import Team from "@components/team/screens/Team";
|
import Team from "@components/team/screens/Team";
|
||||||
import AvatarGroup from "@components/ui/AvatarGroup";
|
import AvatarGroup from "@components/ui/AvatarGroup";
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@ function TeamPage({ team }: TeamPageProps) {
|
|||||||
<Link href={`/team/${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">
|
<a className="flex justify-between px-6 py-4" data-testid="event-type-link">
|
||||||
<div className="flex-shrink">
|
<div className="flex-shrink">
|
||||||
<div className="flex flex-wrap items-center space-x-2">
|
<div className="flex flex-wrap items-center space-x-2 rtl:space-x-reverse">
|
||||||
<h2 className="dark:text-darkgray-700 text-sm font-semibold text-gray-700">{type.title}</h2>
|
<h2 className="dark:text-darkgray-700 text-sm font-semibold text-gray-700">{type.title}</h2>
|
||||||
</div>
|
</div>
|
||||||
<EventTypeDescription className="text-sm" eventType={type} />
|
<EventTypeDescription className="text-sm" eventType={type} />
|
||||||
|
|||||||
@@ -14,8 +14,11 @@ function Teams() {
|
|||||||
heading={t("teams")}
|
heading={t("teams")}
|
||||||
subtitle={t("create_manage_teams_collaborative")}
|
subtitle={t("create_manage_teams_collaborative")}
|
||||||
CTA={
|
CTA={
|
||||||
<Button type="button" href={`${WEBAPP_URL}/settings/teams/new?returnTo=/teams`}>
|
<Button
|
||||||
<Icon.FiPlus className="inline-block h-3.5 w-3.5 text-white group-hover:text-black ltr:mr-2 rtl:ml-2" />
|
size="fab"
|
||||||
|
StartIcon={Icon.FiPlus}
|
||||||
|
type="button"
|
||||||
|
href={`${WEBAPP_URL}/settings/teams/new?returnTo=${WEBAPP_URL}/teams`}>
|
||||||
{t("new")}
|
{t("new")}
|
||||||
</Button>
|
</Button>
|
||||||
}>
|
}>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { getSession } from "next-auth/react";
|
|||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
import { SEO_IMG_OGIMG_VIDEO, WEBSITE_URL } from "@calcom/lib/constants";
|
import { APP_NAME, SEO_IMG_OGIMG_VIDEO, WEBSITE_URL } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||||
import { inferSSRProps } from "@calcom/types/inferSSRProps";
|
import { inferSSRProps } from "@calcom/types/inferSSRProps";
|
||||||
@@ -48,18 +48,18 @@ export default function JoinCall(props: JoinCallPageProps) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Cal.com Video</title>
|
<title>{APP_NAME} Video</title>
|
||||||
<meta name="title" content="Cal.com Video" />
|
<meta name="title" content={APP_NAME + " Video"} />
|
||||||
<meta name="description" content={t("quick_video_meeting")} />
|
<meta name="description" content={t("quick_video_meeting")} />
|
||||||
<meta property="og:image" content={SEO_IMG_OGIMG_VIDEO} />
|
<meta property="og:image" content={SEO_IMG_OGIMG_VIDEO} />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:url" content={`${WEBSITE_URL}/video`} />
|
<meta property="og:url" content={`${WEBSITE_URL}/video`} />
|
||||||
<meta property="og:title" content="Cal.com Video" />
|
<meta property="og:title" content={APP_NAME + " Video"} />
|
||||||
<meta property="og:description" content={t("quick_video_meeting")} />
|
<meta property="og:description" content={t("quick_video_meeting")} />
|
||||||
<meta property="twitter:image" content={SEO_IMG_OGIMG_VIDEO} />
|
<meta property="twitter:image" content={SEO_IMG_OGIMG_VIDEO} />
|
||||||
<meta property="twitter:card" content="summary_large_image" />
|
<meta property="twitter:card" content="summary_large_image" />
|
||||||
<meta property="twitter:url" content={`${WEBSITE_URL}/video`} />
|
<meta property="twitter:url" content={`${WEBSITE_URL}/video`} />
|
||||||
<meta property="twitter:title" content="Cal.com Video" />
|
<meta property="twitter:title" content={APP_NAME + " Video"} />
|
||||||
<meta property="twitter:description" content={t("quick_video_meeting")} />
|
<meta property="twitter:description" content={t("quick_video_meeting")} />
|
||||||
</Head>
|
</Head>
|
||||||
<div style={{ zIndex: 2, position: "relative" }}>
|
<div style={{ zIndex: 2, position: "relative" }}>
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ import dayjs from "@calcom/dayjs";
|
|||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
|
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
|
||||||
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||||
import { Button, Icon } from "@calcom/ui";
|
import { Button, Icon, HeadSeo } from "@calcom/ui";
|
||||||
|
|
||||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
|
|
||||||
import { HeadSeo } from "@components/seo/head-seo";
|
|
||||||
|
|
||||||
export default function MeetingUnavailable(props: inferSSRProps<typeof getServerSideProps>) {
|
export default function MeetingUnavailable(props: inferSSRProps<typeof getServerSideProps>) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||||||
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
|
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
|
||||||
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||||
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
|
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
|
||||||
import { Button, Icon } from "@calcom/ui";
|
import { Button, Icon, HeadSeo } from "@calcom/ui";
|
||||||
|
|
||||||
import { HeadSeo } from "@components/seo/head-seo";
|
|
||||||
|
|
||||||
export default function MeetingNotStarted(props: inferSSRProps<typeof getServerSideProps>) {
|
export default function MeetingNotStarted(props: inferSSRProps<typeof getServerSideProps>) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Button, Icon } from "@calcom/ui";
|
import { Button, Icon, HeadSeo } from "@calcom/ui";
|
||||||
|
|
||||||
import { HeadSeo } from "@components/seo/head-seo";
|
|
||||||
|
|
||||||
export default function NoMeetingFound() {
|
export default function NoMeetingFound() {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|||||||
@@ -457,6 +457,7 @@
|
|||||||
"email_address": "عنوان البريد الإلكتروني",
|
"email_address": "عنوان البريد الإلكتروني",
|
||||||
"enter_valid_email": "يرجى إدخال بريد إلكتروني صالح",
|
"enter_valid_email": "يرجى إدخال بريد إلكتروني صالح",
|
||||||
"location": "الموقع",
|
"location": "الموقع",
|
||||||
|
"in_person_attendee_address": "شخصيًا (عنوان من سيحضر)",
|
||||||
"yes": "نعم",
|
"yes": "نعم",
|
||||||
"no": "لا",
|
"no": "لا",
|
||||||
"additional_notes": "ملاحظات إضافية",
|
"additional_notes": "ملاحظات إضافية",
|
||||||
@@ -1390,7 +1391,7 @@
|
|||||||
"attendee_email_info": "البريد الإلكتروني للشخص الحجز",
|
"attendee_email_info": "البريد الإلكتروني للشخص الحجز",
|
||||||
"kbar_search_placeholder": "اكتب أمرًا أو بحثًا...",
|
"kbar_search_placeholder": "اكتب أمرًا أو بحثًا...",
|
||||||
"invalid_credential": "لا! يبدو أن الصلاحية انتهت أو تم إلغاؤها. يرجى إعادة التثبيت مرة أخرى.",
|
"invalid_credential": "لا! يبدو أن الصلاحية انتهت أو تم إلغاؤها. يرجى إعادة التثبيت مرة أخرى.",
|
||||||
"choose_common_schedule_team_event": "اختر جدولاً زمنياً مشتركاً",
|
"choose_common_schedule_team_event": "اختر جدولًا زمنيًا مشتركًا",
|
||||||
"choose_common_schedule_team_event_description": "قم بتمكين هذا الخيار إذا كنت ترغب في استخدام جدول زمني مشترك بين المضيفين. عند تعطيل هذا الخيار، سيتم حجز كل مضيف على أساس جدوله الافتراضي.",
|
"choose_common_schedule_team_event_description": "قم بتمكين هذا الخيار إذا كنت ترغب في استخدام جدول زمني مشترك بين المضيفين. عند تعطيل هذا الخيار، سيتم حجز كل مضيف على أساس جدوله الافتراضي.",
|
||||||
"reason": "السبب",
|
"reason": "السبب",
|
||||||
"sender_id": "معرّف المرسل",
|
"sender_id": "معرّف المرسل",
|
||||||
|
|||||||
@@ -457,6 +457,7 @@
|
|||||||
"email_address": "E-mailová adresa",
|
"email_address": "E-mailová adresa",
|
||||||
"enter_valid_email": "Zadejte platný e-mail",
|
"enter_valid_email": "Zadejte platný e-mail",
|
||||||
"location": "Místo",
|
"location": "Místo",
|
||||||
|
"in_person_attendee_address": "Osobně (adresa účastníka)",
|
||||||
"yes": "ano",
|
"yes": "ano",
|
||||||
"no": "ne",
|
"no": "ne",
|
||||||
"additional_notes": "Doplňující poznámky",
|
"additional_notes": "Doplňující poznámky",
|
||||||
|
|||||||
@@ -456,6 +456,9 @@
|
|||||||
"email_address": "E-Mail Adresse",
|
"email_address": "E-Mail Adresse",
|
||||||
"enter_valid_email": "Bitte geben Sie eine gültige Emaildresse ein",
|
"enter_valid_email": "Bitte geben Sie eine gültige Emaildresse ein",
|
||||||
"location": "Ort",
|
"location": "Ort",
|
||||||
|
"address": "Adresse",
|
||||||
|
"enter_address": "Geben Sie eine Adresse ein",
|
||||||
|
"in_person_attendee_address": "In Person (Adresse von Ihnen)",
|
||||||
"yes": "Ja",
|
"yes": "Ja",
|
||||||
"no": "Nein",
|
"no": "Nein",
|
||||||
"additional_notes": "Zusätzliche Notizen",
|
"additional_notes": "Zusätzliche Notizen",
|
||||||
|
|||||||
@@ -388,7 +388,7 @@
|
|||||||
"further_billing_help": "If you need any further help with billing, our support team are here to help.",
|
"further_billing_help": "If you need any further help with billing, our support team are here to help.",
|
||||||
"contact": "Contact",
|
"contact": "Contact",
|
||||||
"our_support_team": "our support team",
|
"our_support_team": "our support team",
|
||||||
"contact_our_support_team":"Contact our support team",
|
"contact_our_support_team": "Contact our support team",
|
||||||
"uh_oh": "Uh oh!",
|
"uh_oh": "Uh oh!",
|
||||||
"no_event_types_have_been_setup": "This user hasn't set up any event types yet.",
|
"no_event_types_have_been_setup": "This user hasn't set up any event types yet.",
|
||||||
"edit_logo": "Edit logo",
|
"edit_logo": "Edit logo",
|
||||||
@@ -460,6 +460,9 @@
|
|||||||
"email_address": "Email address",
|
"email_address": "Email address",
|
||||||
"enter_valid_email": "Please enter a valid email",
|
"enter_valid_email": "Please enter a valid email",
|
||||||
"location": "Location",
|
"location": "Location",
|
||||||
|
"address": "Address",
|
||||||
|
"enter_address": "Enter address",
|
||||||
|
"in_person_attendee_address": "In Person (Attendee Address)",
|
||||||
"yes": "yes",
|
"yes": "yes",
|
||||||
"no": "no",
|
"no": "no",
|
||||||
"additional_notes": "Additional notes",
|
"additional_notes": "Additional notes",
|
||||||
@@ -1091,6 +1094,10 @@
|
|||||||
"navigate": "Navigate",
|
"navigate": "Navigate",
|
||||||
"open": "Open",
|
"open": "Open",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
|
"upgrade": "Upgrade",
|
||||||
|
"upgrade_to_access_recordings_title": "Upgrade to access recordings",
|
||||||
|
"upgrade_to_access_recordings_description":"Recordings are only available as part of our teams plan. Upgrade to start recording your calls",
|
||||||
|
"recordings_are_part_of_the_teams_plan":"Recordings are part of the teams plan",
|
||||||
"team_feature_teams": "This is a Team feature. Upgrade to Team to see your team's availability.",
|
"team_feature_teams": "This is a Team feature. Upgrade to Team to see your team's availability.",
|
||||||
"team_feature_workflows": "This is a Team feature. Upgrade to Team to automate your event notifications and reminders with Workflows.",
|
"team_feature_workflows": "This is a Team feature. Upgrade to Team to automate your event notifications and reminders with Workflows.",
|
||||||
"show_eventtype_on_profile": "Show on Profile",
|
"show_eventtype_on_profile": "Show on Profile",
|
||||||
@@ -1148,8 +1155,11 @@
|
|||||||
"confirm_delete_account_modal": "Are you sure you want to delete your {{appName}} account?",
|
"confirm_delete_account_modal": "Are you sure you want to delete your {{appName}} account?",
|
||||||
"delete_my_account": "Delete my account",
|
"delete_my_account": "Delete my account",
|
||||||
"start_of_week": "Start of week",
|
"start_of_week": "Start of week",
|
||||||
|
"recordings_title": "Recordings",
|
||||||
|
"recording": "Recording",
|
||||||
"select_calendars": "Select which calendars you want to check for conflicts to prevent double bookings.",
|
"select_calendars": "Select which calendars you want to check for conflicts to prevent double bookings.",
|
||||||
"check_for_conflicts": "Check for conflicts",
|
"check_for_conflicts": "Check for conflicts",
|
||||||
|
"view_recordings": "View recordings",
|
||||||
"adding_events_to": "Adding events to",
|
"adding_events_to": "Adding events to",
|
||||||
"follow_system_preferences": "Follow system preferences",
|
"follow_system_preferences": "Follow system preferences",
|
||||||
"custom_brand_colors": "Custom brand colors",
|
"custom_brand_colors": "Custom brand colors",
|
||||||
@@ -1221,6 +1231,7 @@
|
|||||||
"to": "To",
|
"to": "To",
|
||||||
"workflow_turned_on_successfully": "{{workflowName}} workflow turned {{offOn}} successfully",
|
"workflow_turned_on_successfully": "{{workflowName}} workflow turned {{offOn}} successfully",
|
||||||
"download_responses": "Download Responses",
|
"download_responses": "Download Responses",
|
||||||
|
"download": "Download",
|
||||||
"create_your_first_form": "Create your first form",
|
"create_your_first_form": "Create your first form",
|
||||||
"create_your_first_form_description": "With Routing Forms you can ask qualifying questions and route to the correct person or event type.",
|
"create_your_first_form_description": "With Routing Forms you can ask qualifying questions and route to the correct person or event type.",
|
||||||
"create_your_first_webhook": "Create your first Webhook",
|
"create_your_first_webhook": "Create your first Webhook",
|
||||||
@@ -1473,5 +1484,9 @@
|
|||||||
"send_code" : "Send code",
|
"send_code" : "Send code",
|
||||||
"number_verified": "Number Verified",
|
"number_verified": "Number Verified",
|
||||||
"create_your_first_team_webhook_description": "Create your first webhook for this team event type",
|
"create_your_first_team_webhook_description": "Create your first webhook for this team event type",
|
||||||
"create_webhook_team_event_type": "Create a webhook for this team event type"
|
"create_webhook_team_event_type": "Create a webhook for this team event type",
|
||||||
|
"disable_success_page":"Disable Success Page (only works if you have a redirect URL)",
|
||||||
|
"invalid_admin_password": "You are set as an admin but you do not have a password length of at least 15 characters",
|
||||||
|
"change_password_admin": "Change Password to gain admin access",
|
||||||
|
"username_already_taken": "Username is already taken"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -457,6 +457,7 @@
|
|||||||
"email_address": "Email",
|
"email_address": "Email",
|
||||||
"enter_valid_email": "Ingresa un correo electrónico válido",
|
"enter_valid_email": "Ingresa un correo electrónico válido",
|
||||||
"location": "Ubicación",
|
"location": "Ubicación",
|
||||||
|
"in_person_attendee_address": "En persona (dirección del asistente)",
|
||||||
"yes": "Sí",
|
"yes": "Sí",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"additional_notes": "Notas Adicionales",
|
"additional_notes": "Notas Adicionales",
|
||||||
|
|||||||
@@ -457,6 +457,7 @@
|
|||||||
"email_address": "Adresse e-mail",
|
"email_address": "Adresse e-mail",
|
||||||
"enter_valid_email": "Veuillez saisir une adresse e-mail valide",
|
"enter_valid_email": "Veuillez saisir une adresse e-mail valide",
|
||||||
"location": "Localisation",
|
"location": "Localisation",
|
||||||
|
"in_person_attendee_address": "En personne (adresse du participant)",
|
||||||
"yes": "oui",
|
"yes": "oui",
|
||||||
"no": "non",
|
"no": "non",
|
||||||
"additional_notes": "Notes additionnelles",
|
"additional_notes": "Notes additionnelles",
|
||||||
|
|||||||
@@ -457,6 +457,7 @@
|
|||||||
"email_address": "כתובת דוא\"ל",
|
"email_address": "כתובת דוא\"ל",
|
||||||
"enter_valid_email": "יש להזין כתובת דוא״ל תקינה",
|
"enter_valid_email": "יש להזין כתובת דוא״ל תקינה",
|
||||||
"location": "מיקום",
|
"location": "מיקום",
|
||||||
|
"in_person_attendee_address": "פנים-אל-פנים (הכתובת של המשתתף/ת)",
|
||||||
"yes": "כן",
|
"yes": "כן",
|
||||||
"no": "לא",
|
"no": "לא",
|
||||||
"additional_notes": "הערות נוספות",
|
"additional_notes": "הערות נוספות",
|
||||||
@@ -611,7 +612,7 @@
|
|||||||
"monthly_other": "חודשים",
|
"monthly_other": "חודשים",
|
||||||
"yearly_one": "שנה",
|
"yearly_one": "שנה",
|
||||||
"yearly_other": "שנים",
|
"yearly_other": "שנים",
|
||||||
"plus_more": "ועוד {{count}}",
|
"plus_more": "{{count}} נוספים",
|
||||||
"max": "מקסימום",
|
"max": "מקסימום",
|
||||||
"single_theme": "ערכת נושא בודדת",
|
"single_theme": "ערכת נושא בודדת",
|
||||||
"brand_color": "צבע המותג",
|
"brand_color": "צבע המותג",
|
||||||
|
|||||||
@@ -457,6 +457,7 @@
|
|||||||
"email_address": "Indirizzo email",
|
"email_address": "Indirizzo email",
|
||||||
"enter_valid_email": "Immettere un indirizzo e-mail valido",
|
"enter_valid_email": "Immettere un indirizzo e-mail valido",
|
||||||
"location": "Luogo",
|
"location": "Luogo",
|
||||||
|
"in_person_attendee_address": "Di persona (indirizzo del partecipante)",
|
||||||
"yes": "sì",
|
"yes": "sì",
|
||||||
"no": "no",
|
"no": "no",
|
||||||
"additional_notes": "Note aggiuntive",
|
"additional_notes": "Note aggiuntive",
|
||||||
|
|||||||
@@ -457,6 +457,7 @@
|
|||||||
"email_address": "メールアドレス",
|
"email_address": "メールアドレス",
|
||||||
"enter_valid_email": "有効なメールアドレスを入力してください",
|
"enter_valid_email": "有効なメールアドレスを入力してください",
|
||||||
"location": "所在地",
|
"location": "所在地",
|
||||||
|
"in_person_attendee_address": "対面(出席者のアドレス)",
|
||||||
"yes": "はい",
|
"yes": "はい",
|
||||||
"no": "いいえ",
|
"no": "いいえ",
|
||||||
"additional_notes": "追加メモ",
|
"additional_notes": "追加メモ",
|
||||||
|
|||||||
@@ -457,6 +457,7 @@
|
|||||||
"email_address": "이메일 주소",
|
"email_address": "이메일 주소",
|
||||||
"enter_valid_email": "유효한 이메일을 입력하세요",
|
"enter_valid_email": "유효한 이메일을 입력하세요",
|
||||||
"location": "위치",
|
"location": "위치",
|
||||||
|
"in_person_attendee_address": "직접 방문(참석자 주소)",
|
||||||
"yes": "네",
|
"yes": "네",
|
||||||
"no": "아니요",
|
"no": "아니요",
|
||||||
"additional_notes": "추가 참고 사항",
|
"additional_notes": "추가 참고 사항",
|
||||||
|
|||||||
@@ -457,6 +457,7 @@
|
|||||||
"email_address": "E-mail adres",
|
"email_address": "E-mail adres",
|
||||||
"enter_valid_email": "Voer een geldig e-mailadres in",
|
"enter_valid_email": "Voer een geldig e-mailadres in",
|
||||||
"location": "Locatie",
|
"location": "Locatie",
|
||||||
|
"in_person_attendee_address": "Persoonlijk (adres deelnemer)",
|
||||||
"yes": "ja",
|
"yes": "ja",
|
||||||
"no": "nee",
|
"no": "nee",
|
||||||
"additional_notes": "Aanvullende notities",
|
"additional_notes": "Aanvullende notities",
|
||||||
|
|||||||
@@ -303,6 +303,7 @@
|
|||||||
"cancelling_all_recurring": "Dette er alle gjenværende forekomster i den gjentakende hendelsen.",
|
"cancelling_all_recurring": "Dette er alle gjenværende forekomster i den gjentakende hendelsen.",
|
||||||
"error_with_status_code_occured": "Det oppstod en feil med statuskoden {{status}}.",
|
"error_with_status_code_occured": "Det oppstod en feil med statuskoden {{status}}.",
|
||||||
"booking_already_cancelled": "Denne bookingen var allerede kansellert",
|
"booking_already_cancelled": "Denne bookingen var allerede kansellert",
|
||||||
|
"booking_already_accepted_rejected": "Denne bookingen var allerede akseptert eller avvist",
|
||||||
"go_back_home": "Gå til hjem",
|
"go_back_home": "Gå til hjem",
|
||||||
"or_go_back_home": "Eller gå tilbake til hjem",
|
"or_go_back_home": "Eller gå tilbake til hjem",
|
||||||
"no_availability": "Utilgjengelig",
|
"no_availability": "Utilgjengelig",
|
||||||
@@ -385,6 +386,8 @@
|
|||||||
"go_to_billing_portal": "Gå til betalingsportalen",
|
"go_to_billing_portal": "Gå til betalingsportalen",
|
||||||
"need_anything_else": "Trenger du noe annet?",
|
"need_anything_else": "Trenger du noe annet?",
|
||||||
"further_billing_help": "Hvis du trenger ytterligere hjelp med betaling, er supportteamet vårt her for å hjelpe.",
|
"further_billing_help": "Hvis du trenger ytterligere hjelp med betaling, er supportteamet vårt her for å hjelpe.",
|
||||||
|
"contact": "Kontakt",
|
||||||
|
"our_support_team": "vårt support-team",
|
||||||
"contact_our_support_team": "Kontakt supportteamet vårt",
|
"contact_our_support_team": "Kontakt supportteamet vårt",
|
||||||
"uh_oh": "Uh oh!",
|
"uh_oh": "Uh oh!",
|
||||||
"no_event_types_have_been_setup": "Denne brukeren har ikke konfigurert noen hendelsestyper ennå.",
|
"no_event_types_have_been_setup": "Denne brukeren har ikke konfigurert noen hendelsestyper ennå.",
|
||||||
@@ -457,6 +460,7 @@
|
|||||||
"email_address": "Epost adresse",
|
"email_address": "Epost adresse",
|
||||||
"enter_valid_email": "Vennligst skriv inn en gyldig e-postadresse",
|
"enter_valid_email": "Vennligst skriv inn en gyldig e-postadresse",
|
||||||
"location": "Sted",
|
"location": "Sted",
|
||||||
|
"in_person_attendee_address": "Personlig Oppmøte (Deltakers Adresse)",
|
||||||
"yes": "ja",
|
"yes": "ja",
|
||||||
"no": "nei",
|
"no": "nei",
|
||||||
"additional_notes": "Tilleggsinformasjon",
|
"additional_notes": "Tilleggsinformasjon",
|
||||||
@@ -629,6 +633,7 @@
|
|||||||
"teams": "Team",
|
"teams": "Team",
|
||||||
"team": "Team",
|
"team": "Team",
|
||||||
"team_billing": "Team Fakturering",
|
"team_billing": "Team Fakturering",
|
||||||
|
"team_billing_description": "Administrer betaling for ditt team",
|
||||||
"upgrade_to_flexible_pro_title": "Vi har endret fakturering for team",
|
"upgrade_to_flexible_pro_title": "Vi har endret fakturering for team",
|
||||||
"upgrade_to_flexible_pro_message": "Det er medlemmer i teamet ditt uten sete. Oppgrader proffplanen din for å dekke manglende seter.",
|
"upgrade_to_flexible_pro_message": "Det er medlemmer i teamet ditt uten sete. Oppgrader proffplanen din for å dekke manglende seter.",
|
||||||
"changed_team_billing_info": "Fra januar 2022 tar vi betalt per sete for teammedlemmer. Medlemmer av teamet ditt som hadde PRO gratis, har nå en 14 dagers prøveversjon. Når prøveperioden deres utløper, vil disse medlemmene bli skjult for teamet ditt med mindre du oppgraderer nå.",
|
"changed_team_billing_info": "Fra januar 2022 tar vi betalt per sete for teammedlemmer. Medlemmer av teamet ditt som hadde PRO gratis, har nå en 14 dagers prøveversjon. Når prøveperioden deres utløper, vil disse medlemmene bli skjult for teamet ditt med mindre du oppgraderer nå.",
|
||||||
@@ -704,6 +709,7 @@
|
|||||||
"hide_event_type": "Skjul hendelsestype",
|
"hide_event_type": "Skjul hendelsestype",
|
||||||
"edit_location": "Rediger sted",
|
"edit_location": "Rediger sted",
|
||||||
"into_the_future": "i fremtiden",
|
"into_the_future": "i fremtiden",
|
||||||
|
"when_booked_with_less_than_notice": "Når booket med mindre enn <time></time> varsel",
|
||||||
"within_date_range": "Innenfor en datoperiode",
|
"within_date_range": "Innenfor en datoperiode",
|
||||||
"indefinitely_into_future": "Uendelig inn i fremtiden",
|
"indefinitely_into_future": "Uendelig inn i fremtiden",
|
||||||
"add_new_custom_input_field": "Legg til nytt egendefinert input felt",
|
"add_new_custom_input_field": "Legg til nytt egendefinert input felt",
|
||||||
@@ -723,6 +729,8 @@
|
|||||||
"delete_account_confirmation_message": "Er du sikker på at du vil slette {{appName}}-kontoen din? Alle du har delt konto-lenken med, vil ikke lenger kunne booke ved å bruke den, og eventuelle innstillinger du har lagret vil gå tapt.",
|
"delete_account_confirmation_message": "Er du sikker på at du vil slette {{appName}}-kontoen din? Alle du har delt konto-lenken med, vil ikke lenger kunne booke ved å bruke den, og eventuelle innstillinger du har lagret vil gå tapt.",
|
||||||
"integrations": "Integrasjoner",
|
"integrations": "Integrasjoner",
|
||||||
"apps": "Apper",
|
"apps": "Apper",
|
||||||
|
"apps_description": "Her finner du en liste over appene dine",
|
||||||
|
"apps_listing": "App liste",
|
||||||
"category_apps": "{{category}} apper",
|
"category_apps": "{{category}} apper",
|
||||||
"app_store": "App Butikken",
|
"app_store": "App Butikken",
|
||||||
"app_store_description": "Kobler sammen folk, teknologi og arbeidsplassen.",
|
"app_store_description": "Kobler sammen folk, teknologi og arbeidsplassen.",
|
||||||
@@ -746,6 +754,7 @@
|
|||||||
"toggle_calendars_conflict": "Huk av kalenderne du vil sjekke for konfliker for å hindre dobbelt-bookinger.",
|
"toggle_calendars_conflict": "Huk av kalenderne du vil sjekke for konfliker for å hindre dobbelt-bookinger.",
|
||||||
"select_destination_calendar": "Opprett hendelser på",
|
"select_destination_calendar": "Opprett hendelser på",
|
||||||
"connect_additional_calendar": "Koble til enda en kalender",
|
"connect_additional_calendar": "Koble til enda en kalender",
|
||||||
|
"calendar_updated_successfully": "Kalenderen er oppdatert",
|
||||||
"conferencing": "Konferansering",
|
"conferencing": "Konferansering",
|
||||||
"calendar": "Kalender",
|
"calendar": "Kalender",
|
||||||
"payments": "Betalinger",
|
"payments": "Betalinger",
|
||||||
@@ -1028,6 +1037,9 @@
|
|||||||
"error_removing_app": "Feil ved fjerning av app",
|
"error_removing_app": "Feil ved fjerning av app",
|
||||||
"web_conference": "Nett-konferanse",
|
"web_conference": "Nett-konferanse",
|
||||||
"requires_confirmation": "Krever bekreftelse",
|
"requires_confirmation": "Krever bekreftelse",
|
||||||
|
"always_requires_confirmation": "Alltid",
|
||||||
|
"requires_confirmation_threshold": "Krever bekreftelse hvis booket med < {{time}} $t({{unit}}_timeUnit) varsel",
|
||||||
|
"may_require_confirmation": "Kan kreve bekreftelse",
|
||||||
"nr_event_type_one": "{{count}} hendelsestype",
|
"nr_event_type_one": "{{count}} hendelsestype",
|
||||||
"nr_event_type_other": "{{count}} hendelsestyper",
|
"nr_event_type_other": "{{count}} hendelsestyper",
|
||||||
"add_action": "Legg til handling",
|
"add_action": "Legg til handling",
|
||||||
@@ -1080,6 +1092,10 @@
|
|||||||
"navigate": "Naviger",
|
"navigate": "Naviger",
|
||||||
"open": "Åpne",
|
"open": "Åpne",
|
||||||
"close": "Lukk",
|
"close": "Lukk",
|
||||||
|
"upgrade": "Oppgrader",
|
||||||
|
"upgrade_to_access_recordings_title": "Oppgrader for å få tilgang til opptak",
|
||||||
|
"upgrade_to_access_recordings_description": "Opptak er kun tilgjengelig som en del av team-planen vår. Oppgrader for å begynne å ta opp samtalene dine",
|
||||||
|
"recordings_are_part_of_the_teams_plan": "Opptak er en del av team-planen",
|
||||||
"team_feature_teams": "Dette er en Team-funksjon. Oppgrader til Team for å se teamets tilgjengelighet.",
|
"team_feature_teams": "Dette er en Team-funksjon. Oppgrader til Team for å se teamets tilgjengelighet.",
|
||||||
"team_feature_workflows": "Dette er en Team-funksjon. Oppgrader til Team for å automatisere hendelsesvarslene og påminnelsene dine med arbeidsflyter.",
|
"team_feature_workflows": "Dette er en Team-funksjon. Oppgrader til Team for å automatisere hendelsesvarslene og påminnelsene dine med arbeidsflyter.",
|
||||||
"show_eventtype_on_profile": "Vis på Profil",
|
"show_eventtype_on_profile": "Vis på Profil",
|
||||||
@@ -1137,8 +1153,11 @@
|
|||||||
"confirm_delete_account_modal": "Er du sikker på at du vil slette {{appName}}-kontoen din?",
|
"confirm_delete_account_modal": "Er du sikker på at du vil slette {{appName}}-kontoen din?",
|
||||||
"delete_my_account": "Slett kontoen min",
|
"delete_my_account": "Slett kontoen min",
|
||||||
"start_of_week": "Begynnelsen av uken",
|
"start_of_week": "Begynnelsen av uken",
|
||||||
|
"recordings_title": "Opptak",
|
||||||
|
"recording": "Opptak",
|
||||||
"select_calendars": "Velg hvilke kalendere du vil sjekke for konflikter for å forhindre dobbeltbookinger.",
|
"select_calendars": "Velg hvilke kalendere du vil sjekke for konflikter for å forhindre dobbeltbookinger.",
|
||||||
"check_for_conflicts": "Se etter konflikter",
|
"check_for_conflicts": "Se etter konflikter",
|
||||||
|
"view_recordings": "Vis opptak",
|
||||||
"adding_events_to": "Legger til hendelser til",
|
"adding_events_to": "Legger til hendelser til",
|
||||||
"follow_system_preferences": "Følg systempreferansene",
|
"follow_system_preferences": "Følg systempreferansene",
|
||||||
"custom_brand_colors": "Egendefinerte merkevare-farger",
|
"custom_brand_colors": "Egendefinerte merkevare-farger",
|
||||||
@@ -1171,8 +1190,11 @@
|
|||||||
"invoices": "Fakturaer",
|
"invoices": "Fakturaer",
|
||||||
"embeds": "Innbygginger",
|
"embeds": "Innbygginger",
|
||||||
"impersonation": "Etterligning",
|
"impersonation": "Etterligning",
|
||||||
|
"impersonation_description": "Innstillinger for å administrere etterligning av brukere",
|
||||||
"users": "Brukere",
|
"users": "Brukere",
|
||||||
"profile_description": "Administrer innstillinger for {{appName}} profilen din",
|
"profile_description": "Administrer innstillinger for {{appName}} profilen din",
|
||||||
|
"users_description": "Her finner du en liste over alle brukere",
|
||||||
|
"users_listing": "Bruker liste",
|
||||||
"general_description": "Administrer innstillinger for språk og tidssone",
|
"general_description": "Administrer innstillinger for språk og tidssone",
|
||||||
"calendars_description": "Konfigurer hvordan hendelsestypene dine samhandler med kalenderne dine",
|
"calendars_description": "Konfigurer hvordan hendelsestypene dine samhandler med kalenderne dine",
|
||||||
"appearance_description": "Administrer innstillinger for booking-utseendet",
|
"appearance_description": "Administrer innstillinger for booking-utseendet",
|
||||||
@@ -1207,6 +1229,7 @@
|
|||||||
"to": "Til",
|
"to": "Til",
|
||||||
"workflow_turned_on_successfully": "Arbeidsflyten for {{workflowName}} ble {{offOn}} slått av",
|
"workflow_turned_on_successfully": "Arbeidsflyten for {{workflowName}} ble {{offOn}} slått av",
|
||||||
"download_responses": "Last ned Respons",
|
"download_responses": "Last ned Respons",
|
||||||
|
"download": "Nedlasting",
|
||||||
"create_your_first_form": "Lag ditt første skjema",
|
"create_your_first_form": "Lag ditt første skjema",
|
||||||
"create_your_first_form_description": "Med Viderekoblings-skjema kan du stille kvalifiserende spørsmål og videresende til riktig person eller hendelsestype.",
|
"create_your_first_form_description": "Med Viderekoblings-skjema kan du stille kvalifiserende spørsmål og videresende til riktig person eller hendelsestype.",
|
||||||
"create_your_first_webhook": "Lag din første Webhook",
|
"create_your_first_webhook": "Lag din første Webhook",
|
||||||
@@ -1315,6 +1338,8 @@
|
|||||||
"booking_confirmation_success": "Booking-bekreftelsen var vellykket",
|
"booking_confirmation_success": "Booking-bekreftelsen var vellykket",
|
||||||
"booking_rejection_success": "Booking-avvisningen var vellykket",
|
"booking_rejection_success": "Booking-avvisningen var vellykket",
|
||||||
"booking_confirmation_fail": "Booking-bekreftelse feilet",
|
"booking_confirmation_fail": "Booking-bekreftelse feilet",
|
||||||
|
"booking_tentative": "Denne bookingen er foreløpig",
|
||||||
|
"booking_accept_intent": "Oops, jeg vil akseptere",
|
||||||
"we_wont_show_again": "Vi vil ikke vise dette igjen",
|
"we_wont_show_again": "Vi vil ikke vise dette igjen",
|
||||||
"couldnt_update_timezone": "Vi kunne ikke oppdatere tidssonen",
|
"couldnt_update_timezone": "Vi kunne ikke oppdatere tidssonen",
|
||||||
"updated_timezone_to": "Oppdaterte tidssone til {{formattedCurrentTz}}",
|
"updated_timezone_to": "Oppdaterte tidssone til {{formattedCurrentTz}}",
|
||||||
@@ -1377,7 +1402,9 @@
|
|||||||
"number_sms_notifications": "Telefonnummer (SMS varsler)",
|
"number_sms_notifications": "Telefonnummer (SMS varsler)",
|
||||||
"attendee_email_workflow": "Deltakerens e-post",
|
"attendee_email_workflow": "Deltakerens e-post",
|
||||||
"attendee_email_info": "Den som booker sin e-post",
|
"attendee_email_info": "Den som booker sin e-post",
|
||||||
|
"kbar_search_placeholder": "Skriv inn en kommando eller søk...",
|
||||||
"invalid_credential": "Å nei! Det ser ut til at tillatelsen er utløpt eller tilbakekalt. Vennligst installer på nytt.",
|
"invalid_credential": "Å nei! Det ser ut til at tillatelsen er utløpt eller tilbakekalt. Vennligst installer på nytt.",
|
||||||
|
"reschedule_reason_success_page": "Årsak for endring av tidspunkt",
|
||||||
"choose_common_schedule_team_event": "Velg en felles timeplan",
|
"choose_common_schedule_team_event": "Velg en felles timeplan",
|
||||||
"choose_common_schedule_team_event_description": "Aktiver dette hvis du vil bruke en felles tidsplan mellom verter. Når den er deaktivert, vil hver vert bookes basert på deres standard tidsplan.",
|
"choose_common_schedule_team_event_description": "Aktiver dette hvis du vil bruke en felles tidsplan mellom verter. Når den er deaktivert, vil hver vert bookes basert på deres standard tidsplan.",
|
||||||
"reason": "Årsak",
|
"reason": "Årsak",
|
||||||
@@ -1388,10 +1415,25 @@
|
|||||||
"route_to": "Videresend til",
|
"route_to": "Videresend til",
|
||||||
"test_preview_description": "Test viderekoblings-skjemaet ditt uten å sende noen data",
|
"test_preview_description": "Test viderekoblings-skjemaet ditt uten å sende noen data",
|
||||||
"test_routing": "Test Viderekobling",
|
"test_routing": "Test Viderekobling",
|
||||||
|
"payment_app_disabled": "En administrator har deaktivert en betalingsapp",
|
||||||
|
"edit_event_type": "Rediger hendelsestype",
|
||||||
|
"collective_scheduling": "Kollektiv planlegging av tidspunkt",
|
||||||
|
"make_it_easy_to_book": "Gjør det enkelt å booke teamet ditt når alle er tilgjengelige.",
|
||||||
|
"find_the_best_person": "Finn den beste personen tilgjengelig og bla gjennom teamet ditt.",
|
||||||
|
"fixed_round_robin": "Fast round robin",
|
||||||
|
"add_one_fixed_attendee": "Legg til én fast deltaker og round robin gjennom et antall deltakere.",
|
||||||
|
"calcom_is_better_with_team": "Cal.com er bedre med team",
|
||||||
|
"add_your_team_members": "Legg til team-medlemmene dine i hendelsestypene dine. Bruk kollektiv planlegging av tidspunkt for å inkludere alle eller finn den mest passende personen med round robin-planlegging.",
|
||||||
"booking_limit_reached": "Booking-grensen for denne hendelsestypen er nådd",
|
"booking_limit_reached": "Booking-grensen for denne hendelsestypen er nådd",
|
||||||
|
"admin_has_disabled": "En administrator har deaktivert {{appName}}",
|
||||||
"fill_this_field": "Vennligst fyll ut dette feltet",
|
"fill_this_field": "Vennligst fyll ut dette feltet",
|
||||||
"options": "Alternativer",
|
"options": "Alternativer",
|
||||||
"enter_option": "Angi alternativ {{index}}",
|
"enter_option": "Angi alternativ {{index}}",
|
||||||
"add_an_option": "Legg til et alternativ",
|
"add_an_option": "Legg til et alternativ",
|
||||||
"radio": "Radio"
|
"radio": "Radio",
|
||||||
|
"create_your_first_team_webhook_description": "Opprett din første webhook for denne team-hendelsestypen",
|
||||||
|
"create_webhook_team_event_type": "Opprett en webhook for denne team-hendelsestypen",
|
||||||
|
"disable_success_page": "Deaktiver Suksess-side (fungerer bare hvis du har en omdirigerings-URL)",
|
||||||
|
"invalid_admin_password": "Du er satt som administrator, men du har ikke en passordlengde på minst 15 tegn",
|
||||||
|
"change_password_admin": "Endre passord for å få administratortilgang"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -457,6 +457,7 @@
|
|||||||
"email_address": "Adres e-mail",
|
"email_address": "Adres e-mail",
|
||||||
"enter_valid_email": "Wprowadź prawidłowy adres e-mail",
|
"enter_valid_email": "Wprowadź prawidłowy adres e-mail",
|
||||||
"location": "Lokalizacja",
|
"location": "Lokalizacja",
|
||||||
|
"in_person_attendee_address": "Osobiście (adres uczestnika)",
|
||||||
"yes": "tak",
|
"yes": "tak",
|
||||||
"no": "nie",
|
"no": "nie",
|
||||||
"additional_notes": "Dotatkowe uwagi",
|
"additional_notes": "Dotatkowe uwagi",
|
||||||
@@ -1394,7 +1395,7 @@
|
|||||||
"reason": "Powód",
|
"reason": "Powód",
|
||||||
"sender_id": "Identyfikator nadawcy",
|
"sender_id": "Identyfikator nadawcy",
|
||||||
"sender_id_error_message": "Dozwolone są tylko litery, cyfry i spacje (maksymalnie 11 znaków)",
|
"sender_id_error_message": "Dozwolone są tylko litery, cyfry i spacje (maksymalnie 11 znaków)",
|
||||||
"test_routing_form": "Przetestuj formularz przekierowania",
|
"test_routing_form": "Testowanie formularza przekierowania",
|
||||||
"test_preview": "Przetestuj podgląd",
|
"test_preview": "Przetestuj podgląd",
|
||||||
"route_to": "Przekieruj do",
|
"route_to": "Przekieruj do",
|
||||||
"test_preview_description": "Przetestuj swój formularz przekierowania bez przesyłania jakichkolwiek danych",
|
"test_preview_description": "Przetestuj swój formularz przekierowania bez przesyłania jakichkolwiek danych",
|
||||||
|
|||||||
@@ -457,6 +457,7 @@
|
|||||||
"email_address": "Endereço de e-mail",
|
"email_address": "Endereço de e-mail",
|
||||||
"enter_valid_email": "Insira um e-mail válido",
|
"enter_valid_email": "Insira um e-mail válido",
|
||||||
"location": "Local",
|
"location": "Local",
|
||||||
|
"in_person_attendee_address": "Pessoalmente (endereço do participante)",
|
||||||
"yes": "sim",
|
"yes": "sim",
|
||||||
"no": "não",
|
"no": "não",
|
||||||
"additional_notes": "Observações adicionais",
|
"additional_notes": "Observações adicionais",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user