Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
162c6c10a8 | ||
|
|
7d7a1c5bbc | ||
|
|
53a7b1cc37 | ||
|
|
f9f4a5aeb9 | ||
|
|
066fce9d73 | ||
|
|
3a1d8b8183 | ||
|
|
38f23986ad | ||
|
|
27422c351c | ||
|
|
c5ad74f61c | ||
|
|
0eff265682 | ||
|
|
2560b72596 | ||
|
|
7d7c61a1ab | ||
|
|
c92283f03d | ||
|
|
ba9be4e69d | ||
|
|
b334586c3b | ||
|
|
a38c76e279 | ||
|
|
6973d26153 | ||
|
|
e62af90619 | ||
|
|
e72cceff81 | ||
|
|
1c296fb2d5 | ||
|
|
1de50bd093 | ||
|
+3 |
4b6c701c48 | ||
|
|
463aff0702 | ||
|
|
c1cc93c164 | ||
|
|
6148e89425 | ||
|
|
a7f7b29bae | ||
|
|
afafd45223 | ||
|
|
31aff64e42 | ||
|
|
952a149604 | ||
|
|
3aa7387ff5 | ||
|
|
f8007a4f01 | ||
|
|
1fb637d29d | ||
|
|
80954aa866 | ||
|
|
821105dccb | ||
|
|
5a124cec72 | ||
|
|
86a4037088 | ||
|
|
faa2805ccc | ||
|
|
ca3e309ad9 | ||
|
|
fc59338057 | ||
|
|
2d56096d8f | ||
|
|
8545bfef67 | ||
|
|
8a9b7ca78c | ||
|
|
c910b41043 | ||
|
|
d51aade461 | ||
|
|
fa837a5d94 | ||
|
|
1b59dacd64 | ||
|
|
adf817766e | ||
|
|
98d7d7a11c | ||
|
|
4fa380d7ed | ||
|
|
39031123e1 | ||
|
|
b718718bd6 | ||
|
|
0153bc583a | ||
|
|
fbbda20792 | ||
|
|
ef8018d2f7 | ||
|
|
46cd74b0b8 | ||
|
|
db01f7be19 | ||
|
|
25c550de3a | ||
|
|
683e8c4490 | ||
|
|
a0296b8ccb | ||
|
|
6771960e9e | ||
|
|
1c4d2fae56 | ||
|
|
3b1dff3b83 | ||
|
|
84967e5cdf | ||
|
|
6b758eeffa | ||
|
|
8265c65e4c | ||
|
|
676268b62d | ||
|
|
9921c43c3f | ||
|
|
fc13a22162 | ||
|
|
6e2cb4f90d |
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
name: Check types
|
name: Check types
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request_target:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
jobs:
|
jobs:
|
||||||
@@ -19,8 +19,9 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Use Node ${{ matrix.node }}
|
- name: Use Node ${{ matrix.node }}
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- run: yarn
|
cache: "yarn"
|
||||||
|
- run: yarn --frozen-lockfile
|
||||||
- run: yarn type-check
|
- run: yarn type-check
|
||||||
+10
-22
@@ -43,6 +43,8 @@ jobs:
|
|||||||
# MS_GRAPH_CLIENT_SECRET: xxx
|
# MS_GRAPH_CLIENT_SECRET: xxx
|
||||||
# ZOOM_CLIENT_ID: xxx
|
# ZOOM_CLIENT_ID: xxx
|
||||||
# ZOOM_CLIENT_SECRET: xxx
|
# ZOOM_CLIENT_SECRET: xxx
|
||||||
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
|
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:12.1
|
image: postgres:12.1
|
||||||
@@ -60,21 +62,11 @@ jobs:
|
|||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
- name: Use Node ${{ matrix.node }}
|
- name: Use Node ${{ matrix.node }}
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
# cache: "yarn"
|
cache: "yarn"
|
||||||
# cache-dependency-path: yarn.lock
|
|
||||||
|
|
||||||
- name: Turbo Cache
|
|
||||||
id: turbo-cache
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: .turbo
|
|
||||||
key: turbo-${{ github.job }}-${{ github.event.pull_request.head.ref }}-${{ github.event.pull_request.head.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
turbo-${{ github.job }}-${{ github.event.pull_request.head.ref }}-
|
|
||||||
- run: yarn
|
|
||||||
- name: Cache playwright binaries
|
- name: Cache playwright binaries
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: playwright-cache
|
id: playwright-cache
|
||||||
@@ -82,22 +74,18 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
~/Library/Caches/ms-playwright
|
~/Library/Caches/ms-playwright
|
||||||
~/.cache/ms-playwright
|
~/.cache/ms-playwright
|
||||||
**/node_modules/playwright
|
${{ github.workspace }}/node_modules/playwright
|
||||||
key: cache-playwright-${{ hashFiles('**/yarn.lock') }}
|
key: cache-playwright-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: cache-playwright-
|
||||||
|
- run: yarn --frozen-lockfile
|
||||||
- name: Install playwright deps
|
- name: Install playwright deps
|
||||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||||
run: yarn playwright install --with-deps
|
run: yarn playwright install --with-deps
|
||||||
- run: yarn test-e2e
|
- run: yarn test-e2e
|
||||||
|
|
||||||
- name: Upload videos
|
- name: Upload test results
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: videos
|
name: test-results
|
||||||
path: |
|
path: test-results
|
||||||
test-results
|
|
||||||
playwright/screenshots
|
|
||||||
playwright/videos
|
|
||||||
playwright/results
|
|
||||||
playwright/reports
|
|
||||||
@@ -18,16 +18,15 @@ jobs:
|
|||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
- name: Use Node.js 14.x
|
- name: Use Node ${{ matrix.node }}
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
# cache: "yarn"
|
cache: "yarn"
|
||||||
# cache-dependency-path: yarn.lock
|
|
||||||
|
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
if: steps.yarn-cache.outputs.cache-hit != 'true'
|
if: steps.yarn-cache.outputs.cache-hit != 'true'
|
||||||
run: yarn
|
run: yarn --frozen-lockfile
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: yarn lint:report
|
run: yarn lint:report
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
[submodule "apps/admin"]
|
[submodule "apps/console"]
|
||||||
path = apps/admin
|
path = apps/console
|
||||||
url = https://github.com/calcom/admin.git
|
url = https://github.com/calcom/console.git
|
||||||
branch = main
|
branch = main
|
||||||
[submodule "apps/api"]
|
[submodule "apps/api"]
|
||||||
path = apps/api
|
path = apps/api
|
||||||
|
|||||||
Vendored
+70
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"presentation": {
|
||||||
|
"echo": false,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"showReuseMessage": true
|
||||||
|
},
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Launch Cal.com Development terminals",
|
||||||
|
"dependsOn": [
|
||||||
|
"Web App(3000)",
|
||||||
|
"Website(3001)",
|
||||||
|
"Embed Core(3100)",
|
||||||
|
"Embed React(3101)",
|
||||||
|
"Prisma Studio(5555)"
|
||||||
|
],
|
||||||
|
// Mark as the default build task so cmd/ctrl+shift+b will create them
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
// Try start the task on folder open
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "folderOpen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// The name that shows up in terminal tab
|
||||||
|
"label": "Web App(3000)",
|
||||||
|
// The task will launch a shell
|
||||||
|
"type": "shell",
|
||||||
|
"command": "yarn dev",
|
||||||
|
// Set the shell type
|
||||||
|
// Mark as a background task to avoid the spinner animation on the terminal tab
|
||||||
|
"isBackground": false,
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Website(3001)",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "cd apps/website && yarn dev",
|
||||||
|
"isBackground": false,
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Embed Core(3100)",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "cd packages/embeds/embed-core && yarn dev",
|
||||||
|
"isBackground": false,
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Embed React(3101)",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "cd packages/embeds/embed-react && yarn dev",
|
||||||
|
"isBackground": false,
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Prisma Studio(5555)",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "yarn db-studio",
|
||||||
|
"isBackground": false,
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -436,4 +436,4 @@ Special thanks to these amazing projects which help power Cal.com:
|
|||||||
|
|
||||||
<a href="https://jitsu.com/?utm_source=cal.com-gihub"><img height="40px" src="https://jitsu.com/img/powered-by-jitsu.png?gh=true" alt="Jitsu.com"></a>
|
<a href="https://jitsu.com/?utm_source=cal.com-gihub"><img height="40px" src="https://jitsu.com/img/powered-by-jitsu.png?gh=true" alt="Jitsu.com"></a>
|
||||||
|
|
||||||
Cal.com is an [open startup](https://jitsu.com) and [Jitsu](https://github.com/jitsucom/jitsu) (an open-source Segment alternative) helps us to track most of the usage metrics.
|
Cal.com is an [open startup](https://cal.com/open) and [Jitsu](https://github.com/jitsucom/jitsu) (an open-source Segment alternative) helps us to track most of the usage metrics.
|
||||||
-1
Submodule apps/admin deleted from 943cd10de1.
+1
-1
Submodule apps/api updated: be2d4338ee...a7889b3436.
Submodule
+1
Submodule apps/console added at 67476f0e24.
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
const withNextra = require("nextra")({
|
const withNextra = require("nextra")({
|
||||||
theme: "nextra-theme-docs",
|
theme: "nextra-theme-docs",
|
||||||
themeConfig: "./theme.config.js",
|
themeConfig: "./theme.config.js",
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"iframe-resizer-react": "^1.1.0",
|
"iframe-resizer-react": "^1.1.0",
|
||||||
"next": "^12.1.0",
|
"next": "^12.1.6",
|
||||||
"nextra": "^1.1.0",
|
"nextra": "^1.1.0",
|
||||||
"nextra-theme-docs": "^1.2.2",
|
"nextra-theme-docs": "^1.2.2",
|
||||||
"react": "^17.0.2",
|
"react": "^18.1.0",
|
||||||
"react-dom": "^17.0.2"
|
"react-dom": "^18.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@calcom/config": "*",
|
"@calcom/config": "*",
|
||||||
"eslint": "^8.10.0"
|
"eslint": "^8.15.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-3623
File diff suppressed because it is too large.
Load diff
@@ -10,16 +10,16 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"highlight.js": "^11.5.1",
|
"highlight.js": "^11.5.1",
|
||||||
"isarray": "2.0.5",
|
"isarray": "2.0.5",
|
||||||
"next": "12.1.5",
|
"next": "12.1.6",
|
||||||
"openapi-snippet": "^0.13.0",
|
"openapi-snippet": "^0.13.0",
|
||||||
"react": "17.0.2",
|
"react": "18.1.0",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "18.1.0",
|
||||||
"swagger-ui-react": "4.10.3"
|
"swagger-ui-react": "4.11.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "17.0.27",
|
"@types/node": "14.17.6",
|
||||||
"@types/react": "17.0.43",
|
"@types/react": "18.0.9",
|
||||||
"@types/react-dom": "17.0.14",
|
"@types/react-dom": "18.0.4",
|
||||||
"typescript": "4.6.3"
|
"typescript": "4.6.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -1,18 +1,18 @@
|
|||||||
import { CodeIcon, EyeIcon, SunIcon, ChevronRightIcon, ArrowLeftIcon } from "@heroicons/react/solid";
|
import { ArrowLeftIcon, ChevronRightIcon, CodeIcon, EyeIcon, SunIcon } from "@heroicons/react/solid";
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { components, ControlProps, SingleValue } from "react-select";
|
import { components, ControlProps } from "react-select";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import showToast from "@calcom/lib/notification";
|
import showToast from "@calcom/lib/notification";
|
||||||
import { EventType } from "@calcom/prisma/client";
|
import { EventType } from "@calcom/prisma/client";
|
||||||
import { Button, Switch } from "@calcom/ui";
|
import { Button, Switch } from "@calcom/ui";
|
||||||
import { Dialog, DialogContent, DialogClose } from "@calcom/ui/Dialog";
|
import { Dialog, DialogClose, DialogContent } from "@calcom/ui/Dialog";
|
||||||
import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields";
|
import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields";
|
||||||
|
|
||||||
import { WEBAPP_URL, EMBED_LIB_URL } from "@lib/config/constants";
|
import { EMBED_LIB_URL, WEBAPP_URL } from "@lib/config/constants";
|
||||||
import { trpc } from "@lib/trpc";
|
import { trpc } from "@lib/trpc";
|
||||||
|
|
||||||
import NavTabs from "@components/NavTabs";
|
import NavTabs from "@components/NavTabs";
|
||||||
@@ -241,7 +241,10 @@ const EmbedNavBar = () => {
|
|||||||
|
|
||||||
return <NavTabs data-testid="embed-tabs" tabs={tabs} linkProps={{ shallow: true }} />;
|
return <NavTabs data-testid="embed-tabs" tabs={tabs} linkProps={{ shallow: true }} />;
|
||||||
};
|
};
|
||||||
const ThemeSelectControl = ({ children, ...props }: ControlProps<any, false>) => {
|
const ThemeSelectControl = ({
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: ControlProps<{ value: string; label: string }, false>) => {
|
||||||
return (
|
return (
|
||||||
<components.Control {...props}>
|
<components.Control {...props}>
|
||||||
<SunIcon className="h-[32px] w-[32px] text-gray-500" />
|
<SunIcon className="h-[32px] w-[32px] text-gray-500" />
|
||||||
@@ -381,7 +384,7 @@ Cal("inline", {
|
|||||||
});
|
});
|
||||||
${getEmbedUIInstructionString().trim()}`;
|
${getEmbedUIInstructionString().trim()}`;
|
||||||
} else if (embedType === "floating-popup") {
|
} else if (embedType === "floating-popup") {
|
||||||
let floatingButtonArg = {
|
const floatingButtonArg = {
|
||||||
calLink,
|
calLink,
|
||||||
...previewState.floatingPopup,
|
...previewState.floatingPopup,
|
||||||
};
|
};
|
||||||
@@ -418,7 +421,7 @@ ${getEmbedUIInstructionString().trim()}`;
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const previewInstruction = (instruction: { name: string; arg: any }) => {
|
const previewInstruction = (instruction: { name: string; arg: unknown }) => {
|
||||||
iframeRef.current?.contentWindow?.postMessage(
|
iframeRef.current?.contentWindow?.postMessage(
|
||||||
{
|
{
|
||||||
mode: "cal:preview",
|
mode: "cal:preview",
|
||||||
@@ -544,7 +547,7 @@ ${getEmbedUIInstructionString().trim()}`;
|
|||||||
value={previewState.inline.width}
|
value={previewState.inline.width}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setPreviewState((previewState) => {
|
setPreviewState((previewState) => {
|
||||||
let width = e.target.value || "100%";
|
const width = e.target.value || "100%";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...previewState,
|
...previewState,
|
||||||
@@ -759,11 +762,11 @@ ${getEmbedUIInstructionString().trim()}`;
|
|||||||
<ColorPicker
|
<ColorPicker
|
||||||
defaultValue="#000000"
|
defaultValue="#000000"
|
||||||
onChange={(color) => {
|
onChange={(color) => {
|
||||||
//@ts-ignore - How to support dynamic palette names?
|
|
||||||
addToPalette({
|
addToPalette({
|
||||||
[palette.name]: color,
|
[palette.name as keyof typeof previewState["palette"]]: color,
|
||||||
});
|
});
|
||||||
}}></ColorPicker>
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Label>
|
</Label>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { FormEvent, useCallback, useEffect, useState } from "react";
|
|||||||
import Cropper from "react-easy-crop";
|
import Cropper from "react-easy-crop";
|
||||||
|
|
||||||
import Button from "@calcom/ui/Button";
|
import Button from "@calcom/ui/Button";
|
||||||
import { DialogClose, DialogTrigger, Dialog, DialogContent } from "@calcom/ui/Dialog";
|
import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui/Dialog";
|
||||||
|
|
||||||
import { Area, getCroppedImg } from "@lib/cropImage";
|
import { Area, getCroppedImg } from "@lib/cropImage";
|
||||||
import { useFileReader } from "@lib/hooks/useFileReader";
|
import { useFileReader } from "@lib/hooks/useFileReader";
|
||||||
@@ -71,15 +71,15 @@ export default function ImageUploader({
|
|||||||
...props
|
...props
|
||||||
}: ImageUploaderProps) {
|
}: ImageUploaderProps) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const [imageSrc, setImageSrc] = useState<string | null>();
|
const [imageSrc, setImageSrc] = useState<string | null>(null);
|
||||||
const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>();
|
const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);
|
||||||
|
|
||||||
const [{ result }, setFile] = useFileReader({
|
const [{ result }, setFile] = useFileReader({
|
||||||
method: "readAsDataURL",
|
method: "readAsDataURL",
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setImageSrc(props.imageSrc);
|
if (props.imageSrc) setImageSrc(props.imageSrc);
|
||||||
}, [props.imageSrc]);
|
}, [props.imageSrc]);
|
||||||
|
|
||||||
const onInputFile = (e: FileEvent<HTMLInputElement>) => {
|
const onInputFile = (e: FileEvent<HTMLInputElement>) => {
|
||||||
@@ -90,8 +90,9 @@ export default function ImageUploader({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const showCroppedImage = useCallback(
|
const showCroppedImage = useCallback(
|
||||||
async (croppedAreaPixels) => {
|
async (croppedAreaPixels: Area | null) => {
|
||||||
try {
|
try {
|
||||||
|
if (!croppedAreaPixels) return;
|
||||||
const croppedImage = await getCroppedImg(
|
const croppedImage = await getCroppedImg(
|
||||||
result as string /* result is always string when using readAsDataUrl */,
|
result as string /* result is always string when using readAsDataUrl */,
|
||||||
croppedAreaPixels
|
croppedAreaPixels
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { AdminRequired } from "components/ui/AdminRequired";
|
import { AdminRequired } from "components/ui/AdminRequired";
|
||||||
|
import noop from "lodash/noop";
|
||||||
import Link, { LinkProps } from "next/link";
|
import Link, { LinkProps } from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React, { ElementType, FC, Fragment, MouseEventHandler } from "react";
|
import { FC, Fragment, MouseEventHandler } from "react";
|
||||||
|
|
||||||
import classNames from "@lib/classNames";
|
import classNames from "@lib/classNames";
|
||||||
|
import { SVGComponent } from "@lib/types/SVGComponent";
|
||||||
|
|
||||||
export interface NavTabProps {
|
export interface NavTabProps {
|
||||||
tabs: {
|
tabs: {
|
||||||
@@ -12,7 +14,7 @@ export interface NavTabProps {
|
|||||||
href?: string;
|
href?: string;
|
||||||
/** If you want to change query param tabName as per current tab */
|
/** If you want to change query param tabName as per current tab */
|
||||||
tabName?: string;
|
tabName?: string;
|
||||||
icon?: ElementType;
|
icon?: SVGComponent;
|
||||||
adminRequired?: boolean;
|
adminRequired?: boolean;
|
||||||
}[];
|
}[];
|
||||||
linkProps?: Omit<LinkProps, "href">;
|
linkProps?: Omit<LinkProps, "href">;
|
||||||
@@ -27,11 +29,11 @@ const NavTabs: FC<NavTabProps> = ({ tabs, linkProps, ...props }) => {
|
|||||||
aria-label="Tabs"
|
aria-label="Tabs"
|
||||||
{...props}>
|
{...props}>
|
||||||
{tabs.map((tab) => {
|
{tabs.map((tab) => {
|
||||||
let href: string;
|
|
||||||
let isCurrent;
|
|
||||||
if ((tab.tabName && tab.href) || (!tab.tabName && !tab.href)) {
|
if ((tab.tabName && tab.href) || (!tab.tabName && !tab.href)) {
|
||||||
throw new Error("Use either tabName or href");
|
throw new Error("Use either tabName or href");
|
||||||
}
|
}
|
||||||
|
let href = "";
|
||||||
|
let isCurrent;
|
||||||
if (tab.href) {
|
if (tab.href) {
|
||||||
href = tab.href;
|
href = tab.href;
|
||||||
isCurrent = router.asPath === tab.href;
|
isCurrent = router.asPath === tab.href;
|
||||||
@@ -39,6 +41,7 @@ const NavTabs: FC<NavTabProps> = ({ tabs, linkProps, ...props }) => {
|
|||||||
href = "";
|
href = "";
|
||||||
isCurrent = router.query.tabName === tab.tabName;
|
isCurrent = router.query.tabName === tab.tabName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClick: MouseEventHandler = tab.tabName
|
const onClick: MouseEventHandler = tab.tabName
|
||||||
? (e) => {
|
? (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -49,12 +52,13 @@ const NavTabs: FC<NavTabProps> = ({ tabs, linkProps, ...props }) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
: () => {};
|
: noop;
|
||||||
|
|
||||||
const Component = tab.adminRequired ? AdminRequired : Fragment;
|
const Component = tab.adminRequired ? AdminRequired : Fragment;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Component key={tab.name}>
|
<Component key={tab.name}>
|
||||||
<Link key={tab.name} href={href!} {...linkProps}>
|
<Link key={tab.name} href={href} {...linkProps}>
|
||||||
<a
|
<a
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React from "react";
|
|||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|
||||||
import NavTabs, { NavTabProps } from "./NavTabs";
|
import NavTabs from "./NavTabs";
|
||||||
|
|
||||||
export default function SettingsShell({ children }: { children: React.ReactNode }) {
|
export default function SettingsShell({ children }: { children: React.ReactNode }) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ const Layout = ({
|
|||||||
<Fragment key={item.name}>
|
<Fragment key={item.name}>
|
||||||
<Link href={item.href}>
|
<Link href={item.href}>
|
||||||
<a
|
<a
|
||||||
|
aria-label={item.name}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
item.current
|
item.current
|
||||||
? "bg-neutral-100 text-neutral-900"
|
? "bg-neutral-100 text-neutral-900"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { StarIcon } from "@heroicons/react/solid";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
import Button from "@calcom/ui/Button";
|
import Button from "@calcom/ui/Button";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { SkeletonAvatar, SkeletonText } from "@calcom/ui";
|
import { SkeletonText } from "@calcom/ui";
|
||||||
|
|
||||||
import { ShellSubHeading } from "@components/Shell";
|
import { ShellSubHeading } from "@components/Shell";
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import "@glidejs/glide/dist/css/glide.theme.min.css";
|
|||||||
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/solid";
|
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/solid";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
const Slider = <T extends unknown>({
|
const Slider = <T extends string | unknown>({
|
||||||
title = "",
|
title = "",
|
||||||
className = "",
|
className = "",
|
||||||
items,
|
items,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||||||
import showToast from "@calcom/lib/notification";
|
import showToast from "@calcom/lib/notification";
|
||||||
import { Button } from "@calcom/ui";
|
import { Button } from "@calcom/ui";
|
||||||
import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui/Dialog";
|
import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui/Dialog";
|
||||||
import { Form, TextField } from "@calcom/ui/form/fields";
|
import { Form } from "@calcom/ui/form/fields";
|
||||||
|
|
||||||
import { HttpError } from "@lib/core/http/error";
|
import { HttpError } from "@lib/core/http/error";
|
||||||
import { trpc } from "@lib/trpc";
|
import { trpc } from "@lib/trpc";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import timezone from "dayjs/plugin/timezone";
|
|||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
|
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
|
||||||
import { GroupBase, Props, SingleValue } from "react-select";
|
import { GroupBase, Props } from "react-select";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import Button from "@calcom/ui/Button";
|
import Button from "@calcom/ui/Button";
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export function ScheduleListItem({
|
|||||||
isDeleting = false,
|
isDeleting = false,
|
||||||
}: {
|
}: {
|
||||||
schedule: inferQueryOutput<"viewer.availability.list">["schedules"][number];
|
schedule: inferQueryOutput<"viewer.availability.list">["schedules"][number];
|
||||||
deleteFunction: Function;
|
deleteFunction: ({ scheduleId }: { scheduleId: number }) => void;
|
||||||
isDeleting: boolean;
|
isDeleting: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { t, i18n } = useLocale();
|
const { t, i18n } = useLocale();
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
import { BanIcon, CheckIcon, ClockIcon, XIcon, PencilAltIcon } from "@heroicons/react/outline";
|
import {
|
||||||
import { PaperAirplaneIcon } from "@heroicons/react/outline";
|
BanIcon,
|
||||||
|
CheckIcon,
|
||||||
|
ClockIcon,
|
||||||
|
PaperAirplaneIcon,
|
||||||
|
PencilAltIcon,
|
||||||
|
XIcon,
|
||||||
|
} from "@heroicons/react/outline";
|
||||||
import { RefreshIcon } from "@heroicons/react/solid";
|
import { RefreshIcon } from "@heroicons/react/solid";
|
||||||
import { BookingStatus } from "@prisma/client";
|
import { BookingStatus } from "@prisma/client";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useMutation } from "react-query";
|
import { useMutation } from "react-query";
|
||||||
import { Frequency as RRuleFrequency } from "rrule";
|
import { Frequency as RRuleFrequency } from "rrule";
|
||||||
@@ -17,7 +24,7 @@ import { TextArea } from "@calcom/ui/form/fields";
|
|||||||
import { HttpError } from "@lib/core/http/error";
|
import { HttpError } from "@lib/core/http/error";
|
||||||
import useMeQuery from "@lib/hooks/useMeQuery";
|
import useMeQuery from "@lib/hooks/useMeQuery";
|
||||||
import { parseRecurringDates } from "@lib/parseDate";
|
import { parseRecurringDates } from "@lib/parseDate";
|
||||||
import { inferQueryOutput, trpc, inferQueryInput } from "@lib/trpc";
|
import { inferQueryInput, inferQueryOutput, trpc } from "@lib/trpc";
|
||||||
|
|
||||||
import { RescheduleDialog } from "@components/dialog/RescheduleDialog";
|
import { RescheduleDialog } from "@components/dialog/RescheduleDialog";
|
||||||
import TableActions, { ActionType } from "@components/ui/TableActions";
|
import TableActions, { ActionType } from "@components/ui/TableActions";
|
||||||
@@ -37,6 +44,7 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
const user = query.data;
|
const user = query.data;
|
||||||
const { t, i18n } = useLocale();
|
const { t, i18n } = useLocale();
|
||||||
const utils = trpc.useContext();
|
const utils = trpc.useContext();
|
||||||
|
const router = useRouter();
|
||||||
const [rejectionReason, setRejectionReason] = useState<string>("");
|
const [rejectionReason, setRejectionReason] = useState<string>("");
|
||||||
const [rejectionDialogIsOpen, setRejectionDialogIsOpen] = useState(false);
|
const [rejectionDialogIsOpen, setRejectionDialogIsOpen] = useState(false);
|
||||||
const mutation = useMutation(
|
const mutation = useMutation(
|
||||||
@@ -81,7 +89,10 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
booking.listingStatus === "upcoming" && booking.recurringEventId !== null
|
booking.listingStatus === "upcoming" && booking.recurringEventId !== null
|
||||||
? t("reject_all")
|
? t("reject_all")
|
||||||
: t("reject"),
|
: t("reject"),
|
||||||
onClick: () => setRejectionDialogIsOpen(true),
|
onClick: (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setRejectionDialogIsOpen(true);
|
||||||
|
},
|
||||||
icon: BanIcon,
|
icon: BanIcon,
|
||||||
disabled: mutation.isLoading,
|
disabled: mutation.isLoading,
|
||||||
},
|
},
|
||||||
@@ -91,7 +102,10 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
booking.listingStatus === "upcoming" && booking.recurringEventId !== null
|
booking.listingStatus === "upcoming" && booking.recurringEventId !== null
|
||||||
? t("confirm_all")
|
? t("confirm_all")
|
||||||
: t("confirm"),
|
: t("confirm"),
|
||||||
onClick: () => mutation.mutate(true),
|
onClick: (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
mutation.mutate(true);
|
||||||
|
},
|
||||||
icon: CheckIcon,
|
icon: CheckIcon,
|
||||||
disabled: mutation.isLoading,
|
disabled: mutation.isLoading,
|
||||||
color: "primary",
|
color: "primary",
|
||||||
@@ -120,7 +134,10 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
id: "reschedule_request",
|
id: "reschedule_request",
|
||||||
icon: ClockIcon,
|
icon: ClockIcon,
|
||||||
label: t("send_reschedule_request"),
|
label: t("send_reschedule_request"),
|
||||||
onClick: () => setIsOpenRescheduleDialog(true),
|
onClick: (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsOpenRescheduleDialog(true);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -150,6 +167,7 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
i18n
|
i18n
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RescheduleDialog
|
<RescheduleDialog
|
||||||
@@ -191,7 +209,30 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<tr className="flex">
|
<tr
|
||||||
|
className="flex cursor-pointer hover:bg-neutral-50"
|
||||||
|
onClick={() =>
|
||||||
|
router.push({
|
||||||
|
pathname: "/success",
|
||||||
|
query: {
|
||||||
|
date: booking.startTime,
|
||||||
|
type: booking.eventType.id,
|
||||||
|
eventSlug: booking.eventType.slug,
|
||||||
|
user: user?.username || "",
|
||||||
|
name: booking.attendees[0].name,
|
||||||
|
email: booking.attendees[0].email,
|
||||||
|
location: booking.location
|
||||||
|
? booking.location.includes("integration")
|
||||||
|
? (t("web_conferencing_details_to_follow") as string)
|
||||||
|
: booking.location
|
||||||
|
: "",
|
||||||
|
eventName: booking.eventType.eventName || "",
|
||||||
|
bookingId: booking.id,
|
||||||
|
recur: booking.recurringEventId,
|
||||||
|
reschedule: booking.confirmed,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}>
|
||||||
<td className="hidden whitespace-nowrap py-4 align-top ltr:pl-6 rtl:pr-6 sm:table-cell sm:w-56">
|
<td className="hidden whitespace-nowrap py-4 align-top ltr:pl-6 rtl:pr-6 sm:table-cell sm:w-56">
|
||||||
<div className="text-sm leading-6 text-gray-900">{startTime}</div>
|
<div className="text-sm leading-6 text-gray-900">{startTime}</div>
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">
|
||||||
@@ -264,9 +305,12 @@ function BookingListItem(booking: BookingItemProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{booking.attendees.length !== 0 && (
|
{booking.attendees.length !== 0 && (
|
||||||
<div className="text-sm text-gray-900 hover:text-blue-500">
|
<a
|
||||||
<a href={"mailto:" + booking.attendees[0].email}>{booking.attendees[0].email}</a>
|
className="text-sm text-gray-900 hover:text-blue-500"
|
||||||
</div>
|
href={"mailto:" + booking.attendees[0].email}
|
||||||
|
onClick={(e) => e.stopPropagation()}>
|
||||||
|
{booking.attendees[0].email}
|
||||||
|
</a>
|
||||||
)}
|
)}
|
||||||
{isCancelled && booking.rescheduled && (
|
{isCancelled && booking.rescheduled && (
|
||||||
<div className="mt-2 inline-block text-left text-sm md:hidden">
|
<div className="mt-2 inline-block text-left text-sm md:hidden">
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
import { XIcon } from "@heroicons/react/solid";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
import { Button } from "@calcom/ui/Button";
|
||||||
|
|
||||||
|
import useTheme from "@lib/hooks/useTheme";
|
||||||
|
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
booking: {
|
||||||
|
title?: string;
|
||||||
|
uid?: string;
|
||||||
|
};
|
||||||
|
profile: {
|
||||||
|
name: string | null;
|
||||||
|
slug: string | null;
|
||||||
|
};
|
||||||
|
team?: string | null;
|
||||||
|
setIsCancellationMode: (value: boolean) => void;
|
||||||
|
theme: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function CancelBooking(props: Props) {
|
||||||
|
const [cancellationReason, setCancellationReason] = useState<string>("");
|
||||||
|
const { t } = useLocale();
|
||||||
|
const router = useRouter();
|
||||||
|
const { booking, profile, team } = props;
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const telemetry = useTelemetry();
|
||||||
|
const [error, setError] = useState<string | null>(booking ? null : t("booking_already_cancelled"));
|
||||||
|
const { isReady, Theme } = useTheme(props.theme);
|
||||||
|
|
||||||
|
if (isReady) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Theme />
|
||||||
|
{error && (
|
||||||
|
<div>
|
||||||
|
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
|
||||||
|
<XIcon className="h-6 w-6 text-red-600" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 text-center sm:mt-5">
|
||||||
|
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
|
||||||
|
{error}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!error && (
|
||||||
|
<div className="mt-5 sm:mt-6">
|
||||||
|
<label className="text-bookingdark font-medium dark:text-white">{t("cancellation_reason")}</label>
|
||||||
|
<textarea
|
||||||
|
placeholder={t("cancellation_reason_placeholder")}
|
||||||
|
value={cancellationReason}
|
||||||
|
onChange={(e) => setCancellationReason(e.target.value)}
|
||||||
|
className="mt-2 mb-3 w-full dark:border-gray-900 dark:bg-gray-700 dark:text-white sm:mb-3 "
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
<div className="flex rtl:space-x-reverse">
|
||||||
|
<div className="w-full">
|
||||||
|
<Button color="secondary" onClick={() => router.push("/reschedule/" + booking?.uid)}>
|
||||||
|
{t("reschedule_this")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="w-full space-x-2 text-right">
|
||||||
|
<Button color="secondary" onClick={() => props.setIsCancellationMode(false)}>
|
||||||
|
{t("nevermind")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
data-testid="cancel"
|
||||||
|
onClick={async () => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
uid: booking?.uid,
|
||||||
|
reason: cancellationReason,
|
||||||
|
};
|
||||||
|
|
||||||
|
telemetry.withJitsu((jitsu) =>
|
||||||
|
jitsu.track(telemetryEventTypes.bookingCancelled, collectPageParameters())
|
||||||
|
);
|
||||||
|
|
||||||
|
const res = await fetch("/api/cancel", {
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
method: "DELETE",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status >= 200 && res.status < 300) {
|
||||||
|
await router.push(
|
||||||
|
`/cancel/success?name=${props.profile.name}&title=${booking?.title}&eventPage=${
|
||||||
|
profile.slug
|
||||||
|
}&team=${team ? 1 : 0}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setLoading(false);
|
||||||
|
setError(
|
||||||
|
`${t("error_with_status_code_occured", { status: res.status })} ${t(
|
||||||
|
"please_try_again"
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
loading={loading}>
|
||||||
|
{t("cancel_event")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/solid";
|
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/solid";
|
||||||
import { EventType, PeriodType } from "@prisma/client";
|
import { PeriodType } from "@prisma/client";
|
||||||
import dayjs, { Dayjs } from "dayjs";
|
import dayjs, { Dayjs } from "dayjs";
|
||||||
import dayjsBusinessTime from "dayjs-business-time";
|
import dayjsBusinessTime from "dayjs-business-days2";
|
||||||
import timezone from "dayjs/plugin/timezone";
|
import timezone from "dayjs/plugin/timezone";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
import { memoize } from "lodash";
|
import { memoize } from "lodash";
|
||||||
@@ -14,6 +14,7 @@ import classNames from "@lib/classNames";
|
|||||||
import { timeZone } from "@lib/clock";
|
import { timeZone } from "@lib/clock";
|
||||||
import { weekdayNames } from "@lib/core/i18n/weekday";
|
import { weekdayNames } from "@lib/core/i18n/weekday";
|
||||||
import { doWorkAsync } from "@lib/doWorkAsync";
|
import { doWorkAsync } from "@lib/doWorkAsync";
|
||||||
|
import isOutOfBounds from "@lib/isOutOfBounds";
|
||||||
import getSlots from "@lib/slots";
|
import getSlots from "@lib/slots";
|
||||||
import { WorkingHours } from "@lib/types/schedule";
|
import { WorkingHours } from "@lib/types/schedule";
|
||||||
|
|
||||||
@@ -37,41 +38,6 @@ type DatePickerProps = {
|
|||||||
minimumBookingNotice: number;
|
minimumBookingNotice: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
function isOutOfBounds(
|
|
||||||
time: dayjs.ConfigType,
|
|
||||||
{
|
|
||||||
periodType,
|
|
||||||
periodDays,
|
|
||||||
periodCountCalendarDays,
|
|
||||||
periodStartDate,
|
|
||||||
periodEndDate,
|
|
||||||
}: Pick<
|
|
||||||
EventType,
|
|
||||||
"periodType" | "periodDays" | "periodCountCalendarDays" | "periodStartDate" | "periodEndDate"
|
|
||||||
>
|
|
||||||
) {
|
|
||||||
const date = dayjs(time);
|
|
||||||
|
|
||||||
switch (periodType) {
|
|
||||||
case PeriodType.ROLLING: {
|
|
||||||
const periodRollingEndDay = periodCountCalendarDays
|
|
||||||
? dayjs().utcOffset(date.utcOffset()).add(periodDays!, "days").endOf("day")
|
|
||||||
: dayjs().utcOffset(date.utcOffset()).addBusinessTime(periodDays!, "days").endOf("day");
|
|
||||||
return date.endOf("day").isAfter(periodRollingEndDay);
|
|
||||||
}
|
|
||||||
|
|
||||||
case PeriodType.RANGE: {
|
|
||||||
const periodRangeStartDay = dayjs(periodStartDate).utcOffset(date.utcOffset()).endOf("day");
|
|
||||||
const periodRangeEndDay = dayjs(periodEndDate).utcOffset(date.utcOffset()).endOf("day");
|
|
||||||
return date.endOf("day").isBefore(periodRangeStartDay) || date.endOf("day").isAfter(periodRangeEndDay);
|
|
||||||
}
|
|
||||||
|
|
||||||
case PeriodType.UNLIMITED:
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function DatePicker({
|
function DatePicker({
|
||||||
weekStart,
|
weekStart,
|
||||||
onDatePicked,
|
onDatePicked,
|
||||||
@@ -94,7 +60,7 @@ function DatePicker({
|
|||||||
const [isFirstMonth, setIsFirstMonth] = useState<boolean>(false);
|
const [isFirstMonth, setIsFirstMonth] = useState<boolean>(false);
|
||||||
const [daysFromState, setDays] = useState<
|
const [daysFromState, setDays] = useState<
|
||||||
| {
|
| {
|
||||||
disabled: Boolean;
|
disabled: boolean;
|
||||||
date: number;
|
date: number;
|
||||||
}[]
|
}[]
|
||||||
| null
|
| null
|
||||||
@@ -191,7 +157,7 @@ function DatePicker({
|
|||||||
name: "DatePicker",
|
name: "DatePicker",
|
||||||
length: daysInMonth,
|
length: daysInMonth,
|
||||||
callback: (i: number) => {
|
callback: (i: number) => {
|
||||||
let day = i + 1;
|
const day = i + 1;
|
||||||
days[daysInitialOffset + i] = {
|
days[daysInitialOffset + i] = {
|
||||||
disabled: isDisabledMemoized(day, {
|
disabled: isDisabledMemoized(day, {
|
||||||
browsingDate,
|
browsingDate,
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { FC, useEffect, useState } from "react";
|
import { FC, useEffect, useState } from "react";
|
||||||
import TimezoneSelect, { ITimezoneOption } from "react-timezone-select";
|
|
||||||
|
|
||||||
import Switch from "@calcom/ui/Switch";
|
import Switch from "@calcom/ui/Switch";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
import { useLocale } from "@lib/hooks/useLocale";
|
||||||
|
|
||||||
|
import TimezoneSelect, { ITimezoneOption } from "@components/ui/form/TimezoneSelect";
|
||||||
|
|
||||||
import { is24h, timeZone } from "../../lib/clock";
|
import { is24h, timeZone } from "../../lib/clock";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import {
|
|||||||
useIsEmbed,
|
useIsEmbed,
|
||||||
useIsBackgroundTransparent,
|
useIsBackgroundTransparent,
|
||||||
sdkActionManager,
|
sdkActionManager,
|
||||||
useEmbedType,
|
|
||||||
useEmbedNonStylesConfig,
|
useEmbedNonStylesConfig,
|
||||||
} from "@calcom/embed-core";
|
} from "@calcom/embed-core";
|
||||||
import classNames from "@calcom/lib/classNames";
|
import classNames from "@calcom/lib/classNames";
|
||||||
@@ -68,7 +67,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
|
|||||||
const availabilityDatePickerEmbedStyles = useEmbedStyles("availabilityDatePicker");
|
const availabilityDatePickerEmbedStyles = useEmbedStyles("availabilityDatePicker");
|
||||||
const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left";
|
const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left";
|
||||||
const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed;
|
const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed;
|
||||||
let isBackgroundTransparent = useIsBackgroundTransparent();
|
const isBackgroundTransparent = useIsBackgroundTransparent();
|
||||||
useExposePlanGlobally(plan);
|
useExposePlanGlobally(plan);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (eventType.metadata.smartContractAddress) {
|
if (eventType.metadata.smartContractAddress) {
|
||||||
@@ -213,9 +212,11 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
|
|||||||
truncateAfter={5}
|
truncateAfter={5}
|
||||||
/>
|
/>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<p className="text-sm font-medium text-black dark:text-white">{profile.name}</p>
|
<p className="break-words text-sm font-medium text-black dark:text-white">
|
||||||
|
{profile.name}
|
||||||
|
</p>
|
||||||
<div className="mt-2 gap-2 dark:text-gray-100">
|
<div className="mt-2 gap-2 dark:text-gray-100">
|
||||||
<h1 className="text-bookingdark mb-4 text-xl font-semibold dark:text-white">
|
<h1 className="text-bookingdark mb-4 break-words text-xl font-semibold dark:text-white">
|
||||||
{eventType.title}
|
{eventType.title}
|
||||||
</h1>
|
</h1>
|
||||||
{eventType?.description && (
|
{eventType?.description && (
|
||||||
@@ -264,7 +265,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
|
|||||||
<div className="px-4 sm:flex sm:p-4 sm:py-5">
|
<div className="px-4 sm:flex sm:p-4 sm:py-5">
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
"hidden pr-8 sm:border-r sm:dark:border-gray-700 md:flex md:flex-col " +
|
"hidden overflow-hidden pr-8 sm:border-r sm:dark:border-gray-700 md:flex md:flex-col " +
|
||||||
(selectedDate ? "sm:w-1/3" : recurringEventCount ? "sm:w-2/3" : "sm:w-1/2")
|
(selectedDate ? "sm:w-1/3" : recurringEventCount ? "sm:w-2/3" : "sm:w-1/2")
|
||||||
}>
|
}>
|
||||||
<AvatarGroup
|
<AvatarGroup
|
||||||
@@ -284,8 +285,10 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
|
|||||||
size={10}
|
size={10}
|
||||||
truncateAfter={3}
|
truncateAfter={3}
|
||||||
/>
|
/>
|
||||||
<h2 className="mt-3 font-medium text-gray-500 dark:text-gray-300">{profile.name}</h2>
|
<h2 className="mt-3 break-words font-medium text-gray-500 dark:text-gray-300">
|
||||||
<h1 className="font-cal mb-4 text-xl font-semibold text-gray-900 dark:text-white">
|
{profile.name}
|
||||||
|
</h2>
|
||||||
|
<h1 className="font-cal mb-4 break-words text-xl font-semibold text-gray-900 dark:text-white">
|
||||||
{eventType.title}
|
{eventType.title}
|
||||||
</h1>
|
</h1>
|
||||||
{eventType?.description && (
|
{eventType?.description && (
|
||||||
|
|||||||
@@ -52,6 +52,15 @@ import { BookPageProps } from "../../../pages/[user]/book";
|
|||||||
import { HashLinkPageProps } from "../../../pages/d/[link]/book";
|
import { HashLinkPageProps } from "../../../pages/d/[link]/book";
|
||||||
import { TeamBookingPageProps } from "../../../pages/team/[slug]/book";
|
import { TeamBookingPageProps } from "../../../pages/team/[slug]/book";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// eslint-disable-next-line no-var
|
||||||
|
var web3: {
|
||||||
|
currentProvider: {
|
||||||
|
selectedAddress: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** These are like 40kb that not every user needs */
|
/** These are like 40kb that not every user needs */
|
||||||
const PhoneInput = dynamic(
|
const PhoneInput = dynamic(
|
||||||
() => import("@components/ui/form/PhoneInput")
|
() => import("@components/ui/form/PhoneInput")
|
||||||
@@ -68,7 +77,7 @@ type BookingFormValues = {
|
|||||||
phone?: string;
|
phone?: string;
|
||||||
hostPhoneNumber?: string; // Maybe come up with a better way to name this to distingish between two types of phone numbers
|
hostPhoneNumber?: string; // Maybe come up with a better way to name this to distingish between two types of phone numbers
|
||||||
customInputs?: {
|
customInputs?: {
|
||||||
[key: string]: string;
|
[key: string]: string | boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -107,7 +116,6 @@ const BookingPage = ({
|
|||||||
const eventOwner = eventType.users[0];
|
const eventOwner = eventType.users[0];
|
||||||
|
|
||||||
if (!contracts[(eventType.metadata.smartContractAddress || null) as number])
|
if (!contracts[(eventType.metadata.smartContractAddress || null) as number])
|
||||||
/* @ts-ignore */
|
|
||||||
router.replace(`/${eventOwner.username}`);
|
router.replace(`/${eventOwner.username}`);
|
||||||
}
|
}
|
||||||
}, [contracts, eventType.metadata.smartContractAddress, eventType.users, router]);
|
}, [contracts, eventType.metadata.smartContractAddress, eventType.users, router]);
|
||||||
@@ -208,7 +216,7 @@ const BookingPage = ({
|
|||||||
}, [router.query.guest]);
|
}, [router.query.guest]);
|
||||||
|
|
||||||
const locationInfo = (type: LocationType) => locations.find((location) => location.type === type);
|
const locationInfo = (type: LocationType) => locations.find((location) => location.type === type);
|
||||||
const loggedInIsOwner = eventType?.users[0]?.name === session?.user?.name;
|
const loggedInIsOwner = eventType?.users[0]?.id === session?.user?.id;
|
||||||
const guestListEmails = !isDynamicGroupBooking
|
const guestListEmails = !isDynamicGroupBooking
|
||||||
? booking?.attendees.slice(1).map((attendee) => attendee.email)
|
? booking?.attendees.slice(1).map((attendee) => attendee.email)
|
||||||
: [];
|
: [];
|
||||||
@@ -236,11 +244,22 @@ const BookingPage = ({
|
|||||||
if (!primaryAttendee) {
|
if (!primaryAttendee) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const customInputType = booking.customInputs;
|
||||||
return {
|
return {
|
||||||
name: primaryAttendee.name || "",
|
name: primaryAttendee.name || "",
|
||||||
email: primaryAttendee.email || "",
|
email: primaryAttendee.email || "",
|
||||||
guests: guestListEmails,
|
guests: guestListEmails,
|
||||||
notes: booking.description || "",
|
notes: booking.description || "",
|
||||||
|
customInputs: eventType.customInputs.reduce(
|
||||||
|
(customInputs, input) => ({
|
||||||
|
...customInputs,
|
||||||
|
[input.id]: booking.customInputs
|
||||||
|
? booking.customInputs[input.label as keyof typeof customInputType]
|
||||||
|
: "",
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -331,7 +350,6 @@ const BookingPage = ({
|
|||||||
let web3Details: Record<"userWallet" | "userSignature", string> | undefined;
|
let web3Details: Record<"userWallet" | "userSignature", string> | undefined;
|
||||||
if (eventTypeDetail.metadata.smartContractAddress) {
|
if (eventTypeDetail.metadata.smartContractAddress) {
|
||||||
web3Details = {
|
web3Details = {
|
||||||
// @ts-ignore
|
|
||||||
userWallet: window.web3.currentProvider.selectedAddress,
|
userWallet: window.web3.currentProvider.selectedAddress,
|
||||||
userSignature: contracts[(eventTypeDetail.metadata.smartContractAddress || null) as number],
|
userSignature: contracts[(eventTypeDetail.metadata.smartContractAddress || null) as number],
|
||||||
};
|
};
|
||||||
@@ -359,8 +377,8 @@ const BookingPage = ({
|
|||||||
),
|
),
|
||||||
metadata,
|
metadata,
|
||||||
customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({
|
customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({
|
||||||
label: eventType.customInputs.find((input) => input.id === parseInt(inputId))!.label,
|
label: eventType.customInputs.find((input) => input.id === parseInt(inputId))?.label || "",
|
||||||
value: booking.customInputs![inputId],
|
value: booking.customInputs && inputId in booking.customInputs ? booking.customInputs[inputId] : "",
|
||||||
})),
|
})),
|
||||||
hasHashedBookingLink,
|
hasHashedBookingLink,
|
||||||
hashedLink,
|
hashedLink,
|
||||||
@@ -383,8 +401,8 @@ const BookingPage = ({
|
|||||||
),
|
),
|
||||||
metadata,
|
metadata,
|
||||||
customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({
|
customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({
|
||||||
label: eventType.customInputs.find((input) => input.id === parseInt(inputId))!.label,
|
label: eventType.customInputs.find((input) => input.id === parseInt(inputId))?.label || "",
|
||||||
value: booking.customInputs![inputId],
|
value: booking.customInputs && inputId in booking.customInputs ? booking.customInputs[inputId] : "",
|
||||||
})),
|
})),
|
||||||
hasHashedBookingLink,
|
hasHashedBookingLink,
|
||||||
hashedLink,
|
hashedLink,
|
||||||
@@ -393,6 +411,9 @@ const BookingPage = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const disableInput = !!rescheduleUid;
|
const disableInput = !!rescheduleUid;
|
||||||
|
const disabledExceptForOwner = disableInput && !loggedInIsOwner;
|
||||||
|
const inputClassName =
|
||||||
|
"focus:border-brand block w-full rounded-sm border-gray-300 shadow-sm focus:ring-black disabled:bg-gray-200 disabled:hover:cursor-not-allowed dark:border-gray-900 dark:bg-gray-700 dark:text-white dark:selection:bg-green-500 disabled:dark:text-gray-500 sm:text-sm";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -534,10 +555,7 @@ const BookingPage = ({
|
|||||||
name="name"
|
name="name"
|
||||||
id="name"
|
id="name"
|
||||||
required
|
required
|
||||||
className={classNames(
|
className={inputClassName}
|
||||||
"focus:border-brand block w-full rounded-sm border-gray-300 shadow-sm focus:ring-black dark:border-gray-900 dark:bg-gray-700 dark:text-white dark:selection:bg-green-500 sm:text-sm",
|
|
||||||
disableInput ? "bg-gray-200 dark:text-gray-500" : ""
|
|
||||||
)}
|
|
||||||
placeholder={t("example_name")}
|
placeholder={t("example_name")}
|
||||||
disabled={disableInput}
|
disabled={disableInput}
|
||||||
/>
|
/>
|
||||||
@@ -554,8 +572,7 @@ const BookingPage = ({
|
|||||||
{...bookingForm.register("email")}
|
{...bookingForm.register("email")}
|
||||||
required
|
required
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"focus:border-brand block w-full rounded-sm shadow-sm focus:ring-black dark:bg-gray-700 dark:text-white dark:selection:bg-green-500 sm:text-sm",
|
inputClassName,
|
||||||
disableInput ? "bg-gray-200 dark:text-gray-500" : "",
|
|
||||||
bookingForm.formState.errors.email
|
bookingForm.formState.errors.email
|
||||||
? "border-red-700 focus:ring-red-700"
|
? "border-red-700 focus:ring-red-700"
|
||||||
: " border-gray-300 dark:border-gray-900"
|
: " border-gray-300 dark:border-gray-900"
|
||||||
@@ -630,12 +647,9 @@ const BookingPage = ({
|
|||||||
})}
|
})}
|
||||||
id={"custom_" + input.id}
|
id={"custom_" + input.id}
|
||||||
rows={3}
|
rows={3}
|
||||||
className={classNames(
|
className={inputClassName}
|
||||||
"focus:border-brand block w-full rounded-sm border-gray-300 shadow-sm focus:ring-black dark:border-gray-900 dark:bg-gray-700 dark:text-white dark:selection:bg-green-500 sm:text-sm",
|
|
||||||
disableInput ? "bg-gray-200 dark:text-gray-500" : ""
|
|
||||||
)}
|
|
||||||
placeholder={input.placeholder}
|
placeholder={input.placeholder}
|
||||||
disabled={disableInput}
|
disabled={disabledExceptForOwner}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{input.type === EventTypeCustomInputType.TEXT && (
|
{input.type === EventTypeCustomInputType.TEXT && (
|
||||||
@@ -645,9 +659,9 @@ const BookingPage = ({
|
|||||||
required: input.required,
|
required: input.required,
|
||||||
})}
|
})}
|
||||||
id={"custom_" + input.id}
|
id={"custom_" + input.id}
|
||||||
className="focus:border-brand block w-full rounded-sm border-gray-300 shadow-sm focus:ring-black dark:border-gray-900 dark:bg-gray-700 dark:text-white dark:selection:bg-green-500 sm:text-sm"
|
className={inputClassName}
|
||||||
placeholder={input.placeholder}
|
placeholder={input.placeholder}
|
||||||
disabled={disableInput}
|
disabled={disabledExceptForOwner}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{input.type === EventTypeCustomInputType.NUMBER && (
|
{input.type === EventTypeCustomInputType.NUMBER && (
|
||||||
@@ -657,8 +671,9 @@ const BookingPage = ({
|
|||||||
required: input.required,
|
required: input.required,
|
||||||
})}
|
})}
|
||||||
id={"custom_" + input.id}
|
id={"custom_" + input.id}
|
||||||
className="focus:border-brand block w-full rounded-sm border-gray-300 shadow-sm focus:ring-black dark:border-gray-900 dark:bg-gray-700 dark:text-white dark:selection:bg-green-500 sm:text-sm"
|
className={inputClassName}
|
||||||
placeholder=""
|
placeholder=""
|
||||||
|
disabled={disabledExceptForOwner}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{input.type === EventTypeCustomInputType.BOOL && (
|
{input.type === EventTypeCustomInputType.BOOL && (
|
||||||
@@ -669,8 +684,9 @@ const BookingPage = ({
|
|||||||
required: input.required,
|
required: input.required,
|
||||||
})}
|
})}
|
||||||
id={"custom_" + input.id}
|
id={"custom_" + input.id}
|
||||||
className="h-4 w-4 rounded border-gray-300 text-black focus:ring-black ltr:mr-2 rtl:ml-2"
|
className="h-4 w-4 rounded border-gray-300 text-black focus:ring-black disabled:bg-gray-200 ltr:mr-2 rtl:ml-2 disabled:dark:text-gray-500"
|
||||||
placeholder=""
|
placeholder=""
|
||||||
|
disabled={disabledExceptForOwner}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={"custom_" + input.id}
|
htmlFor={"custom_" + input.id}
|
||||||
@@ -757,12 +773,9 @@ const BookingPage = ({
|
|||||||
id="notes"
|
id="notes"
|
||||||
name="notes"
|
name="notes"
|
||||||
rows={3}
|
rows={3}
|
||||||
className={classNames(
|
className={inputClassName}
|
||||||
"focus:border-brand block w-full rounded-sm border-gray-300 shadow-sm focus:ring-black dark:border-gray-900 dark:bg-gray-700 dark:text-white dark:selection:bg-green-500 sm:text-sm",
|
|
||||||
disableInput ? "bg-gray-200 dark:text-gray-500" : ""
|
|
||||||
)}
|
|
||||||
placeholder={t("share_additional_notes")}
|
placeholder={t("share_additional_notes")}
|
||||||
disabled={disableInput}
|
disabled={disabledExceptForOwner}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start space-x-2 rtl:space-x-reverse">
|
<div className="flex items-start space-x-2 rtl:space-x-reverse">
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export type ConfirmationDialogContentProps = {
|
|||||||
confirmBtnText?: string;
|
confirmBtnText?: string;
|
||||||
cancelBtnText?: string;
|
cancelBtnText?: string;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
|
loadingText?: string;
|
||||||
onConfirm?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
onConfirm?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
||||||
title: string;
|
title: string;
|
||||||
variety?: "danger" | "warning" | "success";
|
variety?: "danger" | "warning" | "success";
|
||||||
@@ -25,6 +26,7 @@ export default function ConfirmationDialogContent(props: PropsWithChildren<Confi
|
|||||||
confirmBtn = null,
|
confirmBtn = null,
|
||||||
confirmBtnText = t("confirm"),
|
confirmBtnText = t("confirm"),
|
||||||
cancelBtnText = t("cancel"),
|
cancelBtnText = t("cancel"),
|
||||||
|
loadingText = t("loading"),
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
children,
|
children,
|
||||||
@@ -63,7 +65,7 @@ export default function ConfirmationDialogContent(props: PropsWithChildren<Confi
|
|||||||
<DialogClose disabled={isLoading} onClick={onConfirm} asChild>
|
<DialogClose disabled={isLoading} onClick={onConfirm} asChild>
|
||||||
{confirmBtn || (
|
{confirmBtn || (
|
||||||
<Button color="primary" loading={isLoading}>
|
<Button color="primary" loading={isLoading}>
|
||||||
{isLoading ? t("loading") : confirmBtnText}
|
{isLoading ? loadingText : confirmBtnText}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import { ExclamationIcon } from "@heroicons/react/outline";
|
import { ExclamationIcon } from "@heroicons/react/outline";
|
||||||
import { CheckIcon } from "@heroicons/react/solid";
|
import { CheckIcon } from "@heroicons/react/solid";
|
||||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||||
import React, { PropsWithChildren, ReactNode } from "react";
|
import React, { PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Button } from "@calcom/ui/Button";
|
import { Button } from "@calcom/ui/Button";
|
||||||
import { DialogClose, DialogContent } from "@calcom/ui/Dialog";
|
import { DialogClose, DialogContent } from "@calcom/ui/Dialog";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
|
|
||||||
export type DeleteStripeDialogContentProps = {
|
export type DeleteStripeDialogContentProps = {
|
||||||
confirmBtn?: ReactNode;
|
|
||||||
cancelAllBookingsBtnText?: string;
|
cancelAllBookingsBtnText?: string;
|
||||||
removeBtnText?: string;
|
removeBtnText?: string;
|
||||||
cancelBtnText?: string;
|
cancelBtnText?: string;
|
||||||
@@ -24,7 +22,6 @@ export default function DeleteStripeDialogContent(props: PropsWithChildren<Delet
|
|||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
variety,
|
variety,
|
||||||
confirmBtn = null,
|
|
||||||
cancelAllBookingsBtnText,
|
cancelAllBookingsBtnText,
|
||||||
removeBtnText,
|
removeBtnText,
|
||||||
cancelBtnText = t("cancel"),
|
cancelBtnText = t("cancel"),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ClockIcon, XIcon } from "@heroicons/react/outline";
|
import { ClockIcon } from "@heroicons/react/outline";
|
||||||
import { RescheduleResponse } from "pages/api/book/request-reschedule";
|
import { RescheduleResponse } from "pages/api/book/request-reschedule";
|
||||||
import React, { useState, Dispatch, SetStateAction } from "react";
|
import React, { useState, Dispatch, SetStateAction } from "react";
|
||||||
import { useMutation } from "react-query";
|
import { useMutation } from "react-query";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Collapsible, CollapsibleContent } from "@radix-ui/react-collapsible";
|
import { Collapsible, CollapsibleContent } from "@radix-ui/react-collapsible";
|
||||||
|
import type { FormValues } from "pages/event-types/[type]";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { UseFormReturn } from "react-hook-form";
|
import { UseFormReturn } from "react-hook-form";
|
||||||
import { Frequency as RRuleFrequency } from "rrule";
|
import { Frequency as RRuleFrequency } from "rrule";
|
||||||
@@ -11,9 +12,9 @@ import Select from "@components/ui/form/Select";
|
|||||||
|
|
||||||
type RecurringEventControllerProps = {
|
type RecurringEventControllerProps = {
|
||||||
recurringEvent: RecurringEvent;
|
recurringEvent: RecurringEvent;
|
||||||
formMethods: UseFormReturn<any, any>;
|
formMethods: UseFormReturn<FormValues>;
|
||||||
paymentEnabled: boolean;
|
paymentEnabled: boolean;
|
||||||
onRecurringEventDefined: Function;
|
onRecurringEventDefined: (value: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RecurringEventController({
|
export default function RecurringEventController({
|
||||||
@@ -43,9 +44,7 @@ export default function RecurringEventController({
|
|||||||
return (
|
return (
|
||||||
<div className="block items-start sm:flex">
|
<div className="block items-start sm:flex">
|
||||||
<div className="min-w-48 mb-4 sm:mb-0">
|
<div className="min-w-48 mb-4 sm:mb-0">
|
||||||
<label htmlFor="recurringEvent" className="flex text-sm font-medium text-neutral-700">
|
<span className="flex text-sm font-medium text-neutral-700">{t("recurring_event")}</span>
|
||||||
{t("recurring_event")}
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={!paymentEnabled ? "w-full" : ""}>
|
<div className={!paymentEnabled ? "w-full" : ""}>
|
||||||
{paymentEnabled ? (
|
{paymentEnabled ? (
|
||||||
@@ -78,10 +77,13 @@ export default function RecurringEventController({
|
|||||||
className="text-primary-600 h-4 w-4 rounded border-gray-300"
|
className="text-primary-600 h-4 w-4 rounded border-gray-300"
|
||||||
defaultChecked={recurringEventDefined}
|
defaultChecked={recurringEventDefined}
|
||||||
data-testid="recurring-event-check"
|
data-testid="recurring-event-check"
|
||||||
|
id="recurringEvent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm ltr:ml-3 rtl:mr-3">
|
<div className="text-sm ltr:ml-3 rtl:mr-3">
|
||||||
<p className="text-neutral-900">{t("recurring_event_description")}</p>
|
<label htmlFor="recurringEvent" className="text-neutral-900">
|
||||||
|
{t("recurring_event_description")}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Collapsible
|
<Collapsible
|
||||||
|
|||||||
@@ -118,7 +118,6 @@ export default function TeamSettingsRightSidebar(props: { team: TeamWithMembers;
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{props.team?.id && props.role !== MembershipRole.MEMBER && (
|
{props.team?.id && props.role !== MembershipRole.MEMBER && (
|
||||||
// eslint-disable-next-line @next/next/link-passhref
|
|
||||||
<Link href={`/settings/teams/${props.team.id}/availability`}>
|
<Link href={`/settings/teams/${props.team.id}/availability`}>
|
||||||
<div className="mt-5 hidden space-y-1 sm:block">
|
<div className="mt-5 hidden space-y-1 sm:block">
|
||||||
<LinkIconButton Icon={ClockIcon}>{"View Availability"}</LinkIconButton>
|
<LinkIconButton Icon={ClockIcon}>{"View Availability"}</LinkIconButton>
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ const Team = ({ team }: TeamPageProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// eslint-disable-next-line @next/next/link-passhref
|
|
||||||
<Link key={member.id} href={`/${member.username}`}>
|
<Link key={member.id} href={`/${member.username}`}>
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
<ArrowRightIcon
|
<ArrowRightIcon
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { FC, Fragment } from "react";
|
|||||||
|
|
||||||
type AdminRequiredProps = {
|
type AdminRequiredProps = {
|
||||||
as?: keyof JSX.IntrinsicElements;
|
as?: keyof JSX.IntrinsicElements;
|
||||||
|
children?: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AdminRequired: FC<AdminRequiredProps> = ({ children, as, ...rest }) => {
|
export const AdminRequired: FC<AdminRequiredProps> = ({ children, as, ...rest }) => {
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import { Trans } from "next-i18next";
|
|||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Alert } from "@calcom/ui/Alert";
|
import { Alert } from "@calcom/ui/Alert";
|
||||||
|
|
||||||
type Props = {};
|
function ImpersonatingBanner() {
|
||||||
|
|
||||||
function ImpersonatingBanner({}: Props) {
|
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const { data } = useSession();
|
const { data } = useSession();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog";
|
import { Dialog, DialogContent } from "@calcom/ui/Dialog";
|
||||||
|
|
||||||
interface Props extends React.PropsWithChildren<any> {
|
interface Props extends React.PropsWithChildren<any> {
|
||||||
wide?: boolean;
|
wide?: boolean;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ChevronDownIcon, DotsHorizontalIcon } from "@heroicons/react/solid";
|
|||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
|
|
||||||
import Button from "@calcom/ui/Button";
|
import Button from "@calcom/ui/Button";
|
||||||
import Dropdown, { DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@calcom/ui/Dropdown";
|
import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
|
||||||
|
|
||||||
import { SVGComponent } from "@lib/types/SVGComponent";
|
import { SVGComponent } from "@lib/types/SVGComponent";
|
||||||
|
|
||||||
@@ -12,15 +12,27 @@ export type ActionType = {
|
|||||||
label: string;
|
label: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
color?: "primary" | "secondary";
|
color?: "primary" | "secondary";
|
||||||
} & ({ href?: never; onClick: () => any } | { href?: string; onClick?: never }) & {
|
} & (
|
||||||
actions?: ActionType[];
|
| { href: string; onClick?: never; actions?: never }
|
||||||
};
|
| { href?: never; onClick: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void; actions?: never }
|
||||||
|
| { actions?: ActionType[]; href?: never; onClick?: never }
|
||||||
|
);
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
actions: ActionType[];
|
actions: ActionType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const DropdownActions = ({ actions, actionTrigger }: { actions: ActionType[]; actionTrigger?: any }) => {
|
const defaultAction = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
const DropdownActions = ({
|
||||||
|
actions,
|
||||||
|
actionTrigger,
|
||||||
|
}: {
|
||||||
|
actions: ActionType[];
|
||||||
|
actionTrigger?: React.ReactNode;
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
{!actionTrigger ? (
|
{!actionTrigger ? (
|
||||||
@@ -40,7 +52,7 @@ const DropdownActions = ({ actions, actionTrigger }: { actions: ActionType[]; ac
|
|||||||
className="w-full rounded-none font-normal"
|
className="w-full rounded-none font-normal"
|
||||||
href={action.href}
|
href={action.href}
|
||||||
StartIcon={action.icon}
|
StartIcon={action.icon}
|
||||||
onClick={action.onClick}
|
onClick={action.onClick || defaultAction}
|
||||||
data-testid={action.id}>
|
data-testid={action.id}>
|
||||||
{action.label}
|
{action.label}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -67,7 +79,7 @@ const TableActions: FC<Props> = ({ actions }) => {
|
|||||||
key={action.id}
|
key={action.id}
|
||||||
data-testid={action.id}
|
data-testid={action.id}
|
||||||
href={action.href}
|
href={action.href}
|
||||||
onClick={action.onClick}
|
onClick={action.onClick || defaultAction}
|
||||||
StartIcon={action.icon}
|
StartIcon={action.icon}
|
||||||
{...(action?.actions ? { EndIcon: ChevronDownIcon } : null)}
|
{...(action?.actions ? { EndIcon: ChevronDownIcon } : null)}
|
||||||
disabled={action.disabled}
|
disabled={action.disabled}
|
||||||
|
|||||||
@@ -1,18 +1,30 @@
|
|||||||
import React, { forwardRef, InputHTMLAttributes } from "react";
|
import React, { forwardRef, InputHTMLAttributes } from "react";
|
||||||
|
|
||||||
|
import InfoBadge from "@components/ui/InfoBadge";
|
||||||
|
|
||||||
type Props = InputHTMLAttributes<HTMLInputElement> & {
|
type Props = InputHTMLAttributes<HTMLInputElement> & {
|
||||||
label: React.ReactNode;
|
label?: React.ReactNode;
|
||||||
description: string;
|
description: string;
|
||||||
|
descriptionAsLabel?: boolean;
|
||||||
|
infomationIconText?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CheckboxField = forwardRef<HTMLInputElement, Props>(({ label, description, ...rest }, ref) => {
|
const CheckboxField = forwardRef<HTMLInputElement, Props>(
|
||||||
|
({ label, description, infomationIconText, descriptionAsLabel, ...rest }, ref) => {
|
||||||
return (
|
return (
|
||||||
<div className="block items-center sm:flex">
|
<div className="block items-center sm:flex">
|
||||||
|
{label && !descriptionAsLabel && (
|
||||||
<div className="min-w-48 mb-4 sm:mb-0">
|
<div className="min-w-48 mb-4 sm:mb-0">
|
||||||
<label htmlFor={rest.id} className="flex text-sm font-medium text-neutral-700">
|
<label htmlFor={rest.id} className="flex text-sm font-medium text-neutral-700">
|
||||||
{label}
|
{label}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
{label && descriptionAsLabel && (
|
||||||
|
<div className="min-w-48 mb-4 sm:mb-0">
|
||||||
|
<span className="flex text-sm font-medium text-neutral-700">{label}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="relative flex items-start">
|
<div className="relative flex items-start">
|
||||||
<div className="flex h-5 items-center">
|
<div className="flex h-5 items-center">
|
||||||
@@ -24,13 +36,21 @@ const CheckboxField = forwardRef<HTMLInputElement, Props>(({ label, description,
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm ltr:ml-3 rtl:mr-3">
|
<div className="text-sm ltr:ml-3 rtl:mr-3">
|
||||||
|
{!label || descriptionAsLabel ? (
|
||||||
|
<label htmlFor={rest.id} className="text-neutral-700">
|
||||||
|
{description}
|
||||||
|
</label>
|
||||||
|
) : (
|
||||||
<p className="text-neutral-900">{description}</p>
|
<p className="text-neutral-900">{description}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{infomationIconText && <InfoBadge content={infomationIconText}></InfoBadge>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
CheckboxField.displayName = "CheckboxField";
|
CheckboxField.displayName = "CheckboxField";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { components } from "react-select";
|
import { components } from "react-select";
|
||||||
import BaseSelect, { ITimezone, Props as SelectProps } from "react-timezone-select";
|
import BaseSelect, {
|
||||||
|
allTimezones,
|
||||||
|
ITimezoneOption,
|
||||||
|
ITimezone,
|
||||||
|
Props as SelectProps,
|
||||||
|
} from "react-timezone-select";
|
||||||
|
|
||||||
import { InputComponent } from "@components/ui/form/Select";
|
import { InputComponent } from "@components/ui/form/Select";
|
||||||
|
|
||||||
@@ -28,6 +33,10 @@ function TimezoneSelect({ className, ...props }: SelectProps) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}}
|
}}
|
||||||
|
timezones={{
|
||||||
|
...allTimezones,
|
||||||
|
"America/Asuncion": "Asuncion",
|
||||||
|
}}
|
||||||
components={{
|
components={{
|
||||||
...components,
|
...components,
|
||||||
IndicatorSeparator: () => null,
|
IndicatorSeparator: () => null,
|
||||||
@@ -40,4 +49,4 @@ function TimezoneSelect({ className, ...props }: SelectProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default TimezoneSelect;
|
export default TimezoneSelect;
|
||||||
export type { ITimezone };
|
export type { ITimezone, ITimezoneOption };
|
||||||
@@ -36,7 +36,7 @@ type ChildrenProps = {
|
|||||||
props: RadioAreaProps;
|
props: RadioAreaProps;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
};
|
};
|
||||||
interface RadioAreaGroupProps extends Omit<React.ComponentPropsWithoutRef<"div">, "onChange"> {
|
interface RadioAreaGroupProps extends Omit<React.ComponentPropsWithoutRef<"div">, "onChange" | "children"> {
|
||||||
children: ChildrenProps | ChildrenProps[];
|
children: ChildrenProps | ChildrenProps[];
|
||||||
name?: string;
|
name?: string;
|
||||||
onChange?: (value: string) => void;
|
onChange?: (value: string) => void;
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
|
import noop from "lodash/noop";
|
||||||
import { createContext, ReactNode, useContext } from "react";
|
import { createContext, ReactNode, useContext } from "react";
|
||||||
|
|
||||||
import { localStorage } from "@calcom/lib/webstorage";
|
import { localStorage } from "@calcom/lib/webstorage";
|
||||||
|
|
||||||
type contractsContextType = Record<string, string>;
|
type contractsContextType = {
|
||||||
|
contracts: Record<string, string>;
|
||||||
|
addContract: (payload: addContractsPayload) => void;
|
||||||
|
};
|
||||||
|
|
||||||
const contractsContextDefaultValue: contractsContextType = {};
|
const contractsContextDefaultValue: contractsContextType = {
|
||||||
|
contracts: {},
|
||||||
|
addContract: noop,
|
||||||
|
};
|
||||||
|
|
||||||
const ContractsContext = createContext<contractsContextType>(contractsContextDefaultValue);
|
const ContractsContext = createContext<contractsContextType>(contractsContextDefaultValue);
|
||||||
|
|
||||||
@@ -37,10 +44,5 @@ export function ContractsProvider({ children }: Props) {
|
|||||||
addContract,
|
addContract,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return <ContractsContext.Provider value={value}>{children}</ContractsContext.Provider>;
|
||||||
<>
|
|
||||||
{/* @ts-ignore */}
|
|
||||||
<ContractsContext.Provider value={value}>{children}</ContractsContext.Provider>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,8 @@ const PaymentPage: FC<PaymentPageProps> = (props) => {
|
|||||||
embedIframeWidth = e.detail.data.iframeWidth as number;
|
embedIframeWidth = e.detail.data.iframeWidth as number;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [date, isEmbed]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [isEmbed]);
|
||||||
|
|
||||||
const eventName = props.booking.title;
|
const eventName = props.booking.title;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import TimezoneSelect, { ITimezone } from "react-timezone-select";
|
|
||||||
|
|
||||||
import { WEBSITE_URL } from "@calcom/lib/constants";
|
import { WEBSITE_URL } from "@calcom/lib/constants";
|
||||||
|
|
||||||
@@ -10,6 +9,7 @@ import { trpc, inferQueryOutput } from "@lib/trpc";
|
|||||||
import Avatar from "@components/ui/Avatar";
|
import Avatar from "@components/ui/Avatar";
|
||||||
import { DatePicker } from "@components/ui/form/DatePicker";
|
import { DatePicker } from "@components/ui/form/DatePicker";
|
||||||
import Select from "@components/ui/form/Select";
|
import Select from "@components/ui/form/Select";
|
||||||
|
import TimezoneSelect, { ITimezone } from "@components/ui/form/TimezoneSelect";
|
||||||
|
|
||||||
import TeamAvailabilityTimes from "./TeamAvailabilityTimes";
|
import TeamAvailabilityTimes from "./TeamAvailabilityTimes";
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import React, { useState, useEffect, CSSProperties } from "react";
|
import React, { useState, useEffect, CSSProperties } from "react";
|
||||||
import TimezoneSelect, { ITimezone } from "react-timezone-select";
|
|
||||||
import AutoSizer from "react-virtualized-auto-sizer";
|
import AutoSizer from "react-virtualized-auto-sizer";
|
||||||
import { FixedSizeList as List } from "react-window";
|
import { FixedSizeList as List } from "react-window";
|
||||||
|
|
||||||
import { WEBSITE_URL } from "@calcom/lib/constants";
|
import { WEBSITE_URL } from "@calcom/lib/constants";
|
||||||
|
|
||||||
import { trpc, inferQueryOutput } from "@lib/trpc";
|
import { inferQueryOutput, trpc } from "@lib/trpc";
|
||||||
|
|
||||||
import Avatar from "@components/ui/Avatar";
|
import Avatar from "@components/ui/Avatar";
|
||||||
import { DatePicker } from "@components/ui/form/DatePicker";
|
import { DatePicker } from "@components/ui/form/DatePicker";
|
||||||
import Select from "@components/ui/form/Select";
|
import Select from "@components/ui/form/Select";
|
||||||
|
import TimezoneSelect, { ITimezone } from "@components/ui/form/TimezoneSelect";
|
||||||
|
|
||||||
import TeamAvailabilityTimes from "./TeamAvailabilityTimes";
|
import TeamAvailabilityTimes from "./TeamAvailabilityTimes";
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
import Router from "next/router";
|
import Router from "next/router";
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import React, { useCallback, useMemo, useState } from "react";
|
||||||
import React from "react";
|
|
||||||
import Web3 from "web3";
|
import Web3 from "web3";
|
||||||
|
import type { AbstractProvider } from "web3-core";
|
||||||
import { AbiItem } from "web3-utils";
|
import { AbiItem } from "web3-utils";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import showToast from "@calcom/lib/notification";
|
import showToast from "@calcom/lib/notification";
|
||||||
import { Button } from "@calcom/ui/Button";
|
import { Button } from "@calcom/ui/Button";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
|
|
||||||
import { useContracts } from "../../../contexts/contractsContext";
|
import { useContracts } from "../../../contexts/contractsContext";
|
||||||
import genericAbi from "../../../web3/abis/abiWithGetBalance.json";
|
import genericAbi from "../../../web3/abis/abiWithGetBalance.json";
|
||||||
import verifyAccount, { AUTH_MESSAGE } from "../../../web3/utils/verifyAccount";
|
import verifyAccount, { AUTH_MESSAGE } from "../../../web3/utils/verifyAccount";
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
ethereum: any;
|
ethereum: AbstractProvider & { selectedAddress: string };
|
||||||
web3: Web3;
|
web3: Web3;
|
||||||
}
|
}
|
||||||
interface EvtsToVerify {
|
interface EvtsToVerify {
|
||||||
@@ -40,7 +39,7 @@ const CryptoSection = (props: CryptoSectionProps) => {
|
|||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
const connectMetamask = useCallback(async () => {
|
const connectMetamask = useCallback(async () => {
|
||||||
if (window.ethereum) {
|
if (window.ethereum && typeof window.ethereum.request === "function") {
|
||||||
await window.ethereum.request({ method: "eth_requestAccounts" });
|
await window.ethereum.request({ method: "eth_requestAccounts" });
|
||||||
window.web3 = new Web3(window.ethereum);
|
window.web3 = new Web3(window.ethereum);
|
||||||
toggleEthEnabled(true);
|
toggleEthEnabled(true);
|
||||||
@@ -57,17 +56,13 @@ const CryptoSection = (props: CryptoSectionProps) => {
|
|||||||
|
|
||||||
const contract = new window.web3.eth.Contract(genericAbi as AbiItem[], props.smartContractAddress);
|
const contract = new window.web3.eth.Contract(genericAbi as AbiItem[], props.smartContractAddress);
|
||||||
const balance = await contract.methods.balanceOf(window.ethereum.selectedAddress).call();
|
const balance = await contract.methods.balanceOf(window.ethereum.selectedAddress).call();
|
||||||
|
|
||||||
const hasToken = balance > 0;
|
const hasToken = balance > 0;
|
||||||
|
|
||||||
if (!hasToken) {
|
if (!hasToken) {
|
||||||
throw new Error("Specified wallet does not own any tokens belonging to this smart contract");
|
throw new Error("Specified wallet does not own any tokens belonging to this smart contract");
|
||||||
} else {
|
} else {
|
||||||
const account = (await window.web3.eth.getAccounts())[0];
|
const [account] = await window.web3.eth.getAccounts();
|
||||||
|
const signature = await window.web3.eth.personal.sign(AUTH_MESSAGE, account, "");
|
||||||
// @ts-ignore
|
|
||||||
const signature = await window.web3.eth.personal.sign(AUTH_MESSAGE, account);
|
|
||||||
// @ts-ignore
|
|
||||||
addContract({ address: props.smartContractAddress, signature });
|
addContract({ address: props.smartContractAddress, signature });
|
||||||
|
|
||||||
await verifyAccount(signature, account);
|
await verifyAccount(signature, account);
|
||||||
@@ -93,7 +88,7 @@ const CryptoSection = (props: CryptoSectionProps) => {
|
|||||||
|
|
||||||
const verifyButton = useMemo(() => {
|
const verifyButton = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<Button color="secondary" onClick={verifyWallet} type="button" id="hasToken" name="hasToken">
|
<Button color="secondary" onClick={verifyWallet} type="button" id="hasToken">
|
||||||
{
|
{
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
<img className="mr-1 h-5" src="/apps/metamask.svg" alt="MetaMask" />
|
<img className="mr-1 h-5" src="/apps/metamask.svg" alt="MetaMask" />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { LiveChatLoaderProvider } from "react-live-chat-loader";
|
import { LiveChatLoaderProvider } from "react-live-chat-loader";
|
||||||
|
|
||||||
const Provider: FC = ({ children }) => (
|
const Provider: FC<{ children: React.ReactNode }> = ({ children }) => (
|
||||||
<LiveChatLoaderProvider providerKey={process.env.NEXT_PUBLIC_HELPSCOUT_KEY!} provider="helpScout">
|
<LiveChatLoaderProvider providerKey={process.env.NEXT_PUBLIC_HELPSCOUT_KEY!} provider="helpScout">
|
||||||
<>{children}</>
|
<>{children}</>
|
||||||
</LiveChatLoaderProvider>
|
</LiveChatLoaderProvider>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const ImpersonationProvider = CredentialsProvider({
|
|||||||
username: { label: "Username", type: "text " },
|
username: { label: "Username", type: "text " },
|
||||||
},
|
},
|
||||||
async authorize(creds, req) {
|
async authorize(creds, req) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore need to figure out how to correctly type this
|
// @ts-ignore need to figure out how to correctly type this
|
||||||
const session = await getSession({ req });
|
const session = await getSession({ req });
|
||||||
if (session?.user.role !== "ADMIN") {
|
if (session?.user.role !== "ADMIN") {
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { ChatAltIcon } from "@heroicons/react/solid";
|
import { ChatAltIcon } from "@heroicons/react/solid";
|
||||||
import { useIntercom } from "react-use-intercom";
|
|
||||||
|
|
||||||
import { DropdownMenuItem } from "@calcom/ui/Dropdown";
|
import { DropdownMenuItem } from "@calcom/ui/Dropdown";
|
||||||
|
|
||||||
import classNames from "@lib/classNames";
|
import classNames from "@lib/classNames";
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
import { useLocale } from "@lib/hooks/useLocale";
|
||||||
|
|
||||||
|
import { useIntercom } from "./useIntercom";
|
||||||
|
|
||||||
export default function IntercomMenuItem() {
|
export default function IntercomMenuItem() {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const { boot, show } = useIntercom();
|
const { boot, show } = useIntercom();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { IntercomProvider } from "react-use-intercom";
|
import { IntercomProvider } from "react-use-intercom";
|
||||||
|
|
||||||
const Provider: FC = ({ children }) => (
|
const Provider: FC<{ children: React.ReactNode }> = ({ children }) => (
|
||||||
<IntercomProvider appId={process.env.NEXT_PUBLIC_INTERCOM_APP_ID!}>{children}</IntercomProvider>
|
<IntercomProvider appId={process.env.NEXT_PUBLIC_INTERCOM_APP_ID!}>{children}</IntercomProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import noop from "lodash/noop";
|
||||||
|
import { useIntercom as _useIntercom } from "react-use-intercom";
|
||||||
|
|
||||||
|
export const useIntercom =
|
||||||
|
typeof window !== "undefined" && !!process.env.NEXT_PUBLIC_INTERCOM_APP_ID
|
||||||
|
? _useIntercom
|
||||||
|
: () => ({
|
||||||
|
boot: noop,
|
||||||
|
show: noop,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default useIntercom;
|
||||||
@@ -4,8 +4,9 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
|||||||
import Stripe from "stripe";
|
import Stripe from "stripe";
|
||||||
|
|
||||||
import EventManager from "@calcom/core/EventManager";
|
import EventManager from "@calcom/core/EventManager";
|
||||||
|
import { isPrismaObjOrUndefined } from "@calcom/lib";
|
||||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||||
import stripe from "@calcom/stripe/server";
|
import stripe from "@calcom/stripe/server";
|
||||||
import { CalendarEvent, RecurringEvent } from "@calcom/types/Calendar";
|
import { CalendarEvent, RecurringEvent } from "@calcom/types/Calendar";
|
||||||
|
|
||||||
@@ -42,16 +43,11 @@ async function handlePaymentSuccess(event: Stripe.Event) {
|
|||||||
id: payment.bookingId,
|
id: payment.bookingId,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
title: true,
|
...bookingMinimalSelect,
|
||||||
description: true,
|
|
||||||
startTime: true,
|
|
||||||
endTime: true,
|
|
||||||
confirmed: true,
|
confirmed: true,
|
||||||
attendees: true,
|
|
||||||
location: true,
|
location: true,
|
||||||
eventTypeId: true,
|
eventTypeId: true,
|
||||||
userId: true,
|
userId: true,
|
||||||
id: true,
|
|
||||||
uid: true,
|
uid: true,
|
||||||
paid: true,
|
paid: true,
|
||||||
destinationCalendar: true,
|
destinationCalendar: true,
|
||||||
@@ -113,8 +109,9 @@ async function handlePaymentSuccess(event: Stripe.Event) {
|
|||||||
description: booking.description || undefined,
|
description: booking.description || undefined,
|
||||||
startTime: booking.startTime.toISOString(),
|
startTime: booking.startTime.toISOString(),
|
||||||
endTime: booking.endTime.toISOString(),
|
endTime: booking.endTime.toISOString(),
|
||||||
|
customInputs: isPrismaObjOrUndefined(booking.customInputs),
|
||||||
organizer: {
|
organizer: {
|
||||||
email: user.email!,
|
email: user.email,
|
||||||
name: user.name!,
|
name: user.name!,
|
||||||
timeZone: user.timeZone,
|
timeZone: user.timeZone,
|
||||||
language: { translate: t, locale: user.locale ?? "en" },
|
language: { translate: t, locale: user.locale ?? "en" },
|
||||||
@@ -126,7 +123,7 @@ async function handlePaymentSuccess(event: Stripe.Event) {
|
|||||||
|
|
||||||
if (booking.location) evt.location = booking.location;
|
if (booking.location) evt.location = booking.location;
|
||||||
|
|
||||||
let bookingData: Prisma.BookingUpdateInput = {
|
const bookingData: Prisma.BookingUpdateInput = {
|
||||||
paid: true,
|
paid: true,
|
||||||
confirmed: true,
|
confirmed: true,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { SessionProvider } from "next-auth/react";
|
import { SessionProvider } from "next-auth/react";
|
||||||
import { appWithTranslation } from "next-i18next";
|
import { appWithTranslation } from "next-i18next";
|
||||||
import type { AppProps as NextAppProps } from "next/app";
|
import type { AppProps as NextAppProps, AppProps as NextJsAppProps } from "next/app";
|
||||||
import { ComponentProps, ReactNode, useMemo } from "react";
|
import { ComponentProps, ReactNode, useMemo } from "react";
|
||||||
|
|
||||||
import DynamicHelpscoutProvider from "@ee/lib/helpscout/providerDynamic";
|
import DynamicHelpscoutProvider from "@ee/lib/helpscout/providerDynamic";
|
||||||
@@ -11,7 +11,9 @@ import { createTelemetryClient, TelemetryProvider } from "@lib/telemetry";
|
|||||||
|
|
||||||
import { trpc } from "./trpc";
|
import { trpc } from "./trpc";
|
||||||
|
|
||||||
const I18nextAdapter = appWithTranslation(({ children }: { children?: ReactNode }) => <>{children}</>);
|
const I18nextAdapter = appWithTranslation<NextJsAppProps & { children: React.ReactNode }>(({ children }) => (
|
||||||
|
<>{children}</>
|
||||||
|
));
|
||||||
|
|
||||||
// Workaround for https://github.com/vercel/next.js/issues/8592
|
// Workaround for https://github.com/vercel/next.js/issues/8592
|
||||||
export type AppProps = NextAppProps & {
|
export type AppProps = NextAppProps & {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Account, IdentityProvider, Prisma, PrismaClient, User, VerificationToken } from "@prisma/client";
|
import { Account, IdentityProvider, Prisma, PrismaClient, User, VerificationToken } from "@prisma/client";
|
||||||
|
import { PrismaClientKnownRequestError } from "@prisma/client/runtime";
|
||||||
|
|
||||||
import { identityProviderNameMap } from "@lib/auth";
|
import { identityProviderNameMap } from "@lib/auth";
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ export default function CalComAdapter(prismaClient: PrismaClient) {
|
|||||||
prismaClient.user.update({ where: { id }, data }),
|
prismaClient.user.update({ where: { id }, data }),
|
||||||
deleteUser: (id: User["id"]) => prismaClient.user.delete({ where: { id } }),
|
deleteUser: (id: User["id"]) => prismaClient.user.delete({ where: { id } }),
|
||||||
async createVerificationToken(data: VerificationToken) {
|
async createVerificationToken(data: VerificationToken) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { id: _, ...verificationToken } = await prismaClient.verificationToken.create({
|
const { id: _, ...verificationToken } = await prismaClient.verificationToken.create({
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
@@ -52,6 +54,7 @@ export default function CalComAdapter(prismaClient: PrismaClient) {
|
|||||||
},
|
},
|
||||||
async useVerificationToken(identifier_token: Prisma.VerificationTokenIdentifierTokenCompoundUniqueInput) {
|
async useVerificationToken(identifier_token: Prisma.VerificationTokenIdentifierTokenCompoundUniqueInput) {
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { id: _, ...verificationToken } = await prismaClient.verificationToken.delete({
|
const { id: _, ...verificationToken } = await prismaClient.verificationToken.delete({
|
||||||
where: { identifier_token },
|
where: { identifier_token },
|
||||||
});
|
});
|
||||||
@@ -59,8 +62,9 @@ export default function CalComAdapter(prismaClient: PrismaClient) {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If token already used/deleted, just return null
|
// If token already used/deleted, just return null
|
||||||
// https://www.prisma.io/docs/reference/api-reference/error-reference#p2025
|
// https://www.prisma.io/docs/reference/api-reference/error-reference#p2025
|
||||||
// @ts-ignore
|
if (error instanceof PrismaClientKnownRequestError) {
|
||||||
if (error.code === "P2025") return null;
|
if (error.code === "P2025") return null;
|
||||||
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import noop from "lodash/noop";
|
||||||
|
|
||||||
const data: Record<string, number> = {};
|
const data: Record<string, number> = {};
|
||||||
/**
|
/**
|
||||||
* Starts an iteration from `0` to `length - 1` with batch size `batch`
|
* Starts an iteration from `0` to `length - 1` with batch size `batch`
|
||||||
@@ -20,9 +22,9 @@ export const doWorkAsync = ({
|
|||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
length: number;
|
length: number;
|
||||||
callback: Function;
|
callback: (i: number, b?: boolean) => void;
|
||||||
done?: Function;
|
done?: () => void;
|
||||||
batchDone?: Function;
|
batchDone?: () => void;
|
||||||
batch: number;
|
batch: number;
|
||||||
offsetStart?: number;
|
offsetStart?: number;
|
||||||
__pending?: boolean;
|
__pending?: boolean;
|
||||||
@@ -32,8 +34,8 @@ export const doWorkAsync = ({
|
|||||||
const lastIndex = length - 1;
|
const lastIndex = length - 1;
|
||||||
const offsetEndExclusive = offsetStart + stepLength;
|
const offsetEndExclusive = offsetStart + stepLength;
|
||||||
|
|
||||||
batchDone = batchDone || (() => {});
|
batchDone = batchDone || noop;
|
||||||
done = done || (() => {});
|
done = done || noop;
|
||||||
|
|
||||||
if (!__pending && data[name]) {
|
if (!__pending && data[name]) {
|
||||||
cancelAnimationFrame(data[name]);
|
cancelAnimationFrame(data[name]);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { recurringEvent } from "@calcom/prisma/zod-utils";
|
|
||||||
import type { CalendarEvent, Person, RecurringEvent } from "@calcom/types/Calendar";
|
import type { CalendarEvent, Person, RecurringEvent } from "@calcom/types/Calendar";
|
||||||
|
|
||||||
import AttendeeAwaitingPaymentEmail from "@lib/emails/templates/attendee-awaiting-payment-email";
|
import AttendeeAwaitingPaymentEmail from "@lib/emails/templates/attendee-awaiting-payment-email";
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { getErrorFromUnknown } from "@calcom/lib/errors";
|
|||||||
import { serverConfig } from "@calcom/lib/serverConfig";
|
import { serverConfig } from "@calcom/lib/serverConfig";
|
||||||
|
|
||||||
export default class BaseEmail {
|
export default class BaseEmail {
|
||||||
name: string = "";
|
name = "";
|
||||||
|
|
||||||
protected getNodeMailerPayload(): Record<string, unknown> {
|
protected getNodeMailerPayload(): Record<string, unknown> {
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ ${this.getWhen()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +98,7 @@ ${this.getAdditionalNotes()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ ${this.getWhen()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
${this.calEvent.cancellationReason && this.getCancellationReason()}
|
${this.calEvent.cancellationReason && this.getCancellationReason()}
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
}
|
}
|
||||||
@@ -98,6 +99,7 @@ ${this.calEvent.cancellationReason && this.getCancellationReason()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
${this.calEvent.cancellationReason && this.getCancellationReason()}
|
${this.calEvent.cancellationReason && this.getCancellationReason()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ ${this.getWhen()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
${this.getRejectionReason()}
|
${this.getRejectionReason()}
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
}
|
}
|
||||||
@@ -102,6 +103,7 @@ ${this.getRejectionReason()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
${this.getRejectionReason()}
|
${this.getRejectionReason()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ ${this.getWhen()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +120,7 @@ ${this.getAdditionalNotes()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ ${this.calEvent.organizer.language.translate("request_reschedule_subtitle", {
|
|||||||
${this.getWhat()}
|
${this.getWhat()}
|
||||||
${this.getWhen()}
|
${this.getWhen()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
${this.calEvent.organizer.language.translate("need_to_reschedule_or_cancel")}
|
${this.calEvent.organizer.language.translate("need_to_reschedule_or_cancel")}
|
||||||
${getCancelLink(this.calEvent)}
|
${getCancelLink(this.calEvent)}
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
@@ -154,6 +155,7 @@ ${getCancelLink(this.calEvent)}
|
|||||||
${this.getWhen()}
|
${this.getWhen()}
|
||||||
${this.getWho()}
|
${this.getWho()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ export default class AttendeeRescheduledEmail extends AttendeeScheduledEmail {
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
${this.attendee.language.translate("need_to_reschedule_or_cancel")}
|
${this.attendee.language.translate("need_to_reschedule_or_cancel")}
|
||||||
${getCancelLink(this.calEvent)}
|
${getCancelLink(this.calEvent)}
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
@@ -69,6 +70,7 @@ ${this.getWhat()}
|
|||||||
${this.getWhen()}
|
${this.getWhen()}
|
||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +118,7 @@ ${this.getAdditionalNotes()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ ${getRichDescription(this.calEvent)}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -316,6 +317,28 @@ ${getRichDescription(this.calEvent)}
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getCustomInputs(): string {
|
||||||
|
const { customInputs } = this.calEvent;
|
||||||
|
if (!customInputs) return "";
|
||||||
|
const customInputsString = Object.keys(customInputs)
|
||||||
|
.map((key) => {
|
||||||
|
if (customInputs[key] !== "") {
|
||||||
|
return `
|
||||||
|
<p style="height: 6px"></p>
|
||||||
|
<div style="line-height: 6px;">
|
||||||
|
<p style="color: #494949;">${key}</p>
|
||||||
|
<p style="color: #494949; font-weight: 400;">
|
||||||
|
${customInputs[key]}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
return customInputsString;
|
||||||
|
}
|
||||||
|
|
||||||
protected getRejectionReason(): string {
|
protected getRejectionReason(): string {
|
||||||
if (!this.calEvent.rejectionReason) return "";
|
if (!this.calEvent.rejectionReason) return "";
|
||||||
return `
|
return `
|
||||||
@@ -405,6 +428,12 @@ ${getRichDescription(this.calEvent)}
|
|||||||
<p style="color: #494949; font-weight: 400; line-height: 24px;">${
|
<p style="color: #494949; font-weight: 400; line-height: 24px;">${
|
||||||
providerName || this.calEvent.location
|
providerName || this.calEvent.location
|
||||||
}</p>
|
}</p>
|
||||||
|
<p style="color: #494949; font-weight: 400; line-height: 24px;">${
|
||||||
|
(providerName === "Zoom" || providerName === "Google") &&
|
||||||
|
`
|
||||||
|
${this.calEvent.organizer.language.translate("meeting_url_provided_after_confirmed")}
|
||||||
|
`
|
||||||
|
}</p>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ ${this.getWhen()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
${this.calEvent.cancellationReason && this.getCancellationReason()}
|
${this.calEvent.cancellationReason && this.getCancellationReason()}
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
}
|
}
|
||||||
@@ -106,6 +107,7 @@ ${this.calEvent.cancellationReason && this.getCancellationReason()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
${this.calEvent.cancellationReason && this.getCancellationReason()}
|
${this.calEvent.cancellationReason && this.getCancellationReason()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ ${this.getWhen()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +140,7 @@ ${this.getAdditionalNotes()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ ${this.getWhen()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
${this.calEvent.organizer.language.translate("confirm_or_reject_request")}
|
${this.calEvent.organizer.language.translate("confirm_or_reject_request")}
|
||||||
${process.env.NEXT_PUBLIC_WEBAPP_URL} + "/bookings/upcoming"
|
${process.env.NEXT_PUBLIC_WEBAPP_URL} + "/bookings/upcoming"
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
@@ -110,6 +111,7 @@ ${process.env.NEXT_PUBLIC_WEBAPP_URL} + "/bookings/upcoming"
|
|||||||
${this.getWho()}
|
${this.getWho()}
|
||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ ${this.getWhen()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
${this.calEvent.organizer.language.translate("confirm_or_reject_request")}
|
${this.calEvent.organizer.language.translate("confirm_or_reject_request")}
|
||||||
${process.env.NEXT_PUBLIC_WEBAPP_URL} + "/bookings/upcoming"
|
${process.env.NEXT_PUBLIC_WEBAPP_URL} + "/bookings/upcoming"
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
@@ -108,6 +109,7 @@ ${process.env.NEXT_PUBLIC_WEBAPP_URL} + "/bookings/upcoming"
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ ${this.getWhat()}
|
|||||||
${this.getWhen()}
|
${this.getWhen()}
|
||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
${this.calEvent.organizer.language.translate("need_to_reschedule_or_cancel")}
|
${this.calEvent.organizer.language.translate("need_to_reschedule_or_cancel")}
|
||||||
${getCancelLink(this.calEvent)}
|
${getCancelLink(this.calEvent)}
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
@@ -166,6 +167,7 @@ ${getCancelLink(this.calEvent)}
|
|||||||
${this.getWhen()}
|
${this.getWhen()}
|
||||||
${this.getWho()}
|
${this.getWho()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ ${this.getWhen()}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
${this.calEvent.organizer.language.translate("need_to_reschedule_or_cancel")}
|
${this.calEvent.organizer.language.translate("need_to_reschedule_or_cancel")}
|
||||||
${getCancelLink(this.calEvent)}
|
${getCancelLink(this.calEvent)}
|
||||||
`.replace(/(<([^>]+)>)/gi, "");
|
`.replace(/(<([^>]+)>)/gi, "");
|
||||||
@@ -113,6 +114,7 @@ ${getCancelLink(this.calEvent)}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ ${getRichDescription(this.calEvent)}
|
|||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getDescription()}
|
${this.getDescription()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
|
${this.getCustomInputs()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -303,6 +304,28 @@ ${getRichDescription(this.calEvent)}
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getCustomInputs(): string {
|
||||||
|
const { customInputs } = this.calEvent;
|
||||||
|
if (!customInputs) return "";
|
||||||
|
const customInputsString = Object.keys(customInputs)
|
||||||
|
.map((key) => {
|
||||||
|
if (customInputs[key] !== "") {
|
||||||
|
return `
|
||||||
|
<p style="height: 6px"></p>
|
||||||
|
<div style="line-height: 6px;">
|
||||||
|
<p style="color: #494949;">${key}</p>
|
||||||
|
<p style="color: #494949; font-weight: 400;">
|
||||||
|
${customInputs[key]}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
return customInputsString;
|
||||||
|
}
|
||||||
|
|
||||||
protected getDescription(): string {
|
protected getDescription(): string {
|
||||||
if (!this.calEvent.description) return "";
|
if (!this.calEvent.description) return "";
|
||||||
return `
|
return `
|
||||||
@@ -333,16 +356,16 @@ ${getRichDescription(this.calEvent)}
|
|||||||
const meetingId = this.calEvent.videoCallData.id;
|
const meetingId = this.calEvent.videoCallData.id;
|
||||||
const meetingPassword = this.calEvent.videoCallData.password;
|
const meetingPassword = this.calEvent.videoCallData.password;
|
||||||
const meetingUrl = this.calEvent.videoCallData.url;
|
const meetingUrl = this.calEvent.videoCallData.url;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<p style="height: 6px"></p>
|
<p style="height: 6px"></p>
|
||||||
<div style="line-height: 6px;">
|
<div style="line-height: 6px;">
|
||||||
<p style="color: #494949;">${this.calEvent.organizer.language.translate("where")}</p>
|
<p style="color: #494949;">${this.calEvent.organizer.language.translate("where")}</p>
|
||||||
<p style="color: #494949; font-weight: 400; line-height: 24px;">${providerName} ${
|
<p style="color: #494949; font-weight: 400; line-height: 24px;">${providerName}
|
||||||
|
${
|
||||||
meetingUrl &&
|
meetingUrl &&
|
||||||
`<a href="${meetingUrl}" target="_blank" alt="${this.calEvent.organizer.language.translate(
|
`<a href="${meetingUrl}" target="_blank" alt="${this.calEvent.organizer.language.translate(
|
||||||
"meeting_url"
|
"meeting_url"
|
||||||
)}"><img src="${linkIcon()}" width="12px"></img></a>`
|
)}"><img src="${linkIcon()}" width="12px"/></a>`
|
||||||
}</p>
|
}</p>
|
||||||
${
|
${
|
||||||
meetingId &&
|
meetingId &&
|
||||||
@@ -370,7 +393,6 @@ ${getRichDescription(this.calEvent)}
|
|||||||
|
|
||||||
if (this.calEvent.additionInformation?.hangoutLink) {
|
if (this.calEvent.additionInformation?.hangoutLink) {
|
||||||
const hangoutLink: string = this.calEvent.additionInformation.hangoutLink;
|
const hangoutLink: string = this.calEvent.additionInformation.hangoutLink;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<p style="height: 6px"></p>
|
<p style="height: 6px"></p>
|
||||||
<div style="line-height: 6px;">
|
<div style="line-height: 6px;">
|
||||||
@@ -395,6 +417,12 @@ ${getRichDescription(this.calEvent)}
|
|||||||
<p style="color: #494949; font-weight: 400; line-height: 24px;">${
|
<p style="color: #494949; font-weight: 400; line-height: 24px;">${
|
||||||
providerName || this.calEvent.location
|
providerName || this.calEvent.location
|
||||||
}</p>
|
}</p>
|
||||||
|
<p style="color: #494949; font-weight: 400; line-height: 24px;">${
|
||||||
|
(providerName === "Zoom" || providerName === "Google") &&
|
||||||
|
`
|
||||||
|
${this.calEvent.organizer.language.translate("meeting_url_provided_after_confirmed")}
|
||||||
|
`
|
||||||
|
}</p>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ async function getBooking(prisma: PrismaClient, uid: string) {
|
|||||||
select: {
|
select: {
|
||||||
startTime: true,
|
startTime: true,
|
||||||
description: true,
|
description: true,
|
||||||
|
customInputs: true,
|
||||||
attendees: {
|
attendees: {
|
||||||
select: {
|
select: {
|
||||||
email: true,
|
email: true,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { useEmbedTheme } from "@calcom/embed-core";
|
import { useEmbedTheme } from "@calcom/embed-core";
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { EventType, PeriodType } from "@prisma/client";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
function isOutOfBounds(
|
||||||
|
time: dayjs.ConfigType,
|
||||||
|
{
|
||||||
|
periodType,
|
||||||
|
periodDays,
|
||||||
|
periodCountCalendarDays,
|
||||||
|
periodStartDate,
|
||||||
|
periodEndDate,
|
||||||
|
}: Pick<
|
||||||
|
EventType,
|
||||||
|
"periodType" | "periodDays" | "periodCountCalendarDays" | "periodStartDate" | "periodEndDate"
|
||||||
|
>
|
||||||
|
) {
|
||||||
|
const date = dayjs(time);
|
||||||
|
periodDays = periodDays || 0;
|
||||||
|
|
||||||
|
switch (periodType) {
|
||||||
|
case PeriodType.ROLLING: {
|
||||||
|
const periodRollingEndDay = periodCountCalendarDays
|
||||||
|
? dayjs().utcOffset(date.utcOffset()).add(periodDays, "days").endOf("day")
|
||||||
|
: dayjs().utcOffset(date.utcOffset()).businessDaysAdd(periodDays).endOf("day");
|
||||||
|
return date.endOf("day").isAfter(periodRollingEndDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
case PeriodType.RANGE: {
|
||||||
|
const periodRangeStartDay = dayjs(periodStartDate).utcOffset(date.utcOffset()).endOf("day");
|
||||||
|
const periodRangeEndDay = dayjs(periodEndDate).utcOffset(date.utcOffset()).endOf("day");
|
||||||
|
return date.endOf("day").isBefore(periodRangeStartDay) || date.endOf("day").isAfter(periodRangeEndDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
case PeriodType.UNLIMITED:
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default isOutOfBounds;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Team, User } from ".prisma/client";
|
import { Team, User } from "@prisma/client";
|
||||||
|
|
||||||
export function isSuccessRedirectAvailable(
|
export function isSuccessRedirectAvailable(
|
||||||
eventType: {
|
eventType: {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import dayjs, { Dayjs } from "dayjs";
|
|||||||
import { I18n } from "next-i18next";
|
import { I18n } from "next-i18next";
|
||||||
import { RRule } from "rrule";
|
import { RRule } from "rrule";
|
||||||
|
|
||||||
import { recurringEvent } from "@calcom/prisma/zod-utils";
|
|
||||||
import { RecurringEvent } from "@calcom/types/Calendar";
|
import { RecurringEvent } from "@calcom/types/Calendar";
|
||||||
|
|
||||||
import { detectBrowserTimeFormat } from "@lib/timeFormat";
|
import { detectBrowserTimeFormat } from "@lib/timeFormat";
|
||||||
@@ -29,6 +28,7 @@ export const parseRecurringDates = (
|
|||||||
}: { startDate: string | null | Dayjs; recurringEvent: RecurringEvent; recurringCount: number },
|
}: { startDate: string | null | Dayjs; recurringEvent: RecurringEvent; recurringCount: number },
|
||||||
i18n: I18n
|
i18n: I18n
|
||||||
): [string[], Date[]] => {
|
): [string[], Date[]] => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { count, ...restRecurringEvent } = recurringEvent;
|
const { count, ...restRecurringEvent } = recurringEvent;
|
||||||
const rule = new RRule({
|
const rule = new RRule({
|
||||||
...restRecurringEvent,
|
...restRecurringEvent,
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ const getSlots = ({ inviteeDate, frequency, minimumBookingNotice, workingHours,
|
|||||||
computedLocalWorkingHours.push(tempComputeTimeFrame);
|
computedLocalWorkingHours.push(tempComputeTimeFrame);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
computedLocalWorkingHours.forEach((item, index) => {
|
computedLocalWorkingHours.forEach((item) => {
|
||||||
slotsTimeFrameAvailable.push(...splitAvailableTime(item.startTime, item.endTime, frequency, eventLength));
|
slotsTimeFrameAvailable.push(...splitAvailableTime(item.startTime, item.endTime, frequency, eventLength));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+11
-10
@@ -1,6 +1,11 @@
|
|||||||
import { jitsuClient, JitsuClient } from "@jitsu/sdk-js";
|
import { jitsuClient, JitsuClient } from "@jitsu/sdk-js";
|
||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// eslint-disable-next-line no-var
|
||||||
|
var jitsu: JitsuClient | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumeration of all event types that are being sent
|
* Enumeration of all event types that are being sent
|
||||||
* to telemetry collection.
|
* to telemetry collection.
|
||||||
@@ -11,6 +16,7 @@ export const telemetryEventTypes = {
|
|||||||
bookingCancelled: "booking_cancelled",
|
bookingCancelled: "booking_cancelled",
|
||||||
importSubmitted: "import_submitted",
|
importSubmitted: "import_submitted",
|
||||||
googleLogin: "google_login",
|
googleLogin: "google_login",
|
||||||
|
login: "login",
|
||||||
samlLogin: "saml_login",
|
samlLogin: "saml_login",
|
||||||
samlConfig: "saml_config",
|
samlConfig: "saml_config",
|
||||||
embedView: "embed_view",
|
embedView: "embed_view",
|
||||||
@@ -48,7 +54,10 @@ function isLocalhost(host: string) {
|
|||||||
* Collects page parameters and makes sure no sensitive data made it to telemetry
|
* Collects page parameters and makes sure no sensitive data made it to telemetry
|
||||||
* @param route current next.js route
|
* @param route current next.js route
|
||||||
*/
|
*/
|
||||||
export function collectPageParameters(route?: string, extraData: Record<string, any> = {}): any {
|
export function collectPageParameters(
|
||||||
|
route?: string,
|
||||||
|
extraData: Record<string, unknown> = {}
|
||||||
|
): Record<string, unknown> {
|
||||||
const host = document.location.hostname;
|
const host = document.location.hostname;
|
||||||
const maskedHost = isLocalhost(host) ? "localhost" : "masked";
|
const maskedHost = isLocalhost(host) ? "localhost" : "masked";
|
||||||
//starts with ''
|
//starts with ''
|
||||||
@@ -77,13 +86,7 @@ function createTelemetryClient(): TelemetryClient {
|
|||||||
if (!window) {
|
if (!window) {
|
||||||
console.warn("Jitsu has been called during SSR, this scenario isn't supported yet");
|
console.warn("Jitsu has been called during SSR, this scenario isn't supported yet");
|
||||||
return;
|
return;
|
||||||
} else if (
|
} else if (!window["jitsu"]) {
|
||||||
// FIXME
|
|
||||||
// @ts-ignore
|
|
||||||
!window["jitsu"]
|
|
||||||
) {
|
|
||||||
// FIXME
|
|
||||||
// @ts-ignore
|
|
||||||
window["jitsu"] = jitsuClient({
|
window["jitsu"] = jitsuClient({
|
||||||
log_level: "ERROR",
|
log_level: "ERROR",
|
||||||
tracking_host: "https://t.calendso.com",
|
tracking_host: "https://t.calendso.com",
|
||||||
@@ -92,8 +95,6 @@ function createTelemetryClient(): TelemetryClient {
|
|||||||
capture_3rd_party_cookies: false,
|
capture_3rd_party_cookies: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// FIXME
|
|
||||||
// @ts-ignore
|
|
||||||
const res = callback(window["jitsu"]);
|
const res = callback(window["jitsu"]);
|
||||||
if (res && typeof res["catch"] === "function") {
|
if (res && typeof res["catch"] === "function") {
|
||||||
res.catch((e) => {
|
res.catch((e) => {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export type BookingCreateBody = {
|
|||||||
timeZone: string;
|
timeZone: string;
|
||||||
user?: string | string[];
|
user?: string | string[];
|
||||||
language: string;
|
language: string;
|
||||||
customInputs: { label: string; value: string }[];
|
customInputs: { label: string; value: string | boolean }[];
|
||||||
metadata: {
|
metadata: {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
};
|
};
|
||||||
|
|||||||
+45
-46
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@calcom/web",
|
"name": "@calcom/web",
|
||||||
"version": "1.6",
|
"version": "1.6.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"analyze": "ANALYZE=true next build",
|
"analyze": "ANALYZE=true next build",
|
||||||
@@ -30,21 +30,21 @@
|
|||||||
"@calcom/app-store": "*",
|
"@calcom/app-store": "*",
|
||||||
"@calcom/core": "*",
|
"@calcom/core": "*",
|
||||||
"@calcom/ee": "*",
|
"@calcom/ee": "*",
|
||||||
|
"@calcom/embed-core": "*",
|
||||||
"@calcom/lib": "*",
|
"@calcom/lib": "*",
|
||||||
"@calcom/prisma": "*",
|
"@calcom/prisma": "*",
|
||||||
"@calcom/stripe": "*",
|
"@calcom/stripe": "*",
|
||||||
"@calcom/tsconfig": "*",
|
"@calcom/tsconfig": "*",
|
||||||
"@calcom/ui": "*",
|
"@calcom/ui": "*",
|
||||||
"@calcom/embed-core": "*",
|
"@daily-co/daily-js": "^0.26.0",
|
||||||
"@daily-co/daily-js": "^0.21.0",
|
|
||||||
"@glidejs/glide": "^3.5.2",
|
"@glidejs/glide": "^3.5.2",
|
||||||
"@heroicons/react": "^1.0.6",
|
"@heroicons/react": "^1.0.6",
|
||||||
"@hookform/error-message": "^2.0.0",
|
"@hookform/error-message": "^2.0.0",
|
||||||
"@hookform/resolvers": "^2.8.5",
|
"@hookform/resolvers": "^2.8.9",
|
||||||
"@jitsu/sdk-js": "^2.2.4",
|
"@jitsu/sdk-js": "^2.2.4",
|
||||||
"@metamask/providers": "^8.1.1",
|
"@metamask/providers": "^8.1.1",
|
||||||
"@next-auth/prisma-adapter": "^1.0.3",
|
"@next-auth/prisma-adapter": "^1.0.3",
|
||||||
"@next/bundle-analyzer": "12.1.0",
|
"@next/bundle-analyzer": "12.1.6",
|
||||||
"@radix-ui/react-avatar": "^0.1.0",
|
"@radix-ui/react-avatar": "^0.1.0",
|
||||||
"@radix-ui/react-collapsible": "^0.1.0",
|
"@radix-ui/react-collapsible": "^0.1.0",
|
||||||
"@radix-ui/react-dialog": "^0.1.0",
|
"@radix-ui/react-dialog": "^0.1.0",
|
||||||
@@ -54,20 +54,20 @@
|
|||||||
"@radix-ui/react-slider": "^0.1.1",
|
"@radix-ui/react-slider": "^0.1.1",
|
||||||
"@radix-ui/react-switch": "^0.1.1",
|
"@radix-ui/react-switch": "^0.1.1",
|
||||||
"@radix-ui/react-tooltip": "^0.1.0",
|
"@radix-ui/react-tooltip": "^0.1.0",
|
||||||
"@stripe/react-stripe-js": "^1.4.1",
|
"@stripe/react-stripe-js": "^1.8.0",
|
||||||
"@stripe/stripe-js": "^1.16.0",
|
"@stripe/stripe-js": "^1.29.0",
|
||||||
"@trpc/client": "^9.22.0",
|
"@trpc/client": "^9.23.4",
|
||||||
"@trpc/next": "^9.22.0",
|
"@trpc/next": "^9.23.4",
|
||||||
"@trpc/react": "^9.22.0",
|
"@trpc/react": "^9.23.4",
|
||||||
"@trpc/server": "^9.22.0",
|
"@trpc/server": "^9.23.4",
|
||||||
"@vercel/edge-functions-ui": "^0.2.1",
|
"@vercel/edge-functions-ui": "^0.2.1",
|
||||||
"@wojtekmaj/react-daterange-picker": "^3.3.1",
|
"@wojtekmaj/react-daterange-picker": "^3.3.1",
|
||||||
"accept-language-parser": "^1.5.0",
|
"accept-language-parser": "^1.5.0",
|
||||||
"async": "^3.2.1",
|
"async": "^3.2.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.11.2",
|
||||||
"dayjs-business-time": "^1.0.4",
|
"dayjs-business-days2": "^1.1.0",
|
||||||
"googleapis": "^84.0.0",
|
"googleapis": "^84.0.0",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
@@ -78,78 +78,77 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"micro": "^9.3.4",
|
"micro": "^9.3.4",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"next": "^12.1.0",
|
"next": "^12.1.6",
|
||||||
"next-auth": "^4.3.3",
|
"next-auth": "^4.3.4",
|
||||||
"next-i18next": "^8.9.0",
|
"next-i18next": "^11.0.0",
|
||||||
"next-mdx-remote": "^4.0.2",
|
"next-mdx-remote": "^4.0.3",
|
||||||
"next-seo": "^4.26.0",
|
"next-seo": "^4.26.0",
|
||||||
"next-transpile-modules": "^9.0.0",
|
"next-transpile-modules": "^9.0.0",
|
||||||
"nodemailer": "^6.7.2",
|
"nodemailer": "^6.7.5",
|
||||||
"otplib": "^12.0.1",
|
"otplib": "^12.0.1",
|
||||||
"qrcode": "^1.5.0",
|
"qrcode": "^1.5.0",
|
||||||
"react": "^17.0.2",
|
"react": "^18.1.0",
|
||||||
"react-colorful": "^5.5.1",
|
"react-colorful": "^5.5.1",
|
||||||
"react-date-picker": "^8.3.6",
|
"react-date-picker": "^8.3.6",
|
||||||
"react-digit-input": "^2.1.0",
|
"react-digit-input": "^2.1.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^18.1.0",
|
||||||
"react-easy-crop": "^3.5.2",
|
"react-easy-crop": "^3.5.2",
|
||||||
"react-hook-form": "^7.29.0",
|
"react-hook-form": "^7.31.1",
|
||||||
"react-hot-toast": "^2.1.0",
|
"react-hot-toast": "^2.1.0",
|
||||||
"react-intl": "^5.22.0",
|
"react-intl": "^5.25.1",
|
||||||
"react-live-chat-loader": "^2.7.3",
|
"react-live-chat-loader": "^2.7.3",
|
||||||
"react-multi-email": "^0.5.3",
|
"react-multi-email": "^0.5.3",
|
||||||
"react-phone-number-input": "^3.1.41",
|
"react-phone-number-input": "^3.1.52",
|
||||||
"react-query": "^3.33.7",
|
"react-query": "^3.39.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-select": "^5.3.2",
|
||||||
"react-select": "^5.2.1",
|
|
||||||
"react-timezone-select": "^1.3.1",
|
"react-timezone-select": "^1.3.1",
|
||||||
"react-use-intercom": "1.4.0",
|
"react-use-intercom": "1.5.1",
|
||||||
"react-virtualized-auto-sizer": "^1.0.6",
|
"react-virtualized-auto-sizer": "^1.0.6",
|
||||||
"react-window": "^1.8.6",
|
"react-window": "^1.8.7",
|
||||||
"rrule": "^2.6.9",
|
"rrule": "^2.6.9",
|
||||||
"short-uuid": "^4.2.0",
|
"short-uuid": "^4.2.0",
|
||||||
"stripe": "^8.191.0",
|
"stripe": "^9.1.0",
|
||||||
"superjson": "1.8.1",
|
"superjson": "1.9.1",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"web3": "^1.6.1",
|
"web3": "^1.7.3",
|
||||||
"zod": "^3.14.4"
|
"zod": "^3.16.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.17.8",
|
"@babel/core": "^7.17.10",
|
||||||
"@calcom/config": "*",
|
"@calcom/config": "*",
|
||||||
"@calcom/types": "*",
|
"@calcom/types": "*",
|
||||||
"@microsoft/microsoft-graph-types-beta": "0.15.0-preview",
|
"@microsoft/microsoft-graph-types-beta": "0.15.0-preview",
|
||||||
"@playwright/test": "^1.18.1",
|
"@playwright/test": "^1.22.1",
|
||||||
"@types/accept-language-parser": "1.5.2",
|
"@types/accept-language-parser": "1.5.2",
|
||||||
"@types/async": "^3.2.10",
|
"@types/async": "^3.2.13",
|
||||||
"@types/bcryptjs": "^2.4.2",
|
"@types/bcryptjs": "^2.4.2",
|
||||||
"@types/glidejs__glide": "^3.4.1",
|
"@types/glidejs__glide": "^3.4.2",
|
||||||
"@types/jest": "^27.0.3",
|
"@types/jest": "^27.5.1",
|
||||||
"@types/lodash": "^4.14.177",
|
"@types/lodash": "^4.14.182",
|
||||||
"@types/micro": "^7.3.6",
|
"@types/micro": "7.3.6",
|
||||||
"@types/mime-types": "^2.1.1",
|
"@types/mime-types": "^2.1.1",
|
||||||
"@types/module-alias": "^2.0.1",
|
"@types/module-alias": "^2.0.1",
|
||||||
"@types/node": "^16.11.24",
|
"@types/node": "14.17.6",
|
||||||
"@types/nodemailer": "^6.4.4",
|
"@types/nodemailer": "^6.4.4",
|
||||||
"@types/qrcode": "^1.4.1",
|
"@types/qrcode": "^1.4.1",
|
||||||
"@types/react": "^17.0.37",
|
"@types/react": "18.0.9",
|
||||||
"@types/react-phone-number-input": "^3.0.13",
|
"@types/react-phone-number-input": "^3.0.13",
|
||||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||||
"@types/react-window": "^1.8.5",
|
"@types/react-window": "^1.8.5",
|
||||||
"@types/stripe": "^8.0.417",
|
"@types/stripe": "^8.0.417",
|
||||||
"@types/uuid": "8.3.1",
|
"@types/uuid": "8.3.1",
|
||||||
"autoprefixer": "^10.4.0",
|
"autoprefixer": "^10.4.7",
|
||||||
"babel-jest": "^27.3.1",
|
"babel-jest": "^27.3.1",
|
||||||
"env-cmd": "10.1.0",
|
"env-cmd": "10.1.0",
|
||||||
"eslint": "^8.10.0",
|
"eslint": "^8.15.0",
|
||||||
"jest": "^26.0.0",
|
"jest": "^26.0.0",
|
||||||
"mockdate": "^3.0.5",
|
"mockdate": "^3.0.5",
|
||||||
"module-alias": "^2.2.2",
|
"module-alias": "^2.2.2",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"postcss": "^8.4.4",
|
"postcss": "^8.4.13",
|
||||||
"tailwindcss": "^3.0.23",
|
"tailwindcss": "^3.0.24",
|
||||||
"ts-jest": "^26.0.0",
|
"ts-jest": "^26.0.0",
|
||||||
"ts-node": "^10.6.0",
|
"ts-node": "^10.6.0",
|
||||||
"typescript": "^4.5.3"
|
"typescript": "^4.6.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ export default function Custom404() {
|
|||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const username = router.asPath.replace("%20", "-");
|
const username = router.asPath.replace("%20", "-").split(/[?#]/)[0];
|
||||||
|
|
||||||
const links = [
|
const links = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { GetServerSidePropsContext } from "next";
|
|||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Toaster } from "react-hot-toast";
|
import { Toaster } from "react-hot-toast";
|
||||||
import { JSONObject } from "superjson/dist/types";
|
import { JSONObject } from "superjson/dist/types";
|
||||||
|
|
||||||
@@ -18,7 +18,6 @@ import defaultEvents, {
|
|||||||
getUsernameSlugLink,
|
getUsernameSlugLink,
|
||||||
} from "@calcom/lib/defaultEvents";
|
} from "@calcom/lib/defaultEvents";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { RecurringEvent } from "@calcom/types/Calendar";
|
|
||||||
|
|
||||||
import { useExposePlanGlobally } from "@lib/hooks/useExposePlanGlobally";
|
import { useExposePlanGlobally } from "@lib/hooks/useExposePlanGlobally";
|
||||||
import useTheme from "@lib/hooks/useTheme";
|
import useTheme from "@lib/hooks/useTheme";
|
||||||
@@ -71,7 +70,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
|
|||||||
<div className="overflow-hidden rounded-sm border dark:border-gray-900">
|
<div className="overflow-hidden rounded-sm border dark:border-gray-900">
|
||||||
<div className="p-8 text-center text-gray-400 dark:text-white">
|
<div className="p-8 text-center text-gray-400 dark:text-white">
|
||||||
<h2 className="font-cal mb-2 text-3xl text-gray-600 dark:text-white">{" " + t("unavailable")}</h2>
|
<h2 className="font-cal mb-2 text-3xl text-gray-600 dark:text-white">{" " + t("unavailable")}</h2>
|
||||||
<p className="mx-auto max-w-md">{t("user_dynamic_booking_disabled")}</p>
|
<p className="mx-auto max-w-md">{t("user_dynamic_booking_disabled") as string}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -80,7 +79,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
|
|||||||
{eventTypes.map((type, index) => (
|
{eventTypes.map((type, index) => (
|
||||||
<li
|
<li
|
||||||
key={index}
|
key={index}
|
||||||
className="hover:border-brand group relative rounded-sm border border-neutral-200 bg-white hover:bg-gray-50 dark:border-0 dark:bg-neutral-900 dark:hover:border-neutral-600">
|
className="hover:border-brand group relative rounded-sm border border-neutral-200 bg-white hover:bg-gray-50 dark:border-neutral-700 dark:bg-gray-800 dark:hover:border-neutral-600">
|
||||||
<ArrowRightIcon className="absolute right-3 top-3 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
|
<ArrowRightIcon className="absolute right-3 top-3 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
|
||||||
<Link href={getUsernameSlugLink({ users: props.users, slug: type.slug })}>
|
<Link href={getUsernameSlugLink({ users: props.users, 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">
|
||||||
@@ -167,7 +166,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
|
|||||||
<h2 className="font-cal mb-2 text-3xl text-gray-600 dark:text-white">
|
<h2 className="font-cal mb-2 text-3xl text-gray-600 dark:text-white">
|
||||||
😴{" " + t("user_away")}
|
😴{" " + t("user_away")}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mx-auto max-w-md">{t("user_away_description")}</p>
|
<p className="mx-auto max-w-md">{t("user_away_description") as string}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : isDynamicGroup ? ( //When we deal with dynamic group (users > 1)
|
) : isDynamicGroup ? ( //When we deal with dynamic group (users > 1)
|
||||||
@@ -225,8 +224,10 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
|
|||||||
{eventTypes.length === 0 && (
|
{eventTypes.length === 0 && (
|
||||||
<div className="overflow-hidden rounded-sm border dark:border-gray-900">
|
<div className="overflow-hidden rounded-sm border dark:border-gray-900">
|
||||||
<div className="p-8 text-center text-gray-400 dark:text-white">
|
<div className="p-8 text-center text-gray-400 dark:text-white">
|
||||||
<h2 className="font-cal mb-2 text-3xl text-gray-600 dark:text-white">{t("uh_oh")}</h2>
|
<h2 className="font-cal mb-2 text-3xl text-gray-600 dark:text-white">
|
||||||
<p className="mx-auto max-w-md">{t("no_event_types_have_been_setup")}</p>
|
{t("uh_oh") as string}
|
||||||
|
</h2>
|
||||||
|
<p className="mx-auto max-w-md">{t("no_event_types_have_been_setup") as string}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -287,7 +288,7 @@ const getEventTypesWithHiddenFromDB = async (userId: number, plan: UserPlan) =>
|
|||||||
|
|
||||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||||
const ssr = await ssrInit(context);
|
const ssr = await ssrInit(context);
|
||||||
const crypto = require("crypto");
|
const crypto = await import("crypto");
|
||||||
|
|
||||||
const usernameList = getUsernameList(context.query.user as string);
|
const usernameList = getUsernameList(context.query.user as string);
|
||||||
const dataFetchStart = Date.now();
|
const dataFetchStart = Date.now();
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { DefaultSeo } from "next-seo";
|
import { DefaultSeo } from "next-seo";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { useEffect } from "react";
|
|
||||||
// import { ReactQueryDevtools } from "react-query/devtools";
|
|
||||||
import superjson from "superjson";
|
import superjson from "superjson";
|
||||||
|
|
||||||
import "@calcom/embed-core/src/embed-iframe";
|
import "@calcom/embed-core/src/embed-iframe";
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const CustomError: NextPage<CustomErrorProps> = (props) => {
|
|||||||
// getInitialProps is not called in case of
|
// getInitialProps is not called in case of
|
||||||
// https://github.com/vercel/next.js/issues/8592. As a workaround, we pass
|
// https://github.com/vercel/next.js/issues/8592. As a workaround, we pass
|
||||||
// err via _app.tsx so it can be captured
|
// err via _app.tsx so it can be captured
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const e = getErrorFromUnknown(err);
|
const e = getErrorFromUnknown(err);
|
||||||
// can be captured here
|
// can be captured here
|
||||||
// e.g. Sentry.captureException(e);
|
// e.g. Sentry.captureException(e);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { PrismaAdapter } from "@next-auth/prisma-adapter";
|
|
||||||
import { IdentityProvider, UserPermissionRole } from "@prisma/client";
|
import { IdentityProvider, UserPermissionRole } from "@prisma/client";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import Handlebars from "handlebars";
|
import Handlebars from "handlebars";
|
||||||
@@ -185,6 +184,7 @@ if (true) {
|
|||||||
}
|
}
|
||||||
const calcomAdapter = CalComAdapter(prisma);
|
const calcomAdapter = CalComAdapter(prisma);
|
||||||
export default NextAuth({
|
export default NextAuth({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
adapter: calcomAdapter,
|
adapter: calcomAdapter,
|
||||||
session: {
|
session: {
|
||||||
@@ -203,6 +203,7 @@ export default NextAuth({
|
|||||||
const autoMergeIdentities = async () => {
|
const autoMergeIdentities = async () => {
|
||||||
if (!hostedCal) {
|
if (!hostedCal) {
|
||||||
const existingUser = await prisma.user.findFirst({
|
const existingUser = await prisma.user.findFirst({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
where: { email: token.email! },
|
where: { email: token.email! },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { OAuthReqBody } from "@boxyhq/saml-jackson";
|
import { OAuthReqBody } from "@boxyhq/saml-jackson";
|
||||||
import { NextApiRequest, NextApiResponse } from "next";
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
|
|
||||||
import jackson from "@lib/jackson";
|
import jackson from "@lib/jackson";
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
@@ -12,10 +14,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const { oauthController } = await jackson();
|
const { oauthController } = await jackson();
|
||||||
const { redirect_url } = await oauthController.authorize(req.query as unknown as OAuthReqBody);
|
const { redirect_url } = await oauthController.authorize(req.query as unknown as OAuthReqBody);
|
||||||
res.redirect(302, redirect_url);
|
res.redirect(302, redirect_url);
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
|
if (err instanceof HttpError) {
|
||||||
console.error("authorize error:", err);
|
console.error("authorize error:", err);
|
||||||
const { message, statusCode = 500 } = err;
|
const { message, statusCode = 500 } = err;
|
||||||
|
return res.status(statusCode).send(message);
|
||||||
res.status(statusCode).send(message);
|
}
|
||||||
|
return res.status(500).send("Unknown error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from "next";
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
|
|
||||||
import jackson from "@lib/jackson";
|
import jackson from "@lib/jackson";
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
@@ -12,10 +14,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const { redirect_url } = await oauthController.samlResponse(req.body);
|
const { redirect_url } = await oauthController.samlResponse(req.body);
|
||||||
|
|
||||||
res.redirect(302, redirect_url);
|
res.redirect(302, redirect_url);
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
|
if (err instanceof HttpError) {
|
||||||
console.error("callback error:", err);
|
console.error("callback error:", err);
|
||||||
const { message, statusCode = 500 } = err;
|
const { message, statusCode = 500 } = err;
|
||||||
|
return res.status(statusCode).send(message);
|
||||||
res.status(statusCode).send(message);
|
}
|
||||||
|
return res.status(500).send("Unknown error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const user = asStringOrNull(req.query.user);
|
const user = asStringOrNull(req.query.user);
|
||||||
const dateFrom = dayjs(asStringOrNull(req.query.dateFrom));
|
const dateFrom = dayjs(asStringOrNull(req.query.dateFrom));
|
||||||
const dateTo = dayjs(asStringOrNull(req.query.dateTo));
|
const dateTo = dayjs(asStringOrNull(req.query.dateTo));
|
||||||
const eventTypeId = parseInt(asStringOrNull(req.query.eventTypeId) || "");
|
const eventTypeId = typeof req.query.eventTypeId === "string" ? parseInt(req.query.eventTypeId) : undefined;
|
||||||
|
|
||||||
if (!dateFrom.isValid() || !dateTo.isValid()) {
|
if (!dateFrom.isValid() || !dateTo.isValid()) {
|
||||||
return res.status(400).json({ message: "Invalid time range given." });
|
return res.status(400).json({ message: "Invalid time range given." });
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
import { Prisma, User, Booking, SchedulingType, BookingStatus } from "@prisma/client";
|
import { Booking, BookingStatus, Prisma, SchedulingType, User } from "@prisma/client";
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
import rrule from "rrule";
|
|
||||||
|
|
||||||
import EventManager from "@calcom/core/EventManager";
|
import EventManager from "@calcom/core/EventManager";
|
||||||
|
import { isPrismaObjOrUndefined } from "@calcom/lib";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import type { AdditionInformation, RecurringEvent } from "@calcom/types/Calendar";
|
import type { AdditionInformation, CalendarEvent, RecurringEvent } from "@calcom/types/Calendar";
|
||||||
import type { CalendarEvent } from "@calcom/types/Calendar";
|
|
||||||
import { refund } from "@ee/lib/stripe/server";
|
import { refund } from "@ee/lib/stripe/server";
|
||||||
|
|
||||||
import { asStringOrNull } from "@lib/asStringOrNull";
|
import { asStringOrNull } from "@lib/asStringOrNull";
|
||||||
import { getSession } from "@lib/auth";
|
import { getSession } from "@lib/auth";
|
||||||
import { sendDeclinedEmails } from "@lib/emails/email-manager";
|
import { sendDeclinedEmails, sendScheduledEmails } from "@lib/emails/email-manager";
|
||||||
import { sendScheduledEmails } from "@lib/emails/email-manager";
|
|
||||||
import prisma from "@lib/prisma";
|
import prisma from "@lib/prisma";
|
||||||
import { BookingConfirmBody } from "@lib/types/booking";
|
import { BookingConfirmBody } from "@lib/types/booking";
|
||||||
|
|
||||||
@@ -90,6 +88,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
select: {
|
select: {
|
||||||
title: true,
|
title: true,
|
||||||
description: true,
|
description: true,
|
||||||
|
customInputs: true,
|
||||||
startTime: true,
|
startTime: true,
|
||||||
endTime: true,
|
endTime: true,
|
||||||
confirmed: true,
|
confirmed: true,
|
||||||
@@ -157,6 +156,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
type: booking.title,
|
type: booking.title,
|
||||||
title: booking.title,
|
title: booking.title,
|
||||||
description: booking.description,
|
description: booking.description,
|
||||||
|
customInputs: isPrismaObjOrUndefined(booking.customInputs),
|
||||||
startTime: booking.startTime.toISOString(),
|
startTime: booking.startTime.toISOString(),
|
||||||
endTime: booking.endTime.toISOString(),
|
endTime: booking.endTime.toISOString(),
|
||||||
organizer: {
|
organizer: {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BookingStatus, Credential, Prisma, SchedulingType, WebhookTriggerEvents } from "@prisma/client";
|
import { BookingStatus, Credential, Prisma, SchedulingType, WebhookTriggerEvents } from "@prisma/client";
|
||||||
import async from "async";
|
import async from "async";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import dayjsBusinessTime from "dayjs-business-time";
|
import dayjsBusinessTime from "dayjs-business-days2";
|
||||||
import isBetween from "dayjs/plugin/isBetween";
|
import isBetween from "dayjs/plugin/isBetween";
|
||||||
import timezone from "dayjs/plugin/timezone";
|
import timezone from "dayjs/plugin/timezone";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
@@ -11,6 +11,7 @@ import short from "short-uuid";
|
|||||||
import { v5 as uuidv5 } from "uuid";
|
import { v5 as uuidv5 } from "uuid";
|
||||||
|
|
||||||
import EventManager from "@calcom/core/EventManager";
|
import EventManager from "@calcom/core/EventManager";
|
||||||
|
import { isPrismaObjOrUndefined } from "@calcom/lib";
|
||||||
import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defaultEvents";
|
import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defaultEvents";
|
||||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
@@ -28,6 +29,7 @@ import {
|
|||||||
import { ensureArray } from "@lib/ensureArray";
|
import { ensureArray } from "@lib/ensureArray";
|
||||||
import { getEventName } from "@lib/event";
|
import { getEventName } from "@lib/event";
|
||||||
import getBusyTimes from "@lib/getBusyTimes";
|
import getBusyTimes from "@lib/getBusyTimes";
|
||||||
|
import isOutOfBounds from "@lib/isOutOfBounds";
|
||||||
import prisma from "@lib/prisma";
|
import prisma from "@lib/prisma";
|
||||||
import { BookingCreateBody } from "@lib/types/booking";
|
import { BookingCreateBody } from "@lib/types/booking";
|
||||||
import sendPayload from "@lib/webhooks/sendPayload";
|
import sendPayload from "@lib/webhooks/sendPayload";
|
||||||
@@ -104,32 +106,6 @@ function isAvailable(busyTimes: BufferedBusyTimes, time: dayjs.ConfigType, lengt
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isOutOfBounds(
|
|
||||||
time: dayjs.ConfigType,
|
|
||||||
{ periodType, periodDays, periodCountCalendarDays, periodStartDate, periodEndDate, timeZone }: any // FIXME types
|
|
||||||
): boolean {
|
|
||||||
const date = dayjs(time);
|
|
||||||
|
|
||||||
switch (periodType) {
|
|
||||||
case "rolling": {
|
|
||||||
const periodRollingEndDay = periodCountCalendarDays
|
|
||||||
? dayjs().tz(timeZone).add(periodDays, "days").endOf("day")
|
|
||||||
: dayjs().tz(timeZone).addBusinessTime(periodDays, "days").endOf("day");
|
|
||||||
return date.endOf("day").isAfter(periodRollingEndDay);
|
|
||||||
}
|
|
||||||
|
|
||||||
case "range": {
|
|
||||||
const periodRangeStartDay = dayjs(periodStartDate).tz(timeZone).endOf("day");
|
|
||||||
const periodRangeEndDay = dayjs(periodEndDate).tz(timeZone).endOf("day");
|
|
||||||
return date.endOf("day").isBefore(periodRangeStartDay) || date.endOf("day").isAfter(periodRangeEndDay);
|
|
||||||
}
|
|
||||||
|
|
||||||
case "unlimited":
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const userSelect = Prisma.validator<Prisma.UserArgs>()({
|
const userSelect = Prisma.validator<Prisma.UserArgs>()({
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
@@ -348,18 +324,22 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
t: tOrganizer,
|
t: tOrganizer,
|
||||||
};
|
};
|
||||||
|
|
||||||
const additionalNotes =
|
const additionalNotes = reqBody.notes;
|
||||||
reqBody.notes +
|
|
||||||
reqBody.customInputs.reduce(
|
const customInputs = {} as NonNullable<CalendarEvent["customInputs"]>;
|
||||||
(str, input) => str + "<br /><br />" + input.label + ":<br />" + input.value,
|
|
||||||
""
|
if (reqBody.customInputs.length > 0) {
|
||||||
);
|
reqBody.customInputs.forEach(({ label, value }) => {
|
||||||
|
customInputs[label] = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const evt: CalendarEvent = {
|
const evt: CalendarEvent = {
|
||||||
type: eventType.title,
|
type: eventType.title,
|
||||||
title: getEventName(eventNameObject), //this needs to be either forced in english, or fetched for each attendee and organizer separately
|
title: getEventName(eventNameObject), //this needs to be either forced in english, or fetched for each attendee and organizer separately
|
||||||
description: eventType.description,
|
description: eventType.description,
|
||||||
additionalNotes,
|
additionalNotes,
|
||||||
|
customInputs,
|
||||||
startTime: reqBody.start,
|
startTime: reqBody.start,
|
||||||
endTime: reqBody.end,
|
endTime: reqBody.end,
|
||||||
organizer: {
|
organizer: {
|
||||||
@@ -456,6 +436,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
startTime: dayjs(evt.startTime).toDate(),
|
startTime: dayjs(evt.startTime).toDate(),
|
||||||
endTime: dayjs(evt.endTime).toDate(),
|
endTime: dayjs(evt.endTime).toDate(),
|
||||||
description: evt.additionalNotes,
|
description: evt.additionalNotes,
|
||||||
|
customInputs: isPrismaObjOrUndefined(evt.customInputs),
|
||||||
confirmed: (!eventType.requiresConfirmation && !eventType.price) || !!rescheduleUid,
|
confirmed: (!eventType.requiresConfirmation && !eventType.price) || !!rescheduleUid,
|
||||||
location: evt.location,
|
location: evt.location,
|
||||||
eventType: eventTypeRel,
|
eventType: eventTypeRel,
|
||||||
@@ -613,7 +594,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
periodEndDate: eventType.periodEndDate,
|
periodEndDate: eventType.periodEndDate,
|
||||||
periodStartDate: eventType.periodStartDate,
|
periodStartDate: eventType.periodStartDate,
|
||||||
periodCountCalendarDays: eventType.periodCountCalendarDays,
|
periodCountCalendarDays: eventType.periodCountCalendarDays,
|
||||||
timeZone: currentUser.timeZone,
|
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
log.debug({
|
log.debug({
|
||||||
@@ -731,6 +711,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
...evt,
|
...evt,
|
||||||
additionInformation: metadata,
|
additionInformation: metadata,
|
||||||
additionalNotes,
|
additionalNotes,
|
||||||
|
customInputs,
|
||||||
},
|
},
|
||||||
reqBody.recurringEventId ? (eventType.recurringEvent as RecurringEvent) : {}
|
reqBody.recurringEventId ? (eventType.recurringEvent as RecurringEvent) : {}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ const handler = async (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (bookingToReschedule.userId !== userOwner.id) throw new Error("UNAUTHORIZED");
|
||||||
|
|
||||||
if (bookingToReschedule && userOwner) {
|
if (bookingToReschedule && userOwner) {
|
||||||
let event: Partial<EventType> = {};
|
let event: Partial<EventType> = {};
|
||||||
if (bookingToReschedule.eventTypeId) {
|
if (bookingToReschedule.eventTypeId) {
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ import { NextApiRequest, NextApiResponse } from "next";
|
|||||||
import { FAKE_DAILY_CREDENTIAL } from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter";
|
import { FAKE_DAILY_CREDENTIAL } from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter";
|
||||||
import { getCalendar } from "@calcom/core/CalendarManager";
|
import { getCalendar } from "@calcom/core/CalendarManager";
|
||||||
import { deleteMeeting } from "@calcom/core/videoClient";
|
import { deleteMeeting } from "@calcom/core/videoClient";
|
||||||
|
import { isPrismaObjOrUndefined } from "@calcom/lib";
|
||||||
|
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||||
import type { CalendarEvent } from "@calcom/types/Calendar";
|
import type { CalendarEvent } from "@calcom/types/Calendar";
|
||||||
import { refund } from "@ee/lib/stripe/server";
|
import { refund } from "@ee/lib/stripe/server";
|
||||||
|
|
||||||
import { asStringOrNull } from "@lib/asStringOrNull";
|
import { asStringOrNull } from "@lib/asStringOrNull";
|
||||||
import { getSession } from "@lib/auth";
|
import { getSession } from "@lib/auth";
|
||||||
import { sendCancelledEmails } from "@lib/emails/email-manager";
|
import { sendCancelledEmails } from "@lib/emails/email-manager";
|
||||||
import prisma from "@lib/prisma";
|
|
||||||
import sendPayload from "@lib/webhooks/sendPayload";
|
import sendPayload from "@lib/webhooks/sendPayload";
|
||||||
import getWebhooks from "@lib/webhooks/subscriptions";
|
import getWebhooks from "@lib/webhooks/subscriptions";
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
uid,
|
uid,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
...bookingMinimalSelect,
|
||||||
userId: true,
|
userId: true,
|
||||||
user: {
|
user: {
|
||||||
select: {
|
select: {
|
||||||
@@ -45,7 +46,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
destinationCalendar: true,
|
destinationCalendar: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
attendees: true,
|
|
||||||
location: true,
|
location: true,
|
||||||
references: {
|
references: {
|
||||||
select: {
|
select: {
|
||||||
@@ -56,15 +56,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
},
|
},
|
||||||
payment: true,
|
payment: true,
|
||||||
paid: true,
|
paid: true,
|
||||||
title: true,
|
|
||||||
eventType: {
|
eventType: {
|
||||||
select: {
|
select: {
|
||||||
title: true,
|
title: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
description: true,
|
|
||||||
startTime: true,
|
|
||||||
endTime: true,
|
|
||||||
uid: true,
|
uid: true,
|
||||||
eventTypeId: true,
|
eventTypeId: true,
|
||||||
destinationCalendar: true,
|
destinationCalendar: true,
|
||||||
@@ -115,6 +111,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
title: bookingToDelete?.title,
|
title: bookingToDelete?.title,
|
||||||
type: (bookingToDelete?.eventType?.title as string) || bookingToDelete?.title,
|
type: (bookingToDelete?.eventType?.title as string) || bookingToDelete?.title,
|
||||||
description: bookingToDelete?.description || "",
|
description: bookingToDelete?.description || "",
|
||||||
|
customInputs: isPrismaObjOrUndefined(bookingToDelete.customInputs),
|
||||||
startTime: bookingToDelete?.startTime ? dayjs(bookingToDelete.startTime).format() : "",
|
startTime: bookingToDelete?.startTime ? dayjs(bookingToDelete.startTime).format() : "",
|
||||||
endTime: bookingToDelete?.endTime ? dayjs(bookingToDelete.endTime).format() : "",
|
endTime: bookingToDelete?.endTime ? dayjs(bookingToDelete.endTime).format() : "",
|
||||||
organizer: {
|
organizer: {
|
||||||
@@ -183,6 +180,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
type: bookingToDelete?.eventType?.title as string,
|
type: bookingToDelete?.eventType?.title as string,
|
||||||
title: bookingToDelete.title,
|
title: bookingToDelete.title,
|
||||||
description: bookingToDelete.description ?? "",
|
description: bookingToDelete.description ?? "",
|
||||||
|
customInputs: isPrismaObjOrUndefined(bookingToDelete.customInputs),
|
||||||
startTime: bookingToDelete.startTime.toISOString(),
|
startTime: bookingToDelete.startTime.toISOString(),
|
||||||
endTime: bookingToDelete.endTime.toISOString(),
|
endTime: bookingToDelete.endTime.toISOString(),
|
||||||
organizer: {
|
organizer: {
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import { ReminderType } from "@prisma/client";
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
import { isPrismaObjOrUndefined } from "@calcom/lib";
|
||||||
|
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||||
import type { CalendarEvent } from "@calcom/types/Calendar";
|
import type { CalendarEvent } from "@calcom/types/Calendar";
|
||||||
|
|
||||||
import { sendOrganizerRequestReminderEmail } from "@lib/emails/email-manager";
|
import { sendOrganizerRequestReminderEmail } from "@lib/emails/email-manager";
|
||||||
import prisma from "@lib/prisma";
|
|
||||||
|
|
||||||
import { getTranslation } from "@server/lib/i18n";
|
import { getTranslation } from "@server/lib/i18n";
|
||||||
|
|
||||||
@@ -32,12 +33,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
title: true,
|
...bookingMinimalSelect,
|
||||||
description: true,
|
|
||||||
location: true,
|
location: true,
|
||||||
startTime: true,
|
|
||||||
endTime: true,
|
|
||||||
attendees: true,
|
|
||||||
user: {
|
user: {
|
||||||
select: {
|
select: {
|
||||||
email: true,
|
email: true,
|
||||||
@@ -48,7 +45,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
destinationCalendar: true,
|
destinationCalendar: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
id: true,
|
|
||||||
uid: true,
|
uid: true,
|
||||||
destinationCalendar: true,
|
destinationCalendar: true,
|
||||||
},
|
},
|
||||||
@@ -94,6 +90,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
type: booking.title,
|
type: booking.title,
|
||||||
title: booking.title,
|
title: booking.title,
|
||||||
description: booking.description || undefined,
|
description: booking.description || undefined,
|
||||||
|
customInputs: isPrismaObjOrUndefined(booking.customInputs),
|
||||||
location: booking.location ?? "",
|
location: booking.location ?? "",
|
||||||
startTime: booking.startTime.toISOString(),
|
startTime: booking.startTime.toISOString(),
|
||||||
endTime: booking.endTime.toISOString(),
|
endTime: booking.endTime.toISOString(),
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
if (typeof handler !== "function")
|
if (typeof handler !== "function")
|
||||||
throw new HttpError({ statusCode: 404, message: `API handler not found` });
|
throw new HttpError({ statusCode: 404, message: `API handler not found` });
|
||||||
|
|
||||||
const response = await handler(req, res);
|
await handler(req, res);
|
||||||
|
|
||||||
return res.status(200);
|
return res.status(200);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Prisma } from "@prisma/client";
|
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
import { getStripeCustomerId } from "@calcom/stripe/customer";
|
import { getStripeCustomerId } from "@calcom/stripe/customer";
|
||||||
|
|||||||
@@ -63,8 +63,6 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<p>{t("password_has_been_reset_login")}</p>
|
<p>{t("password_has_been_reset_login")}</p>
|
||||||
{
|
|
||||||
// eslint-disable-next-line @next/next/link-passhref
|
|
||||||
<Link href="/auth/login">
|
<Link href="/auth/login">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -72,7 +70,6 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
|
|||||||
{t("login")}
|
{t("login")}
|
||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ export default function Login({
|
|||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const form = useForm<LoginValues>();
|
const form = useForm<LoginValues>();
|
||||||
|
const { formState } = form;
|
||||||
|
const { isSubmitting } = formState;
|
||||||
|
|
||||||
const [twoFactorRequired, setTwoFactorRequired] = useState(false);
|
const [twoFactorRequired, setTwoFactorRequired] = useState(false);
|
||||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||||
@@ -97,16 +99,20 @@ export default function Login({
|
|||||||
<AuthContainer
|
<AuthContainer
|
||||||
title={t("login")}
|
title={t("login")}
|
||||||
description={t("login")}
|
description={t("login")}
|
||||||
loading={form.formState.isSubmitting}
|
|
||||||
showLogo
|
showLogo
|
||||||
heading={twoFactorRequired ? t("2fa_code") : t("sign_in_account")}
|
heading={twoFactorRequired ? t("2fa_code") : t("sign_in_account")}
|
||||||
footerText={twoFactorRequired ? TwoFactorFooter : LoginFooter}>
|
footerText={twoFactorRequired ? TwoFactorFooter : LoginFooter}>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
className="space-y-6"
|
className="space-y-6"
|
||||||
handleSubmit={(values) => {
|
handleSubmit={async (values) => {
|
||||||
signIn<"credentials">("credentials", { ...values, callbackUrl, redirect: false })
|
setErrorMessage(null);
|
||||||
.then((res) => {
|
telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.login, collectPageParameters()));
|
||||||
|
const res = await signIn<"credentials">("credentials", {
|
||||||
|
...values,
|
||||||
|
callbackUrl,
|
||||||
|
redirect: false,
|
||||||
|
});
|
||||||
if (!res) setErrorMessage(errorMessages[ErrorCode.InternalServerError]);
|
if (!res) setErrorMessage(errorMessages[ErrorCode.InternalServerError]);
|
||||||
// we're logged in! let's do a hard refresh to the desired url
|
// we're logged in! let's do a hard refresh to the desired url
|
||||||
else if (!res.error) router.push(callbackUrl);
|
else if (!res.error) router.push(callbackUrl);
|
||||||
@@ -114,8 +120,6 @@ export default function Login({
|
|||||||
else if (res.error === ErrorCode.SecondFactorRequired) setTwoFactorRequired(true);
|
else if (res.error === ErrorCode.SecondFactorRequired) setTwoFactorRequired(true);
|
||||||
// fallback if error not found
|
// fallback if error not found
|
||||||
else setErrorMessage(errorMessages[res.error] || t("something_went_wrong"));
|
else setErrorMessage(errorMessages[res.error] || t("something_went_wrong"));
|
||||||
})
|
|
||||||
.catch(() => setErrorMessage(errorMessages[ErrorCode.InternalServerError]));
|
|
||||||
}}
|
}}
|
||||||
data-testid="login-form">
|
data-testid="login-form">
|
||||||
<div>
|
<div>
|
||||||
@@ -156,10 +160,7 @@ export default function Login({
|
|||||||
|
|
||||||
{errorMessage && <Alert severity="error" title={errorMessage} />}
|
{errorMessage && <Alert severity="error" title={errorMessage} />}
|
||||||
<div className="flex space-y-2">
|
<div className="flex space-y-2">
|
||||||
<Button
|
<Button className="flex w-full justify-center" type="submit" disabled={isSubmitting}>
|
||||||
className="flex w-full justify-center"
|
|
||||||
type="submit"
|
|
||||||
disabled={form.formState.isSubmitting || form.formState.isSubmitted}>
|
|
||||||
{twoFactorRequired ? t("submit") : t("sign_in")}
|
{twoFactorRequired ? t("submit") : t("sign_in")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loaded 100 of 192 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user