Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7b4d5932d | ||
|
|
3c7fc85b34 | ||
|
|
af018f0e8b | ||
|
|
6573f75a83 | ||
|
|
a3b26cd1e7 | ||
|
|
4f99746f68 | ||
|
|
fd76bc9cab | ||
|
|
602c813928 | ||
|
|
2c54ea90f9 | ||
|
+17 |
2698cde311 | ||
|
|
1433ec6946 | ||
|
|
48e5d18ea1 | ||
|
|
5da76c698e | ||
|
|
578308a8cb | ||
|
|
6b3862f96d | ||
|
|
94f239bc42 | ||
|
|
2f2f6006e6 | ||
|
|
f8558e16e8 | ||
|
|
38be2bef9a | ||
|
|
485619602f | ||
|
|
ce01537fe6 | ||
|
|
f77e7476d2 | ||
|
|
b6016c8f20 | ||
|
|
a5e5d103a0 | ||
|
|
fec02979ba | ||
|
|
dc200b95cb | ||
|
|
78a54e5f54 | ||
|
|
00ebda2fc6 | ||
|
|
7c749299bb | ||
|
|
3df289b557 | ||
|
|
f4c849b75c | ||
|
|
09cc6f07e4 | ||
|
|
78656f2116 | ||
|
|
1fa5e296af | ||
|
|
0f76530c11 | ||
|
|
ab6758194b | ||
|
|
5528d60897 | ||
|
|
bb1c55e07f | ||
|
|
c899921591 | ||
|
|
ea34545d72 | ||
|
|
9d3540511b | ||
|
|
e8cf92361a | ||
|
|
e8525b963b | ||
|
|
24dabd0ddc | ||
|
|
5203168b2e | ||
|
|
c3e40f84a9 | ||
|
|
8e2ece7526 | ||
|
|
b6eca6e083 | ||
|
|
4cb264f1f7 | ||
|
|
e248fda36b | ||
|
|
1c97e549eb | ||
|
|
77600f8168 | ||
|
|
6cad606ba1 | ||
|
|
7cae7a7bd4 | ||
|
|
93f65807e7 | ||
|
|
0a63820699 | ||
|
|
05ad437b27 | ||
|
|
26a57a71ae | ||
|
|
34a10205a3 | ||
|
|
fbf8ff6db7 | ||
|
|
bef6d18fdf | ||
|
|
2e96e2ef3f | ||
|
|
2ca6769a70 | ||
|
|
1eb514dfcf | ||
|
|
d6c004ba22 | ||
|
+17 |
afb9605f0a | ||
|
|
a2549bd7b6 | ||
|
|
c2a8f799ff | ||
|
|
c68ff54e77 | ||
|
|
74fe20802d | ||
|
|
d59cda3286 | ||
|
|
1cae2b6f3d | ||
|
|
9d9fa8e575 | ||
|
|
f04c634c48 | ||
|
|
c3e7981917 | ||
|
|
d6594dab0f | ||
|
|
5fcfbb2d90 | ||
|
|
8a99c3d2a5 | ||
|
|
336c2d3643 | ||
|
|
cfe3ef4ab0 | ||
|
|
4ec8241a86 | ||
|
|
e6313aafac | ||
|
|
f87f030bb5 | ||
|
|
f87254cdc3 | ||
|
|
1f74837189 | ||
|
|
c7064906c9 | ||
|
|
86c94a3cd5 | ||
|
|
2056ebe186 | ||
|
|
53424ca9ae | ||
|
|
8197ba18d4 | ||
|
|
d341ea5111 | ||
|
|
42dcd4a2a9 | ||
|
|
c63225fdbf | ||
|
|
41774e4932 |
No files matched your search
@@ -0,0 +1,22 @@
|
|||||||
|
name: Cron - mark stale for inactive issues
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
on:
|
||||||
|
# "Scheduled workflows run on the latest commit on the default or base branch."
|
||||||
|
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
|
||||||
|
schedule:
|
||||||
|
# Runs every day (see https://crontab.guru)
|
||||||
|
- cron: "0 0 * * *"
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v7
|
||||||
|
with:
|
||||||
|
days-before-stale: 60
|
||||||
|
include-only-assigned: true
|
||||||
|
days-before-close: -1
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
name: "Next.js Bundle Analysis"
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
# change this if your nextjs app does not live at the root of the repo
|
||||||
|
working-directory: ./apps/web/
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
env:
|
||||||
|
DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
|
||||||
|
NEXT_PUBLIC_WEBAPP_URL: http://localhost:3000
|
||||||
|
NEXT_PUBLIC_WEBSITE_URL: http://localhost:3000
|
||||||
|
NEXTAUTH_SECRET: secret
|
||||||
|
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
|
||||||
|
GOOGLE_LOGIN_ENABLED: true
|
||||||
|
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||||
|
DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }}
|
||||||
|
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
|
||||||
|
NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE }}
|
||||||
|
NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE }}
|
||||||
|
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE }}
|
||||||
|
NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE: ${{ secrets.NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE }}
|
||||||
|
NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN: 1
|
||||||
|
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
|
||||||
|
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
|
||||||
|
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
|
||||||
|
STRIPE_PRO_PLAN_PRODUCT_ID: ${{ secrets.CI_STRIPE_PRO_PLAN_PRODUCT_ID }}
|
||||||
|
STRIPE_PREMIUM_PLAN_PRODUCT_ID: ${{ secrets.CI_STRIPE_PREMIUM_PLAN_PRODUCT_ID }}
|
||||||
|
STRIPE_FREE_PLAN_PRODUCT_ID: ${{ secrets.CI_STRIPE_FREE_PLAN_PRODUCT_ID }}
|
||||||
|
PAYMENT_FEE_PERCENTAGE: 0.005
|
||||||
|
PAYMENT_FEE_FIXED: 10
|
||||||
|
SAML_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
|
||||||
|
SAML_ADMINS: pro@example.com
|
||||||
|
NEXTAUTH_URL: http://localhost:3000/api/auth
|
||||||
|
NEXT_PUBLIC_IS_E2E: 1
|
||||||
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
|
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:12.1
|
||||||
|
env:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_DB: calendso
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16.x
|
||||||
|
cache: "yarn"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
uses: bahmutov/npm-install@v1
|
||||||
|
|
||||||
|
- name: Restore next build
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: restore-build-cache
|
||||||
|
env:
|
||||||
|
cache-name: cache-next-build
|
||||||
|
with:
|
||||||
|
path: apps/web/.next/cache
|
||||||
|
# change this if you prefer a more strict cache
|
||||||
|
key: ${{ runner.os }}-build-${{ env.cache-name }}
|
||||||
|
|
||||||
|
- name: Build next.js app
|
||||||
|
# change this if your site requires a custom build command
|
||||||
|
run: cd ../.. && yarn db-seed && yarn build
|
||||||
|
|
||||||
|
# Here's the first place where next-bundle-analysis' own script is used
|
||||||
|
# This step pulls the raw bundle stats for the current bundle
|
||||||
|
- name: Analyze bundle
|
||||||
|
run: npx -p nextjs-bundle-analysis report
|
||||||
|
|
||||||
|
- name: Upload bundle
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: bundle
|
||||||
|
path: apps/web/.next/analyze/__bundle_analysis.json
|
||||||
|
|
||||||
|
- name: Download base branch bundle stats
|
||||||
|
uses: dawidd6/action-download-artifact@v2
|
||||||
|
if: success() && github.event.number
|
||||||
|
with:
|
||||||
|
workflow: nextjs-bundle-analysis.yml
|
||||||
|
branch: ${{ github.event.pull_request.base.ref }}
|
||||||
|
path: apps/web/.next/analyze/base
|
||||||
|
|
||||||
|
# And here's the second place - this runs after we have both the current and
|
||||||
|
# base branch bundle stats, and will compare them to determine what changed.
|
||||||
|
# There are two configurable arguments that come from package.json:
|
||||||
|
#
|
||||||
|
# - budget: optional, set a budget (bytes) against which size changes are measured
|
||||||
|
# it's set to 350kb here by default, as informed by the following piece:
|
||||||
|
# https://infrequently.org/2021/03/the-performance-inequality-gap/
|
||||||
|
#
|
||||||
|
# - red-status-percentage: sets the percent size increase where you get a red
|
||||||
|
# status indicator, defaults to 20%
|
||||||
|
#
|
||||||
|
# Either of these arguments can be changed or removed by editing the `nextBundleAnalysis`
|
||||||
|
# entry in your package.json file.
|
||||||
|
- name: Compare with base branch bundle
|
||||||
|
if: success() && github.event.number
|
||||||
|
run: ls -laR .next/analyze/base && npx -p nextjs-bundle-analysis compare
|
||||||
|
|
||||||
|
- name: Get comment body
|
||||||
|
id: get-comment-body
|
||||||
|
if: success() && github.event.number
|
||||||
|
run: |
|
||||||
|
body=$(cat .next/analyze/__bundle_analysis_comment.txt)
|
||||||
|
body="${body//'%'/'%25'}"
|
||||||
|
body="${body//$'\n'/'%0A'}"
|
||||||
|
body="${body//$'\r'/'%0D'}"
|
||||||
|
echo ::set-output name=body::$body
|
||||||
|
|
||||||
|
- name: Find Comment
|
||||||
|
uses: peter-evans/find-comment@v1
|
||||||
|
if: success() && github.event.number
|
||||||
|
id: fc
|
||||||
|
with:
|
||||||
|
issue-number: ${{ github.event.number }}
|
||||||
|
body-includes: "<!-- __NEXTJS_BUNDLE -->"
|
||||||
|
|
||||||
|
- name: Create Comment
|
||||||
|
uses: peter-evans/create-or-update-comment@v1.4.4
|
||||||
|
if: success() && github.event.number && steps.fc.outputs.comment-id == 0
|
||||||
|
with:
|
||||||
|
issue-number: ${{ github.event.number }}
|
||||||
|
body: ${{ steps.get-comment-body.outputs.body }}
|
||||||
|
|
||||||
|
- name: Update Comment
|
||||||
|
uses: peter-evans/create-or-update-comment@v1.4.4
|
||||||
|
if: success() && github.event.number && steps.fc.outputs.comment-id != 0
|
||||||
|
with:
|
||||||
|
issue-number: ${{ github.event.number }}
|
||||||
|
body: ${{ steps.get-comment-body.outputs.body }}
|
||||||
|
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||||
|
edit-mode: replace
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
name: TODO to Issue
|
name: TODO to Issue
|
||||||
on:
|
on:
|
||||||
push:
|
|
||||||
branches: [add-todo-to-issue-action] # FIXME: Remove this after merged in main
|
|
||||||
pull_request_target: # So we can test on forks
|
pull_request_target: # So we can test on forks
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ lint-results
|
|||||||
#Storybook
|
#Storybook
|
||||||
apps/storybook/build-storybook.log
|
apps/storybook/build-storybook.log
|
||||||
|
|
||||||
|
# Infisical
|
||||||
|
.infisical.json
|
||||||
|
|
||||||
# Snaplet
|
# Snaplet
|
||||||
.snaplet/snapshots
|
.snaplet/snapshots
|
||||||
.snaplet/structure.d.ts
|
.snaplet/structure.d.ts
|
||||||
|
|||||||
@@ -4,6 +4,60 @@ Contributions are what make the open source community such an amazing place to l
|
|||||||
|
|
||||||
- Before jumping into a PR be sure to search [existing PRs](https://github.com/calcom/cal.com/pulls) or [issues](https://github.com/calcom/cal.com/issues) for an open or closed item that relates to your submission.
|
- Before jumping into a PR be sure to search [existing PRs](https://github.com/calcom/cal.com/pulls) or [issues](https://github.com/calcom/cal.com/issues) for an open or closed item that relates to your submission.
|
||||||
|
|
||||||
|
## Priorities
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Type of Issue
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
Priority
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Minor improvements, non-core feature requests
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://github.com/calcom/cal.com/issues?q=is:issue+is:open+sort:updated-desc+label:%22Low+priority%22">
|
||||||
|
<img src="https://img.shields.io/badge/-Low%20Priority-green">
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Confusing UX (... but working)
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://github.com/calcom/cal.com/issues?q=is:issue+is:open+sort:updated-desc+label:%22Medium+priority%22">
|
||||||
|
<img src="https://img.shields.io/badge/-Medium%20Priority-yellow">
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Core Features (Booking page, availabilty, timezone calculation)
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://github.com/calcom/cal.com/issues?q=is:issue+is:open+sort:updated-desc+label:%22High+priority%22">
|
||||||
|
<img src="https://img.shields.io/badge/-High%20Priority-orange">
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Core Bugs (Login, Booking page, Emails are not working)
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://github.com/calcom/cal.com/issues?q=is:issue+is:open+sort:updated-desc+label:Urgent">
|
||||||
|
<img src="https://img.shields.io/badge/-Urgent-red">
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
|
||||||
The development branch is `main`. This is the branch that all pull
|
The development branch is `main`. This is the branch that all pull
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
<a href="https://cal.com/figma"><img src="https://img.shields.io/badge/Figma-Design%20System-blueviolet"></a>
|
<a href="https://cal.com/figma"><img src="https://img.shields.io/badge/Figma-Design%20System-blueviolet"></a>
|
||||||
<a href="https://calendso.slack.com/archives/C02BY67GMMW"><img src="https://img.shields.io/badge/translations-contribute-brightgreen" /></a>
|
<a href="https://calendso.slack.com/archives/C02BY67GMMW"><img src="https://img.shields.io/badge/translations-contribute-brightgreen" /></a>
|
||||||
<a href="https://www.contributor-covenant.org/version/1/4/code-of-conduct/ "><img src="https://img.shields.io/badge/Contributor%20Covenant-1.4-purple" /></a>
|
<a href="https://www.contributor-covenant.org/version/1/4/code-of-conduct/ "><img src="https://img.shields.io/badge/Contributor%20Covenant-1.4-purple" /></a>
|
||||||
|
<a href="https://console.algora.io/org/cal/bounties?status=open"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Fcal%2Fbounties%3Fstatus%3Dopen" /></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- ABOUT THE PROJECT -->
|
<!-- ABOUT THE PROJECT -->
|
||||||
@@ -76,8 +77,6 @@ That's where Cal.com comes in. Self-hosted or hosted by us. White-label by desig
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### [Product Hunt](https://www.producthunt.com/posts/calendso?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-calendso)
|
#### [Product Hunt](https://www.producthunt.com/posts/calendso?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-calendso)
|
||||||
|
|
||||||
<a href="https://www.producthunt.com/posts/calendso?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-calendso" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/top-post-badge.svg?post_id=291910&theme=light&period=monthly" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a> <a href="https://www.producthunt.com/posts/calendso?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-calendso" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=291910&theme=light" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a> <a href="https://www.producthunt.com/stories/how-this-open-source-calendly-alternative-rocketed-to-product-of-the-day" target="_blank"><img src="https://cal.com/maker-grant.svg" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
<a href="https://www.producthunt.com/posts/calendso?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-calendso" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/top-post-badge.svg?post_id=291910&theme=light&period=monthly" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a> <a href="https://www.producthunt.com/posts/calendso?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-calendso" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=291910&theme=light" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a> <a href="https://www.producthunt.com/stories/how-this-open-source-calendly-alternative-rocketed-to-product-of-the-day" target="_blank"><img src="https://cal.com/maker-grant.svg" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
@@ -341,6 +340,17 @@ Please see our [contributing guide](/CONTRIBUTING.md).
|
|||||||
|
|
||||||
We have a list of [help wanted](https://github.com/calcom/cal.com/issues?q=is:issue+is:open+label:%22%F0%9F%99%8B%F0%9F%8F%BB%E2%80%8D%E2%99%82%EF%B8%8Fhelp+wanted%22) that contain small features and bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process.
|
We have a list of [help wanted](https://github.com/calcom/cal.com/issues?q=is:issue+is:open+label:%22%F0%9F%99%8B%F0%9F%8F%BB%E2%80%8D%E2%99%82%EF%B8%8Fhelp+wanted%22) that contain small features and bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process.
|
||||||
|
|
||||||
|
<!-- BOUNTIES -->
|
||||||
|
|
||||||
|
### Bounties
|
||||||
|
|
||||||
|
<a href="https://console.algora.io/org/cal/bounties?status=open">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://console.algora.io/api/og/cal/bounties.png?p=0&status=open&theme=dark">
|
||||||
|
<img alt="Bounties of cal" src="https://console.algora.io/api/og/cal/bounties.png?p=0&status=open&theme=light">
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
<!-- TRANSLATIONS -->
|
<!-- TRANSLATIONS -->
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -25,6 +25,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@calcom/config": "*",
|
"@calcom/config": "*",
|
||||||
"eslint": "^8.22.0"
|
"eslint": "^8.34.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AppProps } from "next/app";
|
import type { AppProps } from "next/app";
|
||||||
import "nextra-theme-docs/style.css";
|
import "nextra-theme-docs/style.css";
|
||||||
|
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
"@radix-ui/react-dialog": "^1.0.0",
|
"@radix-ui/react-dialog": "^1.0.0",
|
||||||
"@radix-ui/react-dropdown-menu": "^1.0.0",
|
"@radix-ui/react-dropdown-menu": "^1.0.0",
|
||||||
"@radix-ui/react-id": "^1.0.0",
|
"@radix-ui/react-id": "^1.0.0",
|
||||||
"@radix-ui/react-popover": "^1.0.0",
|
"@radix-ui/react-popover": "^1.0.2",
|
||||||
"@radix-ui/react-radio-group": "^1.0.0",
|
"@radix-ui/react-radio-group": "^1.0.0",
|
||||||
"@radix-ui/react-slider": "^1.0.0",
|
"@radix-ui/react-slider": "^1.0.0",
|
||||||
"@radix-ui/react-switch": "^1.0.0",
|
"@radix-ui/react-switch": "^1.0.0",
|
||||||
@@ -36,8 +36,8 @@
|
|||||||
"@storybook/manager-webpack5": "^6.5.13",
|
"@storybook/manager-webpack5": "^6.5.13",
|
||||||
"@storybook/react": "^6.5.13",
|
"@storybook/react": "^6.5.13",
|
||||||
"@storybook/testing-library": "^0.0.13",
|
"@storybook/testing-library": "^0.0.13",
|
||||||
"@types/react": "^18.0.17",
|
"@types/react": "18.0.26",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "18.0.9",
|
||||||
"@vitejs/plugin-react": "^2.1.0",
|
"@vitejs/plugin-react": "^2.1.0",
|
||||||
"autoprefixer": "^10.4.12",
|
"autoprefixer": "^10.4.12",
|
||||||
"babel-loader": "^8.2.5",
|
"babel-loader": "^8.2.5",
|
||||||
|
|||||||
@@ -2,4 +2,9 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@import "../../../packages/ui/styles/shared-globals.css"
|
@import "../../../packages/ui/styles/shared-globals.css";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--font-inter: "Inter var";
|
||||||
|
--font-cal: "Cal Sans";
|
||||||
|
}
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "16.9.1",
|
"@types/node": "16.9.1",
|
||||||
"@types/react": "^18.0.17",
|
"@types/react": "18.0.26",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "18.0.9",
|
||||||
"typescript": "^4.9.4"
|
"typescript": "^4.9.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { SwaggerUI } from "swagger-ui-react";
|
import type { SwaggerUI } from "swagger-ui-react";
|
||||||
|
|
||||||
import { SnippedGenerator, requestSnippets } from "@lib/snippets";
|
import { SnippedGenerator, requestSnippets } from "@lib/snippets";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Select from "react-select";
|
import Select from "react-select";
|
||||||
import { OptionProps } from "react-select";
|
import type { OptionProps } from "react-select";
|
||||||
|
|
||||||
import { InstallAppButton } from "@calcom/app-store/components";
|
import { InstallAppButton } from "@calcom/app-store/components";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { ReactNode, useEffect, useRef, useState } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
@@ -62,8 +63,8 @@ export default function AppListCard(props: AppListCardProps) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`p-4 ${highlight ? "bg-yellow-100" : ""}`}>
|
<div className={`${highlight ? "bg-yellow-100" : ""}`}>
|
||||||
<div className="flex gap-x-3">
|
<div className="flex gap-x-3 p-4">
|
||||||
{logo ? <img className="h-10 w-10" src={logo} alt={`${title} logo`} /> : null}
|
{logo ? <img className="h-10 w-10" src={logo} alt={`${title} logo`} /> : null}
|
||||||
<div className="flex grow flex-col gap-y-1 truncate">
|
<div className="flex grow flex-col gap-y-1 truncate">
|
||||||
<div className="flex items-center gap-x-2">
|
<div className="flex items-center gap-x-2">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React, { useEffect, useState } from "react";
|
|||||||
import Select from "react-select";
|
import Select from "react-select";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { DestinationCalendar } from "@calcom/prisma/client";
|
import type { DestinationCalendar } from "@calcom/prisma/client";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
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 { NextRouter, useRouter } from "next/router";
|
import type { NextRouter } from "next/router";
|
||||||
import { createRef, forwardRef, MutableRefObject, RefObject, useRef, useState } from "react";
|
import { useRouter } from "next/router";
|
||||||
import { components, ControlProps } from "react-select";
|
import type { MutableRefObject, RefObject } from "react";
|
||||||
|
import { createRef, forwardRef, useRef, useState } from "react";
|
||||||
|
import type { ControlProps } from "react-select";
|
||||||
|
import { components } from "react-select";
|
||||||
|
|
||||||
import { APP_NAME, EMBED_LIB_URL, WEBAPP_URL } from "@calcom/lib/constants";
|
import { APP_NAME, EMBED_LIB_URL, WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
@@ -12,16 +15,15 @@ import {
|
|||||||
DialogClose,
|
DialogClose,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
HorizontalTabs,
|
HorizontalTabs,
|
||||||
InputLeading,
|
|
||||||
Label,
|
Label,
|
||||||
showToast,
|
showToast,
|
||||||
Switch,
|
Switch,
|
||||||
TextArea,
|
TextArea,
|
||||||
TextField,
|
TextField,
|
||||||
|
ColorPicker,
|
||||||
} from "@calcom/ui";
|
} from "@calcom/ui";
|
||||||
import { FiCode, FiTrello, FiSun, FiArrowLeft, FiChevronRight } from "@calcom/ui/components/icon";
|
import { FiCode, FiTrello, FiSun, FiArrowLeft, FiChevronRight } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import ColorPicker from "@components/ui/colorpicker";
|
|
||||||
import Select from "@components/ui/form/Select";
|
import Select from "@components/ui/form/Select";
|
||||||
|
|
||||||
type EmbedType = "inline" | "floating-popup" | "element-click";
|
type EmbedType = "inline" | "floating-popup" | "element-click";
|
||||||
@@ -676,7 +678,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||||||
return `${router.asPath.split("?")[0]}?${searchParams.toString()}`;
|
return `${router.asPath.split("?")[0]}?${searchParams.toString()}`;
|
||||||
};
|
};
|
||||||
const parsedTabs = tabs.map((t) => ({ ...t, href: s(t.href) }));
|
const parsedTabs = tabs.map((t) => ({ ...t, href: s(t.href) }));
|
||||||
const embedCodeRefs: Record<typeof tabs[0]["name"], RefObject<HTMLTextAreaElement>> = {};
|
const embedCodeRefs: Record<(typeof tabs)[0]["name"], RefObject<HTMLTextAreaElement>> = {};
|
||||||
tabs
|
tabs
|
||||||
.filter((tab) => tab.type === "code")
|
.filter((tab) => tab.type === "code")
|
||||||
.forEach((codeTab) => {
|
.forEach((codeTab) => {
|
||||||
@@ -720,7 +722,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||||||
|
|
||||||
const calLink = decodeURIComponent(embedUrl);
|
const calLink = decodeURIComponent(embedUrl);
|
||||||
|
|
||||||
const addToPalette = (update: typeof previewState["palette"]) => {
|
const addToPalette = (update: (typeof previewState)["palette"]) => {
|
||||||
setPreviewState((previewState) => {
|
setPreviewState((previewState) => {
|
||||||
return {
|
return {
|
||||||
...previewState,
|
...previewState,
|
||||||
@@ -1061,7 +1063,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||||||
defaultValue="#000000"
|
defaultValue="#000000"
|
||||||
onChange={(color) => {
|
onChange={(color) => {
|
||||||
addToPalette({
|
addToPalette({
|
||||||
[palette.name as keyof typeof previewState["palette"]]: color,
|
[palette.name as keyof (typeof previewState)["palette"]]: color,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { Dispatch, useState, useEffect } from "react";
|
import type { Dispatch } from "react";
|
||||||
import { JSONObject } from "superjson/dist/types";
|
import { useState, useEffect } from "react";
|
||||||
|
import type { JSONObject } from "superjson/dist/types";
|
||||||
|
|
||||||
export type Gate = undefined | "rainbow"; // Add more like ` | "geolocation" | "payment"`
|
export type Gate = undefined | "rainbow"; // Add more like ` | "geolocation" | "payment"`
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { FormEvent, useCallback, useEffect, useState } from "react";
|
import type { FormEvent } from "react";
|
||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import Cropper from "react-easy-crop";
|
import Cropper from "react-easy-crop";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Button, Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui";
|
import { Button, Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui";
|
||||||
|
|
||||||
import { Area, getCroppedImg } from "@lib/cropImage";
|
import type { Area } from "@lib/cropImage";
|
||||||
|
import { getCroppedImg } from "@lib/cropImage";
|
||||||
import { useFileReader } from "@lib/hooks/useFileReader";
|
import { useFileReader } from "@lib/hooks/useFileReader";
|
||||||
|
|
||||||
import Slider from "@components/Slider";
|
import Slider from "@components/Slider";
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { AdminRequired } from "components/ui/AdminRequired";
|
import { AdminRequired } from "components/ui/AdminRequired";
|
||||||
import noop from "lodash/noop";
|
import noop from "lodash/noop";
|
||||||
import Link, { LinkProps } from "next/link";
|
import type { LinkProps } from "next/link";
|
||||||
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { FC, Fragment, MouseEventHandler } from "react";
|
import type { FC, MouseEventHandler } from "react";
|
||||||
|
import { Fragment } from "react";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|
||||||
import classNames from "@lib/classNames";
|
import classNames from "@lib/classNames";
|
||||||
import { SVGComponent } from "@lib/types/SVGComponent";
|
import type { SVGComponent } from "@lib/types/SVGComponent";
|
||||||
|
|
||||||
export interface NavTabProps {
|
export interface NavTabProps {
|
||||||
tabs: {
|
tabs: {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { ComponentProps } from "react";
|
import type { ComponentProps } from "react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
import Shell from "@calcom/features/shell/Shell";
|
import Shell from "@calcom/features/shell/Shell";
|
||||||
import { ErrorBoundary } from "@calcom/ui";
|
import { ErrorBoundary } from "@calcom/ui";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { OptionProps } from "react-select";
|
import type { OptionProps } from "react-select";
|
||||||
|
|
||||||
import { InstallAppButton } from "@calcom/app-store/components";
|
import { InstallAppButton } from "@calcom/app-store/components";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import classNames from "@calcom/lib/classNames";
|
|||||||
import { APP_NAME, COMPANY_NAME, SUPPORT_MAIL_ADDRESS } from "@calcom/lib/constants";
|
import { APP_NAME, COMPANY_NAME, SUPPORT_MAIL_ADDRESS } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { App as AppType } from "@calcom/types/App";
|
import type { App as AppType } from "@calcom/types/App";
|
||||||
import { Button, showToast, SkeletonButton, SkeletonText, HeadSeo, Badge } from "@calcom/ui";
|
import { Button, showToast, SkeletonButton, SkeletonText, HeadSeo, Badge } from "@calcom/ui";
|
||||||
import {
|
import {
|
||||||
FiBookOpen,
|
FiBookOpen,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React, { useEffect, useState } from "react";
|
|||||||
import Select from "react-select";
|
import Select from "react-select";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { DestinationCalendar } from "@calcom/prisma/client";
|
import type { DestinationCalendar } from "@calcom/prisma/client";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React, { ComponentProps } from "react";
|
import type { ComponentProps } from "react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
import Shell from "@calcom/features/shell/Shell";
|
import Shell from "@calcom/features/shell/Shell";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React, { ComponentProps } from "react";
|
import type { ComponentProps } from "react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
import AppCategoryNavigation from "@calcom/app-store/_components/AppCategoryNavigation";
|
import AppCategoryNavigation from "@calcom/app-store/_components/AppCategoryNavigation";
|
||||||
import { InstalledAppVariants } from "@calcom/app-store/utils";
|
import type { InstalledAppVariants } from "@calcom/app-store/utils";
|
||||||
import Shell from "@calcom/features/shell/Shell";
|
import Shell from "@calcom/features/shell/Shell";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import type { HorizontalTabItemProps, VerticalTabItemProps } from "@calcom/ui";
|
import type { HorizontalTabItemProps, VerticalTabItemProps } from "@calcom/ui";
|
||||||
@@ -44,7 +45,7 @@ export default function InstalledAppsLayout({
|
|||||||
children,
|
children,
|
||||||
...rest
|
...rest
|
||||||
}: { children: React.ReactNode } & ComponentProps<typeof Shell>) {
|
}: { children: React.ReactNode } & ComponentProps<typeof Shell>) {
|
||||||
const variant: typeof InstalledAppVariants[number] = "payment";
|
const variant: (typeof InstalledAppVariants)[number] = "payment";
|
||||||
|
|
||||||
const query = trpc.viewer.integrations.useQuery({
|
const query = trpc.viewer.integrations.useQuery({
|
||||||
variant,
|
variant,
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
import { UserPermissionRole } from "@prisma/client";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React, { ComponentProps, useEffect } from "react";
|
import type { ComponentProps } from "react";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
import SettingsLayout from "@calcom/features/settings/layouts/SettingsLayout";
|
import SettingsLayout from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
import Shell from "@calcom/features/shell/Shell";
|
import type Shell from "@calcom/features/shell/Shell";
|
||||||
import { ErrorBoundary } from "@calcom/ui";
|
import { ErrorBoundary } from "@calcom/ui";
|
||||||
|
|
||||||
import { UserPermissionRole } from ".prisma/client";
|
|
||||||
|
|
||||||
export default function AdminLayout({
|
export default function AdminLayout({
|
||||||
children,
|
children,
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||||||
import { Tooltip } from "@calcom/ui";
|
import { Tooltip } from "@calcom/ui";
|
||||||
import { FiLink } from "@calcom/ui/components/icon";
|
import { FiLink } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import { Props } from "./pages/AvailabilityPage";
|
import type { Props } from "./pages/AvailabilityPage";
|
||||||
|
|
||||||
export function AvailableEventLocations({ locations }: { locations: Props["eventType"]["locations"] }) {
|
export function AvailableEventLocations({ locations }: { locations: Props["eventType"]["locations"] }) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { FC, useEffect, useState } from "react";
|
import type { FC } from "react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import dayjs, { Dayjs } from "@calcom/dayjs";
|
import type { Dayjs } from "@calcom/dayjs";
|
||||||
|
import dayjs from "@calcom/dayjs";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { TimeFormat } from "@calcom/lib/timeFormat";
|
import { TimeFormat } from "@calcom/lib/timeFormat";
|
||||||
import { nameOfDay } from "@calcom/lib/weekday";
|
import { nameOfDay } from "@calcom/lib/weekday";
|
||||||
@@ -37,6 +40,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
|
|||||||
seatsPerTimeSlot,
|
seatsPerTimeSlot,
|
||||||
ethSignature,
|
ethSignature,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [slotPickerRef] = useAutoAnimate<HTMLDivElement>();
|
||||||
const { t, i18n } = useLocale();
|
const { t, i18n } = useLocale();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { rescheduleUid } = router.query;
|
const { rescheduleUid } = router.query;
|
||||||
@@ -47,122 +51,124 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
|
|||||||
setBrand(getComputedStyle(document.documentElement).getPropertyValue("--brand-color").trim());
|
setBrand(getComputedStyle(document.documentElement).getPropertyValue("--brand-color").trim());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!date) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dark:bg-darkgray-100 mt-8 flex h-full w-full flex-col px-4 text-center sm:mt-0 sm:p-5 md:-mb-5 md:min-w-[200px] lg:min-w-[300px]">
|
<div ref={slotPickerRef}>
|
||||||
<div className="mb-6 flex items-center text-left text-base">
|
{!!date ? (
|
||||||
<div className="mr-4">
|
<div className="dark:bg-darkgray-100 mt-8 flex h-full w-full flex-col px-4 text-center sm:mt-0 sm:p-5 md:-mb-5 md:min-w-[200px] lg:min-w-[300px]">
|
||||||
<span className="text-bookingdarker dark:text-darkgray-800 font-semibold text-gray-900">
|
<div className="mb-6 flex items-center text-left text-base">
|
||||||
{nameOfDay(i18n.language, Number(date.format("d")), "short")}
|
<div className="mr-4">
|
||||||
</span>
|
<span className="text-bookingdarker dark:text-darkgray-800 font-semibold text-gray-900">
|
||||||
<span className="text-bookinglight font-medium">
|
{nameOfDay(i18n.language, Number(date.format("d")), "short")}
|
||||||
, {date.toDate().toLocaleString(i18n.language, { month: "short" })} {date.format(" D ")}
|
</span>
|
||||||
</span>
|
<span className="text-bookinglight font-medium">
|
||||||
</div>
|
, {date.toDate().toLocaleString(i18n.language, { month: "short" })} {date.format(" D ")}
|
||||||
<div className="ml-auto">
|
</span>
|
||||||
<ToggleGroup
|
</div>
|
||||||
onValueChange={(timeFormat) => onTimeFormatChange(timeFormat === "24")}
|
<div className="ml-auto">
|
||||||
defaultValue={timeFormat === TimeFormat.TWELVE_HOUR ? "12" : "24"}
|
<ToggleGroup
|
||||||
options={[
|
onValueChange={(timeFormat) => onTimeFormatChange(timeFormat === "24")}
|
||||||
{ value: "12", label: t("12_hour_short") },
|
defaultValue={timeFormat === TimeFormat.TWELVE_HOUR ? "12" : "24"}
|
||||||
{ value: "24", label: t("24_hour_short") },
|
options={[
|
||||||
]}
|
{ value: "12", label: t("12_hour_short") },
|
||||||
/>
|
{ value: "24", label: t("24_hour_short") },
|
||||||
</div>
|
]}
|
||||||
</div>
|
/>
|
||||||
<div className="flex-grow overflow-y-auto sm:block md:h-[364px]">
|
</div>
|
||||||
{slots.length > 0 &&
|
|
||||||
slots.map((slot) => {
|
|
||||||
type BookingURL = {
|
|
||||||
pathname: string;
|
|
||||||
query: Record<string, string | number | string[] | undefined>;
|
|
||||||
};
|
|
||||||
const bookingUrl: BookingURL = {
|
|
||||||
pathname: router.pathname.endsWith("/embed") ? "../book" : "book",
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
date: dayjs.utc(slot.time).tz(timeZone()).format(),
|
|
||||||
type: eventTypeId,
|
|
||||||
slug: eventTypeSlug,
|
|
||||||
/** Treat as recurring only when a count exist and it's not a rescheduling workflow */
|
|
||||||
count: recurringCount && !rescheduleUid ? recurringCount : undefined,
|
|
||||||
...(ethSignature ? { ethSignature } : {}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (rescheduleUid) {
|
|
||||||
bookingUrl.query.rescheduleUid = rescheduleUid as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If event already has an attendee add booking id
|
|
||||||
if (slot.bookingUid) {
|
|
||||||
bookingUrl.query.bookingUid = slot.bookingUid;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div data-slot-owner={(slot.userIds || []).join(",")} key={`${dayjs(slot.time).format()}`}>
|
|
||||||
{/* ^ data-slot-owner is helpful in debugging and used to identify the owners of the slot. Owners are the users which have the timeslot in their schedule. It doesn't consider if a user has that timeslot booked */}
|
|
||||||
{/* Current there is no way to disable Next.js Links */}
|
|
||||||
{seatsPerTimeSlot && slot.attendees && slot.attendees >= seatsPerTimeSlot ? (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
"text-primary-500 dark:bg-darkgray-200 dark:text-darkgray-900 mb-2 block rounded-sm border bg-white py-2 font-medium opacity-25 dark:border-transparent ",
|
|
||||||
brand === "#fff" || brand === "#ffffff" ? "" : ""
|
|
||||||
)}>
|
|
||||||
{dayjs(slot.time).tz(timeZone()).format(timeFormat)}
|
|
||||||
{!!seatsPerTimeSlot && <p className="text-sm">{t("booking_full")}</p>}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Link
|
|
||||||
href={bookingUrl}
|
|
||||||
prefetch={false}
|
|
||||||
className={classNames(
|
|
||||||
"text-primary-500 hover:border-gray-900 hover:bg-gray-50",
|
|
||||||
"dark:bg-darkgray-200 dark:hover:bg-darkgray-300 dark:hover:border-darkmodebrand dark:text-darkgray-800 mb-2 block rounded-md border bg-white py-2 text-sm font-medium dark:border-transparent",
|
|
||||||
brand === "#fff" || brand === "#ffffff" ? "" : ""
|
|
||||||
)}
|
|
||||||
data-testid="time">
|
|
||||||
{dayjs(slot.time).tz(timeZone()).format(timeFormat)}
|
|
||||||
{!!seatsPerTimeSlot && (
|
|
||||||
<p
|
|
||||||
className={`${
|
|
||||||
slot.attendees && slot.attendees / seatsPerTimeSlot >= 0.8
|
|
||||||
? "text-rose-600"
|
|
||||||
: slot.attendees && slot.attendees / seatsPerTimeSlot >= 0.33
|
|
||||||
? "text-yellow-500"
|
|
||||||
: "text-emerald-400"
|
|
||||||
} text-sm`}>
|
|
||||||
{slot.attendees ? seatsPerTimeSlot - slot.attendees : seatsPerTimeSlot} /{" "}
|
|
||||||
{seatsPerTimeSlot} {t("seats_available")}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
{!isLoading && !slots.length && (
|
|
||||||
<div className="-mt-4 flex h-full w-full flex-col content-center items-center justify-center">
|
|
||||||
<h1 className="my-6 text-xl text-black dark:text-white">{t("all_booked_today")}</h1>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
<div className="flex-grow overflow-y-auto sm:block md:h-[364px]">
|
||||||
|
{slots.length > 0 &&
|
||||||
|
slots.map((slot) => {
|
||||||
|
type BookingURL = {
|
||||||
|
pathname: string;
|
||||||
|
query: Record<string, string | number | string[] | undefined>;
|
||||||
|
};
|
||||||
|
const bookingUrl: BookingURL = {
|
||||||
|
pathname: router.pathname.endsWith("/embed") ? "../book" : "book",
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
date: dayjs.utc(slot.time).tz(timeZone()).format(),
|
||||||
|
type: eventTypeId,
|
||||||
|
slug: eventTypeSlug,
|
||||||
|
/** Treat as recurring only when a count exist and it's not a rescheduling workflow */
|
||||||
|
count: recurringCount && !rescheduleUid ? recurringCount : undefined,
|
||||||
|
...(ethSignature ? { ethSignature } : {}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
{isLoading && !slots.length && (
|
if (rescheduleUid) {
|
||||||
<>
|
bookingUrl.query.rescheduleUid = rescheduleUid as string;
|
||||||
<SkeletonContainer className="mb-2">
|
}
|
||||||
<SkeletonText className="h-5 w-full" />
|
|
||||||
</SkeletonContainer>
|
// If event already has an attendee add booking id
|
||||||
<SkeletonContainer className="mb-2">
|
if (slot.bookingUid) {
|
||||||
<SkeletonText className="h-5 w-full" />
|
bookingUrl.query.bookingUid = slot.bookingUid;
|
||||||
</SkeletonContainer>
|
}
|
||||||
<SkeletonContainer className="mb-2">
|
|
||||||
<SkeletonText className="h-5 w-full" />
|
return (
|
||||||
</SkeletonContainer>
|
<div data-slot-owner={(slot.userIds || []).join(",")} key={`${dayjs(slot.time).format()}`}>
|
||||||
</>
|
{/* ^ data-slot-owner is helpful in debugging and used to identify the owners of the slot. Owners are the users which have the timeslot in their schedule. It doesn't consider if a user has that timeslot booked */}
|
||||||
)}
|
{/* Current there is no way to disable Next.js Links */}
|
||||||
</div>
|
{seatsPerTimeSlot && slot.attendees && slot.attendees >= seatsPerTimeSlot ? (
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
"text-primary-500 dark:bg-darkgray-200 dark:text-darkgray-900 mb-2 block rounded-sm border bg-white py-2 font-medium opacity-25 dark:border-transparent ",
|
||||||
|
brand === "#fff" || brand === "#ffffff" ? "" : ""
|
||||||
|
)}>
|
||||||
|
{dayjs(slot.time).tz(timeZone()).format(timeFormat)}
|
||||||
|
{!!seatsPerTimeSlot && <p className="text-sm">{t("booking_full")}</p>}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Link
|
||||||
|
href={bookingUrl}
|
||||||
|
prefetch={false}
|
||||||
|
className={classNames(
|
||||||
|
"text-primary-500 hover:border-gray-900 hover:bg-gray-50",
|
||||||
|
"dark:bg-darkgray-200 dark:hover:bg-darkgray-300 dark:hover:border-darkmodebrand dark:text-darkgray-800 mb-2 block rounded-md border bg-white py-2 text-sm font-medium dark:border-transparent",
|
||||||
|
brand === "#fff" || brand === "#ffffff" ? "" : ""
|
||||||
|
)}
|
||||||
|
data-testid="time">
|
||||||
|
{dayjs(slot.time).tz(timeZone()).format(timeFormat)}
|
||||||
|
{!!seatsPerTimeSlot && (
|
||||||
|
<p
|
||||||
|
className={`${
|
||||||
|
slot.attendees && slot.attendees / seatsPerTimeSlot >= 0.8
|
||||||
|
? "text-rose-600"
|
||||||
|
: slot.attendees && slot.attendees / seatsPerTimeSlot >= 0.33
|
||||||
|
? "text-yellow-500"
|
||||||
|
: "text-emerald-400"
|
||||||
|
} text-sm`}>
|
||||||
|
{slot.attendees ? seatsPerTimeSlot - slot.attendees : seatsPerTimeSlot} /{" "}
|
||||||
|
{seatsPerTimeSlot} {t("seats_available")}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{!isLoading && !slots.length && (
|
||||||
|
<div className="-mt-4 flex h-full w-full flex-col content-center items-center justify-center">
|
||||||
|
<h1 className="my-6 text-xl text-black dark:text-white">{t("all_booked_today")}</h1>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isLoading && !slots.length && (
|
||||||
|
<>
|
||||||
|
<SkeletonContainer className="mb-2">
|
||||||
|
<SkeletonText className="h-5 w-full" />
|
||||||
|
</SkeletonContainer>
|
||||||
|
<SkeletonContainer className="mb-2">
|
||||||
|
<SkeletonText className="h-5 w-full" />
|
||||||
|
</SkeletonContainer>
|
||||||
|
<SkeletonContainer className="mb-2">
|
||||||
|
<SkeletonText className="h-5 w-full" />
|
||||||
|
</SkeletonContainer>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { SchedulingType } from "@prisma/client";
|
import { SchedulingType } from "@prisma/client";
|
||||||
import { FC, ReactNode, useEffect } from "react";
|
import type { FC, ReactNode } from "react";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import { classNames } from "@calcom/lib";
|
import classNames from "@calcom/lib/classNames";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Badge } from "@calcom/ui";
|
import { Badge } from "@calcom/ui";
|
||||||
import { FiCheckSquare, FiClock, FiInfo } from "@calcom/ui/components/icon";
|
import { FiCheckSquare, FiClock, FiInfo } from "@calcom/ui/components/icon";
|
||||||
|
|||||||
@@ -2,14 +2,17 @@ import { BookingStatus } from "@prisma/client";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
import { EventLocationType, getEventLocationType } from "@calcom/app-store/locations";
|
import type { EventLocationType } from "@calcom/app-store/locations";
|
||||||
|
import { getEventLocationType } from "@calcom/app-store/locations";
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import ViewRecordingsDialog from "@calcom/features/ee/video/ViewRecordingsDialog";
|
import ViewRecordingsDialog from "@calcom/features/ee/video/ViewRecordingsDialog";
|
||||||
import classNames from "@calcom/lib/classNames";
|
import classNames from "@calcom/lib/classNames";
|
||||||
import { formatTime } from "@calcom/lib/date-fns";
|
import { formatTime } from "@calcom/lib/date-fns";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
|
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
|
||||||
import { RouterInputs, RouterOutputs, trpc } from "@calcom/trpc/react";
|
import type { RouterInputs, RouterOutputs } from "@calcom/trpc/react";
|
||||||
|
import { trpc } from "@calcom/trpc/react";
|
||||||
|
import type { ActionType } from "@calcom/ui";
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
@@ -20,7 +23,6 @@ import {
|
|||||||
MeetingTimeInTimezones,
|
MeetingTimeInTimezones,
|
||||||
showToast,
|
showToast,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
ActionType,
|
|
||||||
TableActions,
|
TableActions,
|
||||||
TextAreaField,
|
TextAreaField,
|
||||||
} from "@calcom/ui";
|
} from "@calcom/ui";
|
||||||
|
|||||||
@@ -0,0 +1,200 @@
|
|||||||
|
import type { EventType } from "@prisma/client";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
import type { Dayjs } from "@calcom/dayjs";
|
||||||
|
import dayjs from "@calcom/dayjs";
|
||||||
|
import DatePicker from "@calcom/features/calendars/DatePicker";
|
||||||
|
import classNames from "@calcom/lib/classNames";
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
import type { TimeFormat } from "@calcom/lib/timeFormat";
|
||||||
|
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||||
|
import { trpc } from "@calcom/trpc/react";
|
||||||
|
|
||||||
|
import useRouterQuery from "@lib/hooks/useRouterQuery";
|
||||||
|
|
||||||
|
const AvailableTimes = dynamic(() => import("@components/booking/AvailableTimes"));
|
||||||
|
|
||||||
|
const getRefetchInterval = (refetchCount: number): number => {
|
||||||
|
const intervals = [3000, 3000, 5000, 10000, 20000, 30000] as const;
|
||||||
|
return intervals[refetchCount] || intervals[intervals.length - 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
const useSlots = ({
|
||||||
|
eventTypeId,
|
||||||
|
eventTypeSlug,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
usernameList,
|
||||||
|
timeZone,
|
||||||
|
duration,
|
||||||
|
enabled = true,
|
||||||
|
}: {
|
||||||
|
eventTypeId: number;
|
||||||
|
eventTypeSlug: string;
|
||||||
|
startTime?: Dayjs;
|
||||||
|
endTime?: Dayjs;
|
||||||
|
usernameList: string[];
|
||||||
|
timeZone?: string;
|
||||||
|
duration?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
}) => {
|
||||||
|
const [refetchCount, setRefetchCount] = useState(0);
|
||||||
|
const refetchInterval = getRefetchInterval(refetchCount);
|
||||||
|
const { data, isLoading, isPaused, fetchStatus } = trpc.viewer.public.slots.getSchedule.useQuery(
|
||||||
|
{
|
||||||
|
eventTypeId,
|
||||||
|
eventTypeSlug,
|
||||||
|
usernameList,
|
||||||
|
startTime: startTime?.toISOString() || "",
|
||||||
|
endTime: endTime?.toISOString() || "",
|
||||||
|
timeZone,
|
||||||
|
duration,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!startTime && !!endTime && enabled,
|
||||||
|
refetchInterval,
|
||||||
|
trpc: { context: { skipBatch: true } },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!!data && fetchStatus === "idle") {
|
||||||
|
setRefetchCount(refetchCount + 1);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [fetchStatus, data]);
|
||||||
|
|
||||||
|
// The very first time isPaused is set if auto-fetch is disabled, so isPaused should also be considered a loading state.
|
||||||
|
return { slots: data?.slots || {}, isLoading: isLoading || isPaused };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SlotPicker = ({
|
||||||
|
eventType,
|
||||||
|
timeFormat,
|
||||||
|
onTimeFormatChange,
|
||||||
|
timeZone,
|
||||||
|
recurringEventCount,
|
||||||
|
users,
|
||||||
|
seatsPerTimeSlot,
|
||||||
|
weekStart = 0,
|
||||||
|
ethSignature,
|
||||||
|
}: {
|
||||||
|
eventType: Pick<
|
||||||
|
EventType & { metadata: z.infer<typeof EventTypeMetaDataSchema> },
|
||||||
|
"id" | "schedulingType" | "slug" | "length" | "metadata"
|
||||||
|
>;
|
||||||
|
timeFormat: TimeFormat;
|
||||||
|
onTimeFormatChange: (is24Hour: boolean) => void;
|
||||||
|
timeZone?: string;
|
||||||
|
seatsPerTimeSlot?: number;
|
||||||
|
recurringEventCount?: number;
|
||||||
|
users: string[];
|
||||||
|
weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||||
|
ethSignature?: string;
|
||||||
|
}) => {
|
||||||
|
const [selectedDate, setSelectedDate] = useState<Dayjs>();
|
||||||
|
const [browsingDate, setBrowsingDate] = useState<Dayjs>();
|
||||||
|
let { duration = eventType.length.toString() } = useRouterQuery("duration");
|
||||||
|
const { date, setQuery: setDate } = useRouterQuery("date");
|
||||||
|
const { month, setQuery: setMonth } = useRouterQuery("month");
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
if (!eventType.metadata?.multipleDuration) {
|
||||||
|
duration = eventType.length.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!router.isReady) return;
|
||||||
|
|
||||||
|
// Etc/GMT is not actually a timeZone, so handle this select option explicitly to prevent a hard crash.
|
||||||
|
if (timeZone === "Etc/GMT") {
|
||||||
|
setBrowsingDate(dayjs.utc(month).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0));
|
||||||
|
if (date) {
|
||||||
|
setSelectedDate(dayjs.utc(date));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Set the start of the month without shifting time like startOf() may do.
|
||||||
|
setBrowsingDate(
|
||||||
|
dayjs.tz(month, timeZone).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0)
|
||||||
|
);
|
||||||
|
if (date) {
|
||||||
|
// It's important to set the date immediately to the timeZone, dayjs(date) will convert to browsertime.
|
||||||
|
setSelectedDate(dayjs.tz(date, timeZone));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [router.isReady, month, date, duration, timeZone]);
|
||||||
|
|
||||||
|
const { i18n, isLocaleReady } = useLocale();
|
||||||
|
const { slots: monthSlots, isLoading } = useSlots({
|
||||||
|
eventTypeId: eventType.id,
|
||||||
|
eventTypeSlug: eventType.slug,
|
||||||
|
usernameList: users,
|
||||||
|
startTime:
|
||||||
|
browsingDate === undefined || browsingDate.get("month") === dayjs.tz(undefined, timeZone).get("month")
|
||||||
|
? dayjs.tz(undefined, timeZone).subtract(2, "days").startOf("day")
|
||||||
|
: browsingDate?.startOf("month"),
|
||||||
|
endTime: browsingDate?.endOf("month"),
|
||||||
|
timeZone,
|
||||||
|
duration,
|
||||||
|
});
|
||||||
|
const { slots: selectedDateSlots, isLoading: _isLoadingSelectedDateSlots } = useSlots({
|
||||||
|
eventTypeId: eventType.id,
|
||||||
|
eventTypeSlug: eventType.slug,
|
||||||
|
usernameList: users,
|
||||||
|
startTime: selectedDate?.startOf("day"),
|
||||||
|
endTime: selectedDate?.endOf("day"),
|
||||||
|
timeZone,
|
||||||
|
duration,
|
||||||
|
/** Prevent refetching is we already have this data from month slots */
|
||||||
|
enabled: !!selectedDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Hide skeleton if we have the slot loaded in the month query */
|
||||||
|
const isLoadingSelectedDateSlots = (() => {
|
||||||
|
if (!selectedDate) return _isLoadingSelectedDateSlots;
|
||||||
|
if (!!selectedDateSlots[selectedDate.format("YYYY-MM-DD")]) return false;
|
||||||
|
if (!!monthSlots[selectedDate.format("YYYY-MM-DD")]) return false;
|
||||||
|
return false;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DatePicker
|
||||||
|
isLoading={isLoading}
|
||||||
|
className={classNames(
|
||||||
|
"mt-8 px-4 pb-4 sm:mt-0 md:min-w-[300px] md:px-5 lg:min-w-[455px]",
|
||||||
|
selectedDate ? "sm:dark:border-darkgray-200 border-gray-200 sm:border-r sm:p-4 sm:pr-6" : "sm:p-4"
|
||||||
|
)}
|
||||||
|
includedDates={Object.keys(monthSlots).filter((k) => monthSlots[k].length > 0)}
|
||||||
|
locale={isLocaleReady ? i18n.language : "en"}
|
||||||
|
selected={selectedDate}
|
||||||
|
onChange={(newDate) => {
|
||||||
|
setDate(newDate.format("YYYY-MM-DD"));
|
||||||
|
}}
|
||||||
|
onMonthChange={(newMonth) => {
|
||||||
|
setMonth(newMonth.format("YYYY-MM"));
|
||||||
|
}}
|
||||||
|
browsingDate={browsingDate}
|
||||||
|
weekStart={weekStart}
|
||||||
|
/>
|
||||||
|
<AvailableTimes
|
||||||
|
isLoading={isLoadingSelectedDateSlots}
|
||||||
|
slots={
|
||||||
|
selectedDate &&
|
||||||
|
(selectedDateSlots[selectedDate.format("YYYY-MM-DD")] ||
|
||||||
|
monthSlots[selectedDate.format("YYYY-MM-DD")])
|
||||||
|
}
|
||||||
|
date={selectedDate}
|
||||||
|
timeFormat={timeFormat}
|
||||||
|
onTimeFormatChange={onTimeFormatChange}
|
||||||
|
eventTypeId={eventType.id}
|
||||||
|
eventTypeSlug={eventType.slug}
|
||||||
|
seatsPerTimeSlot={seatsPerTimeSlot}
|
||||||
|
recurringCount={recurringEventCount}
|
||||||
|
ethSignature={ethSignature}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import { FC, useEffect, useState } from "react";
|
import type { FC } from "react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import useTheme from "@calcom/lib/hooks/useTheme";
|
import useTheme from "@calcom/lib/hooks/useTheme";
|
||||||
import { ITimezoneOption, TimezoneSelect } from "@calcom/ui";
|
import type { ITimezoneOption } from "@calcom/ui";
|
||||||
|
import { TimezoneSelect } from "@calcom/ui";
|
||||||
|
|
||||||
import useMeQuery from "@lib/hooks/useMeQuery";
|
import useMeQuery from "@lib/hooks/useMeQuery";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { FiGlobe } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
|
import { timeZone as localStorageTimeZone } from "@lib/clock";
|
||||||
|
|
||||||
|
import TimeOptions from "@components/booking/TimeOptions";
|
||||||
|
|
||||||
|
export function TimezoneDropdown({
|
||||||
|
onChangeTimeZone,
|
||||||
|
}: {
|
||||||
|
onChangeTimeZone: (newTimeZone: string) => void;
|
||||||
|
timeZone?: string;
|
||||||
|
}) {
|
||||||
|
const handleSelectTimeZone = (newTimeZone: string) => {
|
||||||
|
onChangeTimeZone(newTimeZone);
|
||||||
|
localStorageTimeZone(newTimeZone);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="dark:focus-within:bg-darkgray-200 dark:bg-darkgray-100 dark:hover:bg-darkgray-200 -mx-[2px] !mt-3 flex w-fit items-center rounded-[4px] px-1 py-[2px] text-sm font-medium focus-within:bg-gray-200 hover:bg-gray-100 [&_svg]:focus-within:text-gray-900 dark:[&_svg]:focus-within:text-white [&_p]:focus-within:text-gray-900 dark:[&_p]:focus-within:text-white">
|
||||||
|
<FiGlobe className="dark:text-darkgray-600 flex h-4 w-4 text-gray-600 ltr:mr-[2px] rtl:ml-[2px]" />
|
||||||
|
<TimeOptions onSelectTimeZone={handleSelectTimeZone} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { CAL_URL } from "@calcom/lib/constants";
|
import { CAL_URL } from "@calcom/lib/constants";
|
||||||
import { AvatarGroup, AvatarGroupProps } from "@calcom/ui";
|
import type { AvatarGroupProps } from "@calcom/ui";
|
||||||
|
import { AvatarGroup } from "@calcom/ui";
|
||||||
|
|
||||||
export const UserAvatars = ({
|
export const UserAvatars = ({
|
||||||
profile,
|
profile,
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
import dynamic from "next/dynamic";
|
||||||
import { EventType } from "@prisma/client";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useReducer, useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useReducer, useState } from "react";
|
||||||
import { Toaster } from "react-hot-toast";
|
|
||||||
import { FormattedNumber, IntlProvider } from "react-intl";
|
import { FormattedNumber, IntlProvider } from "react-intl";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager";
|
import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager";
|
||||||
import { getEventTypeAppData } from "@calcom/app-store/utils";
|
import { getEventTypeAppData } from "@calcom/app-store/utils";
|
||||||
import dayjs, { Dayjs } from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import {
|
import {
|
||||||
useEmbedNonStylesConfig,
|
useEmbedNonStylesConfig,
|
||||||
useEmbedStyles,
|
useEmbedStyles,
|
||||||
@@ -16,7 +14,6 @@ import {
|
|||||||
useIsBackgroundTransparent,
|
useIsBackgroundTransparent,
|
||||||
useIsEmbed,
|
useIsEmbed,
|
||||||
} from "@calcom/embed-core/embed-iframe";
|
} from "@calcom/embed-core/embed-iframe";
|
||||||
import DatePicker from "@calcom/features/calendars/DatePicker";
|
|
||||||
import CustomBranding from "@calcom/lib/CustomBranding";
|
import CustomBranding from "@calcom/lib/CustomBranding";
|
||||||
import classNames from "@calcom/lib/classNames";
|
import classNames from "@calcom/lib/classNames";
|
||||||
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
|
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
|
||||||
@@ -26,209 +23,30 @@ import notEmpty from "@calcom/lib/notEmpty";
|
|||||||
import { getRecurringFreq } from "@calcom/lib/recurringStrings";
|
import { getRecurringFreq } from "@calcom/lib/recurringStrings";
|
||||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||||
import { detectBrowserTimeFormat, setIs24hClockInLocalStorage, TimeFormat } from "@calcom/lib/timeFormat";
|
import { detectBrowserTimeFormat, setIs24hClockInLocalStorage, TimeFormat } from "@calcom/lib/timeFormat";
|
||||||
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
|
||||||
import { trpc } from "@calcom/trpc/react";
|
|
||||||
import { HeadSeo } from "@calcom/ui";
|
import { HeadSeo } from "@calcom/ui";
|
||||||
import { FiCreditCard, FiGlobe, FiRefreshCcw } from "@calcom/ui/components/icon";
|
import { FiCreditCard, FiRefreshCcw } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import { timeZone as localStorageTimeZone } from "@lib/clock";
|
import { timeZone as localStorageTimeZone } from "@lib/clock";
|
||||||
import useRouterQuery from "@lib/hooks/useRouterQuery";
|
|
||||||
|
|
||||||
import Gates, { Gate, GateState } from "@components/Gates";
|
import type { Gate, GateState } from "@components/Gates";
|
||||||
import AvailableTimes from "@components/booking/AvailableTimes";
|
import Gates from "@components/Gates";
|
||||||
import BookingDescription from "@components/booking/BookingDescription";
|
import BookingDescription from "@components/booking/BookingDescription";
|
||||||
import TimeOptions from "@components/booking/TimeOptions";
|
import { SlotPicker } from "@components/booking/SlotPicker";
|
||||||
import PoweredByCal from "@components/ui/PoweredByCal";
|
|
||||||
|
|
||||||
import type { AvailabilityPageProps } from "../../../pages/[user]/[type]";
|
import type { AvailabilityPageProps } from "../../../pages/[user]/[type]";
|
||||||
import type { DynamicAvailabilityPageProps } from "../../../pages/d/[link]/[slug]";
|
import type { DynamicAvailabilityPageProps } from "../../../pages/d/[link]/[slug]";
|
||||||
import type { AvailabilityTeamPageProps } from "../../../pages/team/[slug]/[type]";
|
import type { AvailabilityTeamPageProps } from "../../../pages/team/[slug]/[type]";
|
||||||
|
|
||||||
const useSlots = ({
|
const PoweredByCal = dynamic(() => import("@components/ui/PoweredByCal"));
|
||||||
eventTypeId,
|
|
||||||
eventTypeSlug,
|
|
||||||
startTime,
|
|
||||||
endTime,
|
|
||||||
usernameList,
|
|
||||||
timeZone,
|
|
||||||
duration,
|
|
||||||
}: {
|
|
||||||
eventTypeId: number;
|
|
||||||
eventTypeSlug: string;
|
|
||||||
startTime?: Dayjs;
|
|
||||||
endTime?: Dayjs;
|
|
||||||
usernameList: string[];
|
|
||||||
timeZone?: string;
|
|
||||||
duration?: string;
|
|
||||||
}) => {
|
|
||||||
const { data, isLoading, isPaused } = trpc.viewer.public.slots.getSchedule.useQuery(
|
|
||||||
{
|
|
||||||
eventTypeId,
|
|
||||||
eventTypeSlug,
|
|
||||||
usernameList,
|
|
||||||
startTime: startTime?.toISOString() || "",
|
|
||||||
endTime: endTime?.toISOString() || "",
|
|
||||||
timeZone,
|
|
||||||
duration,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enabled: !!startTime && !!endTime,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const [cachedSlots, setCachedSlots] = useState<NonNullable<typeof data>["slots"]>({});
|
|
||||||
|
|
||||||
useEffect(() => {
|
const Toaster = dynamic(() => import("react-hot-toast").then((mod) => mod.Toaster), { ssr: false });
|
||||||
if (data?.slots) {
|
/*const SlotPicker = dynamic(() => import("../SlotPicker").then((mod) => mod.SlotPicker), {
|
||||||
setCachedSlots((c) => ({ ...c, ...data?.slots }));
|
ssr: false,
|
||||||
}
|
loading: () => <div className="mt-8 px-4 pb-4 sm:mt-0 sm:p-4 md:min-w-[300px] md:px-5 lg:min-w-[455px]" />,
|
||||||
}, [data]);
|
});*/
|
||||||
|
const TimezoneDropdown = dynamic(() => import("../TimezoneDropdown").then((mod) => mod.TimezoneDropdown), {
|
||||||
// The very first time isPaused is set if auto-fetch is disabled, so isPaused should also be considered a loading state.
|
ssr: false,
|
||||||
return { slots: cachedSlots, isLoading: isLoading || isPaused };
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const SlotPicker = ({
|
|
||||||
eventType,
|
|
||||||
timeFormat,
|
|
||||||
onTimeFormatChange,
|
|
||||||
timeZone,
|
|
||||||
recurringEventCount,
|
|
||||||
users,
|
|
||||||
seatsPerTimeSlot,
|
|
||||||
weekStart = 0,
|
|
||||||
ethSignature,
|
|
||||||
}: {
|
|
||||||
eventType: Pick<
|
|
||||||
EventType & { metadata: z.infer<typeof EventTypeMetaDataSchema> },
|
|
||||||
"id" | "schedulingType" | "slug" | "length" | "metadata"
|
|
||||||
>;
|
|
||||||
timeFormat: TimeFormat;
|
|
||||||
onTimeFormatChange: (is24Hour: boolean) => void;
|
|
||||||
timeZone?: string;
|
|
||||||
seatsPerTimeSlot?: number;
|
|
||||||
recurringEventCount?: number;
|
|
||||||
users: string[];
|
|
||||||
weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
||||||
ethSignature?: string;
|
|
||||||
}) => {
|
|
||||||
const [selectedDate, setSelectedDate] = useState<Dayjs>();
|
|
||||||
const [browsingDate, setBrowsingDate] = useState<Dayjs>();
|
|
||||||
let { duration = eventType.length.toString() } = useRouterQuery("duration");
|
|
||||||
const { date, setQuery: setDate } = useRouterQuery("date");
|
|
||||||
const { month, setQuery: setMonth } = useRouterQuery("month");
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
if (!eventType.metadata?.multipleDuration) {
|
|
||||||
duration = eventType.length.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
const [slotPickerRef] = useAutoAnimate<HTMLDivElement>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!router.isReady) return;
|
|
||||||
|
|
||||||
// Etc/GMT is not actually a timeZone, so handle this select option explicitly to prevent a hard crash.
|
|
||||||
if (timeZone === "Etc/GMT") {
|
|
||||||
setBrowsingDate(dayjs.utc(month).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0));
|
|
||||||
if (date) {
|
|
||||||
setSelectedDate(dayjs.utc(date));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Set the start of the month without shifting time like startOf() may do.
|
|
||||||
setBrowsingDate(
|
|
||||||
dayjs.tz(month, timeZone).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0)
|
|
||||||
);
|
|
||||||
if (date) {
|
|
||||||
// It's important to set the date immediately to the timeZone, dayjs(date) will convert to browsertime.
|
|
||||||
setSelectedDate(dayjs.tz(date, timeZone));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [router.isReady, month, date, duration, timeZone]);
|
|
||||||
|
|
||||||
const { i18n, isLocaleReady } = useLocale();
|
|
||||||
const { slots: _1 } = useSlots({
|
|
||||||
eventTypeId: eventType.id,
|
|
||||||
eventTypeSlug: eventType.slug,
|
|
||||||
usernameList: users,
|
|
||||||
startTime: selectedDate?.startOf("day"),
|
|
||||||
endTime: selectedDate?.endOf("day"),
|
|
||||||
timeZone,
|
|
||||||
duration,
|
|
||||||
});
|
|
||||||
const { slots: _2, isLoading } = useSlots({
|
|
||||||
eventTypeId: eventType.id,
|
|
||||||
eventTypeSlug: eventType.slug,
|
|
||||||
usernameList: users,
|
|
||||||
startTime:
|
|
||||||
browsingDate === undefined || browsingDate.get("month") === dayjs.tz(undefined, timeZone).get("month")
|
|
||||||
? dayjs.tz(undefined, timeZone).subtract(2, "days").startOf("day")
|
|
||||||
: browsingDate?.startOf("month"),
|
|
||||||
endTime: browsingDate?.endOf("month"),
|
|
||||||
timeZone,
|
|
||||||
duration,
|
|
||||||
});
|
|
||||||
|
|
||||||
const slots = useMemo(() => ({ ..._2, ..._1 }), [_1, _2]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<DatePicker
|
|
||||||
isLoading={isLoading}
|
|
||||||
className={classNames(
|
|
||||||
"mt-8 px-4 pb-4 sm:mt-0 md:min-w-[300px] md:px-5 lg:min-w-[455px]",
|
|
||||||
selectedDate ? "sm:dark:border-darkgray-200 border-gray-200 sm:border-r sm:p-4 sm:pr-6" : "sm:p-4"
|
|
||||||
)}
|
|
||||||
includedDates={Object.keys(slots).filter((k) => slots[k].length > 0)}
|
|
||||||
locale={isLocaleReady ? i18n.language : "en"}
|
|
||||||
selected={selectedDate}
|
|
||||||
onChange={(newDate) => {
|
|
||||||
setDate(newDate.format("YYYY-MM-DD"));
|
|
||||||
}}
|
|
||||||
onMonthChange={(newMonth) => {
|
|
||||||
setMonth(newMonth.format("YYYY-MM"));
|
|
||||||
}}
|
|
||||||
browsingDate={browsingDate}
|
|
||||||
weekStart={weekStart}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div ref={slotPickerRef}>
|
|
||||||
<AvailableTimes
|
|
||||||
isLoading={isLoading}
|
|
||||||
slots={selectedDate && slots[selectedDate.format("YYYY-MM-DD")]}
|
|
||||||
date={selectedDate}
|
|
||||||
timeFormat={timeFormat}
|
|
||||||
onTimeFormatChange={onTimeFormatChange}
|
|
||||||
eventTypeId={eventType.id}
|
|
||||||
eventTypeSlug={eventType.slug}
|
|
||||||
seatsPerTimeSlot={seatsPerTimeSlot}
|
|
||||||
recurringCount={recurringEventCount}
|
|
||||||
ethSignature={ethSignature}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function TimezoneDropdown({
|
|
||||||
onChangeTimeZone,
|
|
||||||
}: {
|
|
||||||
onChangeTimeZone: (newTimeZone: string) => void;
|
|
||||||
timeZone?: string;
|
|
||||||
}) {
|
|
||||||
const handleSelectTimeZone = (newTimeZone: string) => {
|
|
||||||
onChangeTimeZone(newTimeZone);
|
|
||||||
localStorageTimeZone(newTimeZone);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="dark:focus-within:bg-darkgray-200 dark:bg-darkgray-100 dark:hover:bg-darkgray-200 -mx-[2px] !mt-3 flex w-fit items-center rounded-[4px] px-1 py-[2px] text-sm font-medium focus-within:bg-gray-200 hover:bg-gray-100 [&_svg]:focus-within:text-gray-900 dark:[&_svg]:focus-within:text-white [&_p]:focus-within:text-gray-900 dark:[&_p]:focus-within:text-white">
|
|
||||||
<FiGlobe className="dark:text-darkgray-600 flex h-4 w-4 text-gray-600 ltr:mr-[2px] rtl:ml-[2px]" />
|
|
||||||
<TimeOptions onSelectTimeZone={handleSelectTimeZone} />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const dateQuerySchema = z.object({
|
const dateQuerySchema = z.object({
|
||||||
rescheduleUid: z.string().optional().default(""),
|
rescheduleUid: z.string().optional().default(""),
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import { useSession } from "next-auth/react";
|
|||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useMemo, useReducer, useState } from "react";
|
import { useEffect, useMemo, useReducer, useState } from "react";
|
||||||
import { useFieldArray, useForm, useWatch, Controller } from "react-hook-form";
|
import { useFieldArray, useForm, useWatch } from "react-hook-form";
|
||||||
import { FormattedNumber, IntlProvider } from "react-intl";
|
import { FormattedNumber, IntlProvider } from "react-intl";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager";
|
import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager";
|
||||||
|
import type { EventLocationType } from "@calcom/app-store/locations";
|
||||||
import {
|
import {
|
||||||
EventLocationType,
|
|
||||||
getEventLocationType,
|
getEventLocationType,
|
||||||
getEventLocationValue,
|
getEventLocationValue,
|
||||||
getHumanReadableLocationValue,
|
getHumanReadableLocationValue,
|
||||||
@@ -21,7 +21,8 @@ import {
|
|||||||
} from "@calcom/app-store/locations";
|
} from "@calcom/app-store/locations";
|
||||||
import { createPaymentLink } from "@calcom/app-store/stripepayment/lib/client";
|
import { createPaymentLink } from "@calcom/app-store/stripepayment/lib/client";
|
||||||
import { getEventTypeAppData } from "@calcom/app-store/utils";
|
import { getEventTypeAppData } from "@calcom/app-store/utils";
|
||||||
import { LocationObject, LocationType } from "@calcom/core/location";
|
import type { LocationObject } from "@calcom/core/location";
|
||||||
|
import { LocationType } from "@calcom/core/location";
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import {
|
import {
|
||||||
useEmbedNonStylesConfig,
|
useEmbedNonStylesConfig,
|
||||||
@@ -69,12 +70,13 @@ import createBooking from "@lib/mutations/bookings/create-booking";
|
|||||||
import createRecurringBooking from "@lib/mutations/bookings/create-recurring-booking";
|
import createRecurringBooking from "@lib/mutations/bookings/create-recurring-booking";
|
||||||
import { parseDate, parseRecurringDates } from "@lib/parseDate";
|
import { parseDate, parseRecurringDates } from "@lib/parseDate";
|
||||||
|
|
||||||
import Gates, { Gate, GateState } from "@components/Gates";
|
import type { Gate, GateState } from "@components/Gates";
|
||||||
|
import Gates from "@components/Gates";
|
||||||
import BookingDescription from "@components/booking/BookingDescription";
|
import BookingDescription from "@components/booking/BookingDescription";
|
||||||
|
|
||||||
import { BookPageProps } from "../../../pages/[user]/book";
|
import type { BookPageProps } from "../../../pages/[user]/book";
|
||||||
import { HashLinkPageProps } from "../../../pages/d/[link]/book";
|
import type { HashLinkPageProps } from "../../../pages/d/[link]/book";
|
||||||
import { TeamBookingPageProps } from "../../../pages/team/[slug]/book";
|
import type { TeamBookingPageProps } from "../../../pages/team/[slug]/book";
|
||||||
|
|
||||||
type BookingPageProps = BookPageProps | TeamBookingPageProps | HashLinkPageProps;
|
type BookingPageProps = BookPageProps | TeamBookingPageProps | HashLinkPageProps;
|
||||||
|
|
||||||
|
|||||||
@@ -7,23 +7,24 @@ import { useEffect } from "react";
|
|||||||
import { Controller, useForm, useWatch } from "react-hook-form";
|
import { Controller, useForm, useWatch } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import type { EventLocationType, LocationObject } from "@calcom/app-store/locations";
|
||||||
import {
|
import {
|
||||||
EventLocationType,
|
|
||||||
getEventLocationType,
|
getEventLocationType,
|
||||||
getHumanReadableLocationValue,
|
getHumanReadableLocationValue,
|
||||||
getMessageForOrganizer,
|
getMessageForOrganizer,
|
||||||
LocationObject,
|
|
||||||
LocationType,
|
LocationType,
|
||||||
} from "@calcom/app-store/locations";
|
} from "@calcom/app-store/locations";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { RouterOutputs, trpc } from "@calcom/trpc/react";
|
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||||
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { Button, Dialog, DialogContent, DialogFooter, Form, PhoneInput } from "@calcom/ui";
|
import { Button, Dialog, DialogContent, DialogFooter, Form, PhoneInput } from "@calcom/ui";
|
||||||
import { FiMapPin } from "@calcom/ui/components/icon";
|
import { FiMapPin } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import { QueryCell } from "@lib/QueryCell";
|
import { QueryCell } from "@lib/QueryCell";
|
||||||
|
|
||||||
import CheckboxField from "@components/ui/form/CheckboxField";
|
import CheckboxField from "@components/ui/form/CheckboxField";
|
||||||
import LocationSelect, { LocationOption } from "@components/ui/form/LocationSelect";
|
import type { LocationOption } from "@components/ui/form/LocationSelect";
|
||||||
|
import LocationSelect from "@components/ui/form/LocationSelect";
|
||||||
|
|
||||||
type BookingItem = RouterOutputs["viewer"]["bookings"]["get"]["bookings"][number];
|
type BookingItem = RouterOutputs["viewer"]["bookings"]["get"]["bookings"][number];
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Dispatch, SetStateAction, useState } from "react";
|
import type { Dispatch, SetStateAction } from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { FormValues } from "pages/event-types/[type]";
|
import type { FormValues } from "pages/event-types/[type]";
|
||||||
import { Controller, useFormContext } from "react-hook-form";
|
import { Controller, useFormContext } from "react-hook-form";
|
||||||
import { components, OptionProps, SingleValueProps } from "react-select";
|
import type { OptionProps, SingleValueProps } from "react-select";
|
||||||
|
import { components } from "react-select";
|
||||||
|
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import classNames from "@calcom/lib/classNames";
|
import classNames from "@calcom/lib/classNames";
|
||||||
@@ -157,6 +158,7 @@ const EventTypeScheduleDetails = () => {
|
|||||||
</span>
|
</span>
|
||||||
<Button
|
<Button
|
||||||
href={`/availability/${schedule?.schedule.id}`}
|
href={`/availability/${schedule?.schedule.id}`}
|
||||||
|
disabled={isLoading}
|
||||||
color="minimal"
|
color="minimal"
|
||||||
EndIcon={FiExternalLink}
|
EndIcon={FiExternalLink}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||||
import { EventTypeCustomInputType } from "@prisma/client";
|
import { EventTypeCustomInputType } from "@prisma/client";
|
||||||
import type { CustomInputParsed } from "pages/event-types/[type]";
|
import type { CustomInputParsed } from "pages/event-types/[type]";
|
||||||
import { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { Control, Controller, useFieldArray, useForm, UseFormRegister, useWatch } from "react-hook-form";
|
import type { Control, UseFormRegister } from "react-hook-form";
|
||||||
|
import { Controller, useFieldArray, useForm, useWatch } from "react-hook-form";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Button, Label, Select, TextField } from "@calcom/ui";
|
import { Button, Label, Select, TextField } from "@calcom/ui";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
import { GetAppData, SetAppData } from "@calcom/app-store/EventTypeAppContext";
|
import type { GetAppData, SetAppData } from "@calcom/app-store/EventTypeAppContext";
|
||||||
import { EventTypeAppCard } from "@calcom/app-store/_components/EventTypeAppCardInterface";
|
import { EventTypeAppCard } from "@calcom/app-store/_components/EventTypeAppCardInterface";
|
||||||
import { EventTypeAppCardComponentProps } from "@calcom/app-store/types";
|
import type { EventTypeAppCardComponentProps } from "@calcom/app-store/types";
|
||||||
import { EventTypeAppsList } from "@calcom/app-store/utils";
|
import type { EventTypeAppsList } from "@calcom/app-store/utils";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { Button, EmptyScreen } from "@calcom/ui";
|
import { Button, EmptyScreen } from "@calcom/ui";
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||||
import * as RadioGroup from "@radix-ui/react-radio-group";
|
import * as RadioGroup from "@radix-ui/react-radio-group";
|
||||||
import { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Controller, useFormContext, UseFormRegisterReturn, useWatch } from "react-hook-form";
|
import type { UseFormRegisterReturn } from "react-hook-form";
|
||||||
|
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
||||||
|
|
||||||
import { classNames } from "@calcom/lib";
|
import { classNames } from "@calcom/lib";
|
||||||
import convertToNewDurationType, { DurationType } from "@calcom/lib/convertToNewDurationType";
|
import type { DurationType } from "@calcom/lib/convertToNewDurationType";
|
||||||
|
import convertToNewDurationType from "@calcom/lib/convertToNewDurationType";
|
||||||
import findDurationType from "@calcom/lib/findDurationType";
|
import findDurationType from "@calcom/lib/findDurationType";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { PeriodType } from "@calcom/prisma/client";
|
import type { PeriodType } from "@calcom/prisma/client";
|
||||||
import type { BookingLimit } from "@calcom/types/Calendar";
|
import type { BookingLimit } from "@calcom/types/Calendar";
|
||||||
import { Button, DateRangePicker, Input, InputField, Label, Select, SettingsToggle } from "@calcom/ui";
|
import { Button, DateRangePicker, Input, InputField, Label, Select, SettingsToggle } from "@calcom/ui";
|
||||||
import { FiPlus, FiTrash } from "@calcom/ui/components/icon";
|
import { FiPlus, FiTrash } from "@calcom/ui/components/icon";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EventTypeSetupProps } from "pages/event-types/[type]";
|
import type { EventTypeSetupProps } from "pages/event-types/[type]";
|
||||||
|
|
||||||
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
|
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
|
||||||
|
|
||||||
|
|||||||
@@ -6,15 +6,11 @@ import Link from "next/link";
|
|||||||
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, useForm, useFormContext } from "react-hook-form";
|
import { Controller, useForm, useFormContext } from "react-hook-form";
|
||||||
import { MultiValue } from "react-select";
|
import type { MultiValue } from "react-select";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import type { EventLocationType } from "@calcom/app-store/locations";
|
||||||
EventLocationType,
|
import { getEventLocationType, MeetLocationType, LocationType } from "@calcom/app-store/locations";
|
||||||
getEventLocationType,
|
|
||||||
MeetLocationType,
|
|
||||||
LocationType,
|
|
||||||
} from "@calcom/app-store/locations";
|
|
||||||
import { CAL_URL } from "@calcom/lib/constants";
|
import { CAL_URL } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { slugify } from "@calcom/lib/slugify";
|
import { slugify } from "@calcom/lib/slugify";
|
||||||
@@ -22,10 +18,8 @@ import { Button, Label, Select, SettingsToggle, Skeleton, TextField } from "@cal
|
|||||||
import { FiEdit2, FiCheck, FiX, FiPlus } from "@calcom/ui/components/icon";
|
import { FiEdit2, FiCheck, FiX, FiPlus } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import { EditLocationDialog } from "@components/dialog/EditLocationDialog";
|
import { EditLocationDialog } from "@components/dialog/EditLocationDialog";
|
||||||
import LocationSelect, {
|
import type { SingleValueLocationOption, LocationOption } from "@components/ui/form/LocationSelect";
|
||||||
SingleValueLocationOption,
|
import LocationSelect from "@components/ui/form/LocationSelect";
|
||||||
LocationOption,
|
|
||||||
} from "@components/ui/form/LocationSelect";
|
|
||||||
|
|
||||||
const getLocationFromType = (
|
const getLocationFromType = (
|
||||||
type: EventLocationType["type"],
|
type: EventLocationType["type"],
|
||||||
@@ -81,7 +75,7 @@ export const EventSetupTab = (
|
|||||||
setShowLocationModal(true);
|
setShowLocationModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeLocation = (selectedLocation: typeof eventType.locations[number]) => {
|
const removeLocation = (selectedLocation: (typeof eventType.locations)[number]) => {
|
||||||
formMethods.setValue(
|
formMethods.setValue(
|
||||||
"locations",
|
"locations",
|
||||||
formMethods.getValues("locations").filter((location) => {
|
formMethods.getValues("locations").filter((location) => {
|
||||||
@@ -196,6 +190,12 @@ export const EventSetupTab = (
|
|||||||
if (!eventLocationType) {
|
if (!eventLocationType) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
// We dont want to translate the string link - it doesnt exist in common.json and it gets prefixed/suffixed with __ or //
|
||||||
|
const eventLabel =
|
||||||
|
eventLocationType.defaultValueVariable === "link"
|
||||||
|
? eventLocationType.label
|
||||||
|
: t(location[eventLocationType.defaultValueVariable] || eventLocationType.label);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
key={`${location.type}${index}`}
|
key={`${location.type}${index}`}
|
||||||
@@ -207,9 +207,7 @@ export const EventSetupTab = (
|
|||||||
className="h-4 w-4"
|
className="h-4 w-4"
|
||||||
alt={`${eventLocationType.label} logo`}
|
alt={`${eventLocationType.label} logo`}
|
||||||
/>
|
/>
|
||||||
<span className="truncate text-sm ltr:ml-1 rtl:mr-1">
|
<span className="truncate text-sm ltr:ml-1 rtl:mr-1">{eventLabel}</span>
|
||||||
{t(location[eventLocationType.defaultValueVariable] || eventLocationType.label)}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { SchedulingType } from "@prisma/client";
|
import { SchedulingType } from "@prisma/client";
|
||||||
import { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||||
import { ComponentProps } from "react";
|
import type { ComponentProps } from "react";
|
||||||
import { Controller, useFormContext, useWatch, Control } from "react-hook-form";
|
import type { Control } from "react-hook-form";
|
||||||
import { Options } from "react-select";
|
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
||||||
|
import type { Options } from "react-select";
|
||||||
|
|
||||||
import CheckedTeamSelect, {
|
import type { CheckedSelectOption } from "@calcom/features/eventtypes/components/CheckedTeamSelect";
|
||||||
CheckedSelectOption,
|
import CheckedTeamSelect from "@calcom/features/eventtypes/components/CheckedTeamSelect";
|
||||||
} from "@calcom/features/eventtypes/components/CheckedTeamSelect";
|
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Label, Select } from "@calcom/ui";
|
import { Label, Select } from "@calcom/ui";
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { EventTypeSetupProps } from "pages/event-types/[type]";
|
import type { EventTypeSetupProps } from "pages/event-types/[type]";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { TbWebhook } from "react-icons/tb";
|
import { TbWebhook } from "react-icons/tb";
|
||||||
|
|
||||||
import { WebhookForm } from "@calcom/features/webhooks/components";
|
import { WebhookForm } from "@calcom/features/webhooks/components";
|
||||||
import { WebhookFormSubmitData } from "@calcom/features/webhooks/components/WebhookForm";
|
import type { WebhookFormSubmitData } from "@calcom/features/webhooks/components/WebhookForm";
|
||||||
import WebhookListItem from "@calcom/features/webhooks/components/WebhookListItem";
|
import WebhookListItem from "@calcom/features/webhooks/components/WebhookListItem";
|
||||||
import { APP_NAME } from "@calcom/lib/constants";
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Webhook } from "@calcom/prisma/client";
|
import type { Webhook } from "@calcom/prisma/client";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { Button, Dialog, DialogContent, EmptyScreen, showToast } from "@calcom/ui";
|
import { Button, Dialog, DialogContent, EmptyScreen, showToast } from "@calcom/ui";
|
||||||
import { FiPlus } from "@calcom/ui/components/icon";
|
import { FiPlus } from "@calcom/ui/components/icon";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { TFunction } from "next-i18next";
|
import type { TFunction } from "next-i18next";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||||
import { useMemo, useState, Suspense } from "react";
|
import { useMemo, useState, Suspense } from "react";
|
||||||
import { UseFormReturn } from "react-hook-form";
|
import type { UseFormReturn } from "react-hook-form";
|
||||||
import { TbWebhook } from "react-icons/tb";
|
import { TbWebhook } from "react-icons/tb";
|
||||||
|
|
||||||
import Shell from "@calcom/features/shell/Shell";
|
import Shell from "@calcom/features/shell/Shell";
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import * as RadioGroup from "@radix-ui/react-radio-group";
|
import * as RadioGroup from "@radix-ui/react-radio-group";
|
||||||
import { UnitTypeLongPlural } from "dayjs";
|
import type { UnitTypeLongPlural } from "dayjs";
|
||||||
import { Trans } from "next-i18next";
|
import { Trans } from "next-i18next";
|
||||||
import type { FormValues } from "pages/event-types/[type]";
|
import type { FormValues } from "pages/event-types/[type]";
|
||||||
import { Dispatch, SetStateAction, useEffect, useState } from "react";
|
import type { Dispatch, SetStateAction } from "react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
import { Controller, useFormContext } from "react-hook-form";
|
import { Controller, useFormContext } from "react-hook-form";
|
||||||
import z from "zod";
|
import type z from "zod";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||||
import { Alert, Input, Label, SettingsToggle } from "@calcom/ui";
|
import { Alert, Input, Label, SettingsToggle } from "@calcom/ui";
|
||||||
|
|
||||||
type RequiresConfirmationControllerProps = {
|
type RequiresConfirmationControllerProps = {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { RouterInputs, trpc } from "@calcom/trpc/react";
|
import type { RouterInputs } from "@calcom/trpc/react";
|
||||||
|
import { trpc } from "@calcom/trpc/react";
|
||||||
|
|
||||||
import DestinationCalendarSelector from "@components/DestinationCalendarSelector";
|
import DestinationCalendarSelector from "@components/DestinationCalendarSelector";
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import { useForm } from "react-hook-form";
|
|||||||
import { Schedule } from "@calcom/features/schedules";
|
import { Schedule } from "@calcom/features/schedules";
|
||||||
import { DEFAULT_SCHEDULE } from "@calcom/lib/availability";
|
import { DEFAULT_SCHEDULE } from "@calcom/lib/availability";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { trpc, TRPCClientErrorLike } from "@calcom/trpc/react";
|
import type { TRPCClientErrorLike } from "@calcom/trpc/react";
|
||||||
import { AppRouter } from "@calcom/trpc/server/routers/_app";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
|
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||||
import { Button, Form } from "@calcom/ui";
|
import { Button, Form } from "@calcom/ui";
|
||||||
|
|
||||||
interface ISetupAvailabilityProps {
|
interface ISetupAvailabilityProps {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { ArrowRightIcon } from "@heroicons/react/solid";
|
import { ArrowRightIcon } from "@heroicons/react/solid";
|
||||||
import MarkdownIt from "markdown-it";
|
import MarkdownIt from "markdown-it";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { FormEvent, useRef, useState } from "react";
|
import type { FormEvent } from "react";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||||
import turndown from "@calcom/lib/turndownService";
|
import turndown from "@calcom/lib/turndownService";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { Button, Editor, ImageUploader, Label, showToast } from "@calcom/ui";
|
import { Button, Editor, ImageUploader, Label, showToast } from "@calcom/ui";
|
||||||
@@ -34,6 +36,7 @@ const UserProfile = (props: IUserProfileProps) => {
|
|||||||
const utils = trpc.useContext();
|
const utils = trpc.useContext();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const createEventType = trpc.viewer.eventTypes.create.useMutation();
|
const createEventType = trpc.viewer.eventTypes.create.useMutation();
|
||||||
|
const telemetry = useTelemetry();
|
||||||
|
|
||||||
const mutation = trpc.viewer.updateProfile.useMutation({
|
const mutation = trpc.viewer.updateProfile.useMutation({
|
||||||
onSuccess: async (_data, context) => {
|
onSuccess: async (_data, context) => {
|
||||||
@@ -64,6 +67,8 @@ const UserProfile = (props: IUserProfileProps) => {
|
|||||||
const onSubmit = handleSubmit((data: { bio: string }) => {
|
const onSubmit = handleSubmit((data: { bio: string }) => {
|
||||||
const { bio } = data;
|
const { bio } = data;
|
||||||
|
|
||||||
|
telemetry.event(telemetryEventTypes.onboardingFinished);
|
||||||
|
|
||||||
mutation.mutate({
|
mutation.mutate({
|
||||||
bio,
|
bio,
|
||||||
completedOnboarding: true,
|
completedOnboarding: true,
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { ArrowRightIcon } from "@heroicons/react/outline";
|
import { ArrowRightIcon } from "@heroicons/react/outline";
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { Button, TimezoneSelect } from "@calcom/ui";
|
import { Button, TimezoneSelect } from "@calcom/ui";
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ const UserSettings = (props: IUserSettingsProps) => {
|
|||||||
const { user, nextStep } = props;
|
const { user, nextStep } = props;
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const [selectedTimeZone, setSelectedTimeZone] = useState(dayjs.tz.guess());
|
const [selectedTimeZone, setSelectedTimeZone] = useState(dayjs.tz.guess());
|
||||||
|
const telemetry = useTelemetry();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -31,6 +32,11 @@ const UserSettings = (props: IUserSettingsProps) => {
|
|||||||
},
|
},
|
||||||
reValidateMode: "onChange",
|
reValidateMode: "onChange",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
telemetry.event(telemetryEventTypes.onboardingStarted);
|
||||||
|
}, [telemetry]);
|
||||||
|
|
||||||
const defaultOptions = { required: true, maxLength: 255 };
|
const defaultOptions = { required: true, maxLength: 255 };
|
||||||
|
|
||||||
const utils = trpc.useContext();
|
const utils = trpc.useContext();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import { Badge } from "@calcom/ui";
|
import { Badge } from "@calcom/ui";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { SyntheticEvent, useState } from "react";
|
import type { SyntheticEvent } from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
import { ErrorCode } from "@calcom/lib/auth";
|
import { ErrorCode } from "@calcom/lib/auth";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { BaseSyntheticEvent, useState } from "react";
|
import type { BaseSyntheticEvent } from "react";
|
||||||
|
import React, { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { ErrorCode } from "@calcom/lib/auth";
|
import { ErrorCode } from "@calcom/lib/auth";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { BaseSyntheticEvent, useState } from "react";
|
import type { BaseSyntheticEvent } from "react";
|
||||||
|
import React, { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { ErrorCode } from "@calcom/lib/auth";
|
import { ErrorCode } from "@calcom/lib/auth";
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import * as z from "zod";
|
|||||||
import { classNames } from "@calcom/lib";
|
import { classNames } from "@calcom/lib";
|
||||||
import { CONSOLE_URL } from "@calcom/lib/constants";
|
import { CONSOLE_URL } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { RouterInputs, RouterOutputs, trpc } from "@calcom/trpc/react";
|
import type { RouterInputs, RouterOutputs } from "@calcom/trpc/react";
|
||||||
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { Button, TextField } from "@calcom/ui";
|
import { Button, TextField } from "@calcom/ui";
|
||||||
import { FiCheck, FiExternalLink, FiLoader } from "@calcom/ui/components/icon";
|
import { FiCheck, FiExternalLink, FiLoader } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { Dispatch, SetStateAction } from "react";
|
import type { Dispatch, SetStateAction } from "react";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { FiCheck } from "@calcom/ui/components/icon";
|
import { FiCheck } from "@calcom/ui/components/icon";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import MarkdownIt from "markdown-it";
|
import MarkdownIt from "markdown-it";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { TeamPageProps } from "pages/team/[slug]";
|
import type { TeamPageProps } from "pages/team/[slug]";
|
||||||
|
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { FC, Fragment } from "react";
|
import type { FC } from "react";
|
||||||
|
import { Fragment } from "react";
|
||||||
|
|
||||||
type AdminRequiredProps = {
|
type AdminRequiredProps = {
|
||||||
as?: keyof JSX.IntrinsicElements;
|
as?: keyof JSX.IntrinsicElements;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { ControllerRenderProps } from "react-hook-form";
|
import type { ControllerRenderProps } from "react-hook-form";
|
||||||
|
|
||||||
import { FiEdit2 } from "@calcom/ui/components/icon";
|
import { FiEdit2 } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { SVGComponent } from "@lib/types/SVGComponent";
|
import type { SVGComponent } from "@lib/types/SVGComponent";
|
||||||
|
|
||||||
interface LinkIconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
interface LinkIconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||||
Icon: SVGComponent;
|
Icon: SVGComponent;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React, { PropsWithChildren } from "react";
|
import type { PropsWithChildren } from "react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
import { Dialog, DialogContent } from "@calcom/ui";
|
import { Dialog, DialogContent } from "@calcom/ui";
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { debounce, noop } from "lodash";
|
import { debounce, noop } from "lodash";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { RefCallback, useEffect, useMemo, useState } from "react";
|
import type { RefCallback } from "react";
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
import { getPremiumPlanPriceValue } from "@calcom/app-store/stripepayment/lib/utils";
|
import { getPremiumPlanPriceValue } from "@calcom/app-store/stripepayment/lib/utils";
|
||||||
import { fetchUsername } from "@calcom/lib/fetchUsername";
|
import { fetchUsername } from "@calcom/lib/fetchUsername";
|
||||||
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
|
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { User } from "@calcom/prisma/client";
|
import type { User } from "@calcom/prisma/client";
|
||||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||||
import { RouterOutputs, trpc } from "@calcom/trpc/react";
|
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||||
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||||
import { Button, Dialog, DialogClose, DialogContent, DialogHeader, Input, Label } from "@calcom/ui";
|
import { Button, Dialog, DialogClose, DialogContent, DialogHeader, Input, Label } from "@calcom/ui";
|
||||||
import { FiCheck, FiEdit2, FiExternalLink, StarIconSolid } from "@calcom/ui/components/icon";
|
import { FiCheck, FiEdit2, FiExternalLink, StarIconSolid } from "@calcom/ui/components/icon";
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { debounce, noop } from "lodash";
|
import { debounce, noop } from "lodash";
|
||||||
import { RefCallback, useEffect, useMemo, useState } from "react";
|
import type { RefCallback } from "react";
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
import { fetchUsername } from "@calcom/lib/fetchUsername";
|
import { fetchUsername } from "@calcom/lib/fetchUsername";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { AppRouter } from "@calcom/trpc/server/routers/_app";
|
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||||
import { Button, Dialog, DialogClose, DialogContent, DialogHeader, TextField } from "@calcom/ui";
|
import { Button, Dialog, DialogClose, DialogContent, DialogHeader, TextField } from "@calcom/ui";
|
||||||
import { FiCheck, FiEdit2 } from "@calcom/ui/components/icon";
|
import { FiCheck, FiEdit2 } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
|
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
|
||||||
import { User } from "@calcom/prisma/client";
|
import type { User } from "@calcom/prisma/client";
|
||||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||||
import { AppRouter } from "@calcom/trpc/server/routers/_app";
|
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||||
|
|
||||||
|
import useRouterQuery from "@lib/hooks/useRouterQuery";
|
||||||
|
|
||||||
import { PremiumTextfield } from "./PremiumTextfield";
|
import { PremiumTextfield } from "./PremiumTextfield";
|
||||||
import { UsernameTextfield } from "./UsernameTextfield";
|
import { UsernameTextfield } from "./UsernameTextfield";
|
||||||
@@ -21,7 +24,13 @@ export const UsernameAvailabilityField = ({
|
|||||||
onErrorMutation,
|
onErrorMutation,
|
||||||
user,
|
user,
|
||||||
}: UsernameAvailabilityFieldProps) => {
|
}: UsernameAvailabilityFieldProps) => {
|
||||||
const [currentUsername, setCurrentUsername] = useState(user.username ?? "");
|
const router = useRouter();
|
||||||
|
const [currentUsernameState, setCurrentUsernameState] = useState(user.username || "");
|
||||||
|
const { username: usernameFromQuery, setQuery: setUsernameFromQuery } = useRouterQuery("username");
|
||||||
|
const { username: currentUsername, setQuery: setCurrentUsername } =
|
||||||
|
router.query["username"] && user.username === null
|
||||||
|
? { username: usernameFromQuery, setQuery: setUsernameFromQuery }
|
||||||
|
: { username: currentUsernameState || "", setQuery: setCurrentUsernameState };
|
||||||
const formMethods = useForm({
|
const formMethods = useForm({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
username: currentUsername,
|
username: currentUsername,
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
import { useCallback, useRef, useState } from "react";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
import { HexColorInput, HexColorPicker } from "react-colorful";
|
|
||||||
|
|
||||||
import { isValidHexCode, fallBackHex } from "@calcom/lib/CustomBranding";
|
|
||||||
import { Swatch } from "@calcom/ui";
|
|
||||||
|
|
||||||
type Handler = (event: MouseEvent | Event) => void;
|
|
||||||
function useEventListener<
|
|
||||||
KW extends keyof WindowEventMap,
|
|
||||||
KH extends keyof HTMLElementEventMap,
|
|
||||||
T extends HTMLElement | void = void
|
|
||||||
>(
|
|
||||||
eventName: KW | KH,
|
|
||||||
handler: (event: WindowEventMap[KW] | HTMLElementEventMap[KH] | Event) => void,
|
|
||||||
element?: React.RefObject<T>
|
|
||||||
) {
|
|
||||||
// Create a ref that stores handler
|
|
||||||
const savedHandler = useRef<typeof handler>();
|
|
||||||
useEffect(() => {
|
|
||||||
// Define the listening target
|
|
||||||
const targetElement: T | Window = element?.current || window;
|
|
||||||
if (!(targetElement && targetElement.addEventListener)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Update saved handler if necessary
|
|
||||||
if (savedHandler.current !== handler) {
|
|
||||||
savedHandler.current = handler;
|
|
||||||
}
|
|
||||||
// Create event listener that calls handler function stored in ref
|
|
||||||
const eventListener: typeof handler = (event) => {
|
|
||||||
// eslint-disable-next-line no-extra-boolean-cast
|
|
||||||
if (!!savedHandler?.current) {
|
|
||||||
savedHandler.current(event);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
targetElement.addEventListener(eventName, eventListener);
|
|
||||||
// Remove event listener on cleanup
|
|
||||||
return () => {
|
|
||||||
targetElement.removeEventListener(eventName, eventListener);
|
|
||||||
};
|
|
||||||
}, [eventName, element, handler]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function useOnClickOutside<T extends HTMLElement = HTMLElement>(
|
|
||||||
ref: React.RefObject<T>,
|
|
||||||
handler: Handler,
|
|
||||||
mouseEvent: "mousedown" | "mouseup" = "mousedown"
|
|
||||||
): void {
|
|
||||||
useEventListener(mouseEvent, (event) => {
|
|
||||||
const el = ref?.current;
|
|
||||||
// Do nothing if clicking ref's element or descendent elements
|
|
||||||
if (!el || el.contains(event.target as Node)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handler(event);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
export type ColorPickerProps = {
|
|
||||||
defaultValue: string;
|
|
||||||
onChange: (text: string) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ColorPicker = (props: ColorPickerProps) => {
|
|
||||||
const init = !isValidHexCode(props.defaultValue)
|
|
||||||
? fallBackHex(props.defaultValue, false)
|
|
||||||
: props.defaultValue;
|
|
||||||
const [color, setColor] = useState(init);
|
|
||||||
const [isOpen, toggle] = useState(false);
|
|
||||||
const popover = useRef() as React.MutableRefObject<HTMLInputElement>;
|
|
||||||
const close = useCallback(() => toggle(false), []);
|
|
||||||
useOnClickOutside(popover, close);
|
|
||||||
return (
|
|
||||||
<div className="relative mt-1 flex items-center justify-center">
|
|
||||||
<Swatch size="sm" backgroundColor={color} onClick={() => toggle(!isOpen)} />
|
|
||||||
|
|
||||||
{isOpen && (
|
|
||||||
<div className="popover" ref={popover}>
|
|
||||||
<HexColorPicker
|
|
||||||
className="!absolute !top-10 !left-0 !z-10 !h-32 !w-32"
|
|
||||||
color={color}
|
|
||||||
onChange={(val) => {
|
|
||||||
setColor(val);
|
|
||||||
props.onChange(val);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<HexColorInput
|
|
||||||
className="ml-1 block w-full rounded-r-md border border-gray-300 px-3 py-2 sm:text-sm"
|
|
||||||
color={color}
|
|
||||||
onChange={(val) => {
|
|
||||||
setColor(val);
|
|
||||||
props.onChange(val);
|
|
||||||
}}
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ColorPicker;
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { forwardRef, InputHTMLAttributes } from "react";
|
import type { InputHTMLAttributes } from "react";
|
||||||
|
import React, { forwardRef } from "react";
|
||||||
|
|
||||||
import classNames from "@calcom/lib/classNames";
|
import classNames from "@calcom/lib/classNames";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Props } from "react-select";
|
import type { Props } from "react-select";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Avatar } from "@calcom/ui";
|
import { Avatar } from "@calcom/ui";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { components, GroupBase, Props, SingleValue } from "react-select";
|
import type { GroupBase, Props, SingleValue } from "react-select";
|
||||||
|
import { components } from "react-select";
|
||||||
|
|
||||||
import type { EventLocationType } from "@calcom/app-store/locations";
|
import type { EventLocationType } from "@calcom/app-store/locations";
|
||||||
import { classNames } from "@calcom/lib";
|
import { classNames } from "@calcom/lib";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React, { forwardRef, InputHTMLAttributes, ReactNode } from "react";
|
import type { InputHTMLAttributes, ReactNode } from "react";
|
||||||
|
import React, { forwardRef } from "react";
|
||||||
|
|
||||||
type Props = InputHTMLAttributes<HTMLInputElement> & {
|
type Props = InputHTMLAttributes<HTMLInputElement> & {
|
||||||
label?: ReactNode;
|
label?: ReactNode;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import ReactSelect, { components, GroupBase, Props, InputProps, SingleValue, MultiValue } from "react-select";
|
import type { GroupBase, Props, InputProps, SingleValue, MultiValue } from "react-select";
|
||||||
|
import ReactSelect, { components } from "react-select";
|
||||||
|
|
||||||
import classNames from "@calcom/lib/classNames";
|
import classNames from "@calcom/lib/classNames";
|
||||||
import useTheme from "@calcom/lib/hooks/useTheme";
|
import useTheme from "@calcom/lib/hooks/useTheme";
|
||||||
@@ -28,7 +29,7 @@ function Select<
|
|||||||
IsMulti extends boolean = false,
|
IsMulti extends boolean = false,
|
||||||
Group extends GroupBase<Option> = GroupBase<Option>
|
Group extends GroupBase<Option> = GroupBase<Option>
|
||||||
>({ className, ...props }: SelectProps<Option, IsMulti, Group>) {
|
>({ className, ...props }: SelectProps<Option, IsMulti, Group>) {
|
||||||
const { resolvedTheme, forcedTheme, isReady } = useTheme();
|
const { resolvedTheme, forcedTheme /*isReady*/ } = useTheme();
|
||||||
const hasDarkTheme = !forcedTheme && resolvedTheme === "dark";
|
const hasDarkTheme = !forcedTheme && resolvedTheme === "dark";
|
||||||
const darkThemeColors = {
|
const darkThemeColors = {
|
||||||
/** Dark Theme starts */
|
/** Dark Theme starts */
|
||||||
@@ -73,9 +74,9 @@ function Select<
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Till we know in JS the theme is ready, we can't render react-select as it would render with light theme instead
|
// Till we know in JS the theme is ready, we can't render react-select as it would render with light theme instead
|
||||||
if (!isReady) {
|
// if (!isReady) {
|
||||||
return <input type="text" className={className} />;
|
// return <input type="text" className={className} />;
|
||||||
}
|
// }
|
||||||
return (
|
return (
|
||||||
<ReactSelect
|
<ReactSelect
|
||||||
theme={(theme) => ({
|
theme={(theme) => ({
|
||||||
|
|||||||
Binary file not shown.
@@ -1,11 +1,11 @@
|
|||||||
import {
|
import type {
|
||||||
QueryObserverLoadingErrorResult,
|
QueryObserverLoadingErrorResult,
|
||||||
QueryObserverLoadingResult,
|
QueryObserverLoadingResult,
|
||||||
QueryObserverRefetchErrorResult,
|
QueryObserverRefetchErrorResult,
|
||||||
QueryObserverSuccessResult,
|
QueryObserverSuccessResult,
|
||||||
UseQueryResult,
|
UseQueryResult,
|
||||||
} from "@tanstack/react-query";
|
} from "@tanstack/react-query";
|
||||||
import { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
|
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||||
@@ -88,7 +88,7 @@ const withQuery = <
|
|||||||
TInput = inferProcedureInput<TQuery>,
|
TInput = inferProcedureInput<TQuery>,
|
||||||
TOutput = inferProcedureOutput<TQuery>
|
TOutput = inferProcedureOutput<TQuery>
|
||||||
>(
|
>(
|
||||||
queryProcedure: DecorateProcedure<TQuery, any>,
|
queryProcedure: DecorateProcedure<TQuery, any, any>,
|
||||||
input?: TInput,
|
input?: TInput,
|
||||||
params?: UseTRPCQueryOptions<any, TInput, TOutput, TOutput, TError>
|
params?: UseTRPCQueryOptions<any, TInput, TOutput, TOutput, TError>
|
||||||
) => {
|
) => {
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { TooltipProvider } from "@radix-ui/react-tooltip";
|
import { TooltipProvider } from "@radix-ui/react-tooltip";
|
||||||
import { SessionProvider } from "next-auth/react";
|
import { SessionProvider } from "next-auth/react";
|
||||||
import { EventCollectionProvider } from "next-collect/client";
|
import { EventCollectionProvider } from "next-collect/client";
|
||||||
import { appWithTranslation, SSRConfig } from "next-i18next";
|
import type { SSRConfig } from "next-i18next";
|
||||||
|
import { appWithTranslation } from "next-i18next";
|
||||||
import { ThemeProvider } from "next-themes";
|
import { ThemeProvider } from "next-themes";
|
||||||
import type { AppProps as NextAppProps, AppProps as NextJsAppProps } from "next/app";
|
import type { AppProps as NextAppProps, AppProps as NextJsAppProps } from "next/app";
|
||||||
import { NextRouter } from "next/router";
|
import type { NextRouter } from "next/router";
|
||||||
import { ComponentProps, ReactNode } from "react";
|
import type { ComponentProps, ReactNode } from "react";
|
||||||
|
|
||||||
import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic";
|
import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic";
|
||||||
import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic";
|
import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic";
|
||||||
@@ -13,7 +14,7 @@ import { trpc } from "@calcom/trpc/react";
|
|||||||
import { MetaProvider } from "@calcom/ui";
|
import { MetaProvider } from "@calcom/ui";
|
||||||
|
|
||||||
import usePublicPage from "@lib/hooks/usePublicPage";
|
import usePublicPage from "@lib/hooks/usePublicPage";
|
||||||
import { WithNonceProps } from "@lib/withNonce";
|
import type { WithNonceProps } from "@lib/withNonce";
|
||||||
|
|
||||||
const I18nextAdapter = appWithTranslation<NextJsAppProps<SSRConfig> & { children: React.ReactNode }>(
|
const I18nextAdapter = appWithTranslation<NextJsAppProps<SSRConfig> & { children: React.ReactNode }>(
|
||||||
({ children }) => <>{children}</>
|
({ children }) => <>{children}</>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { IdentityProvider } from "@prisma/client";
|
import { IdentityProvider } from "@prisma/client";
|
||||||
import { compare, hash } from "bcryptjs";
|
import { compare, hash } from "bcryptjs";
|
||||||
import { Session } from "next-auth";
|
import type { Session } from "next-auth";
|
||||||
import { getSession as getSessionInner, GetSessionParams } from "next-auth/react";
|
import type { GetSessionParams } from "next-auth/react";
|
||||||
|
import { getSession as getSessionInner } from "next-auth/react";
|
||||||
|
|
||||||
/** @deprecated use the one from `@calcom/lib/auth` */
|
/** @deprecated use the one from `@calcom/lib/auth` */
|
||||||
export async function hashPassword(password: string) {
|
export async function hashPassword(password: string) {
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
import { Account, IdentityProvider, Prisma, PrismaClient, User, VerificationToken } from "@prisma/client";
|
import type {
|
||||||
|
Account,
|
||||||
|
IdentityProvider,
|
||||||
|
Prisma,
|
||||||
|
PrismaClient,
|
||||||
|
User,
|
||||||
|
VerificationToken,
|
||||||
|
} from "@prisma/client";
|
||||||
import { PrismaClientKnownRequestError } from "@prisma/client/runtime";
|
import { PrismaClientKnownRequestError } from "@prisma/client/runtime";
|
||||||
|
|
||||||
import { identityProviderNameMap } from "@lib/auth";
|
import { identityProviderNameMap } from "@lib/auth";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DefaultSeoProps, NextSeoProps } from "next-seo";
|
import type { DefaultSeoProps, NextSeoProps } from "next-seo";
|
||||||
|
|
||||||
import { APP_NAME, SEO_IMG_DEFAULT, SEO_IMG_OGIMG } from "@calcom/lib/constants";
|
import { APP_NAME, SEO_IMG_DEFAULT, SEO_IMG_OGIMG } from "@calcom/lib/constants";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import parser from "accept-language-parser";
|
import parser from "accept-language-parser";
|
||||||
import { IncomingMessage } from "http";
|
import type { IncomingMessage } from "http";
|
||||||
|
|
||||||
import { Maybe } from "@calcom/trpc/server";
|
import type { Maybe } from "@calcom/trpc/server";
|
||||||
|
|
||||||
import { getSession } from "@lib/auth";
|
import { getSession } from "@lib/auth";
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import { IncomingMessage, OutgoingMessage } from "http";
|
import type { IncomingMessage, OutgoingMessage } from "http";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IS_PRODUCTION } from "@calcom/lib/constants";
|
import { IS_PRODUCTION } from "@calcom/lib/constants";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Prisma, PrismaClient } from "@prisma/client";
|
import type { Prisma, PrismaClient } from "@prisma/client";
|
||||||
|
|
||||||
async function getBooking(prisma: PrismaClient, uid: string) {
|
async function getBooking(prisma: PrismaClient, uid: string) {
|
||||||
const booking = await prisma.booking.findFirst({
|
const booking = await prisma.booking.findFirst({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as fetch from "@lib/core/http/fetch-wrapper";
|
import * as fetch from "@lib/core/http/fetch-wrapper";
|
||||||
import { BookingCreateBody, BookingResponse } from "@lib/types/booking";
|
import type { BookingCreateBody, BookingResponse } from "@lib/types/booking";
|
||||||
|
|
||||||
const createBooking = async (data: BookingCreateBody) => {
|
const createBooking = async (data: BookingCreateBody) => {
|
||||||
const response = await fetch.post<BookingCreateBody, BookingResponse>("/api/book/event", data);
|
const response = await fetch.post<BookingCreateBody, BookingResponse>("/api/book/event", data);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { AppsStatus } from "@calcom/types/Calendar";
|
import type { AppsStatus } from "@calcom/types/Calendar";
|
||||||
|
|
||||||
import * as fetch from "@lib/core/http/fetch-wrapper";
|
import * as fetch from "@lib/core/http/fetch-wrapper";
|
||||||
import { BookingCreateBody, BookingResponse } from "@lib/types/booking";
|
import type { BookingCreateBody, BookingResponse } from "@lib/types/booking";
|
||||||
|
|
||||||
type ExtendedBookingCreateBody = BookingCreateBody & {
|
type ExtendedBookingCreateBody = BookingCreateBody & {
|
||||||
noEmail?: boolean;
|
noEmail?: boolean;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { I18n } from "next-i18next";
|
import type { I18n } from "next-i18next";
|
||||||
import { RRule } from "rrule";
|
import { RRule } from "rrule";
|
||||||
|
|
||||||
import dayjs, { Dayjs } from "@calcom/dayjs";
|
import type { Dayjs } from "@calcom/dayjs";
|
||||||
|
import dayjs from "@calcom/dayjs";
|
||||||
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
|
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
|
||||||
import type { RecurringEvent } from "@calcom/types/Calendar";
|
import type { RecurringEvent } from "@calcom/types/Calendar";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Attendee, Booking } from "@prisma/client";
|
import type { Attendee, Booking } from "@prisma/client";
|
||||||
|
|
||||||
import { AppsStatus } from "@calcom/types/Calendar";
|
import type { AppsStatus } from "@calcom/types/Calendar";
|
||||||
|
|
||||||
export type BookingCreateBody = {
|
export type BookingCreateBody = {
|
||||||
email: string;
|
email: string;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GetServerSideProps, GetServerSidePropsContext, GetServerSidePropsResult } from "next";
|
import type { GetServerSideProps, GetServerSidePropsContext, GetServerSidePropsResult } from "next";
|
||||||
|
|
||||||
export type EmbedProps = {
|
export type EmbedProps = {
|
||||||
isEmbed?: boolean;
|
isEmbed?: boolean;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GetServerSideProps, GetServerSidePropsContext } from "next";
|
import type { GetServerSideProps, GetServerSidePropsContext } from "next";
|
||||||
|
|
||||||
import { csp } from "@lib/csp";
|
import { csp } from "@lib/csp";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { get } from "@vercel/edge-config";
|
import { get } from "@vercel/edge-config";
|
||||||
import { collectEvents } from "next-collect/server";
|
import { collectEvents } from "next-collect/server";
|
||||||
import { NextMiddleware, NextResponse, userAgent } from "next/server";
|
import type { NextMiddleware } from "next/server";
|
||||||
|
import { NextResponse, userAgent } from "next/server";
|
||||||
|
|
||||||
import { CONSOLE_URL, WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants";
|
import { CONSOLE_URL, WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants";
|
||||||
import { isIpInBanlist } from "@calcom/lib/getIP";
|
import { isIpInBanlist } from "@calcom/lib/getIP";
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ if (process.env.ANALYZE === "true") {
|
|||||||
|
|
||||||
plugins.push(withTM);
|
plugins.push(withTM);
|
||||||
plugins.push(withAxiom);
|
plugins.push(withAxiom);
|
||||||
|
|
||||||
/** @type {import("next").NextConfig} */
|
/** @type {import("next").NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
i18n,
|
i18n,
|
||||||
@@ -104,7 +103,7 @@ const nextConfig = {
|
|||||||
images: {
|
images: {
|
||||||
unoptimized: true,
|
unoptimized: true,
|
||||||
},
|
},
|
||||||
webpack: (config) => {
|
webpack: (config, { webpack, buildId }) => {
|
||||||
config.plugins.push(
|
config.plugins.push(
|
||||||
new CopyWebpackPlugin({
|
new CopyWebpackPlugin({
|
||||||
patterns: [
|
patterns: [
|
||||||
@@ -126,6 +125,8 @@ const nextConfig = {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
config.plugins.push(new webpack.DefinePlugin({ "process.env.BUILD_ID": JSON.stringify(buildId) }));
|
||||||
|
|
||||||
config.resolve.fallback = {
|
config.resolve.fallback = {
|
||||||
...config.resolve.fallback, // if you miss it, all the other options in fallback, specified
|
...config.resolve.fallback, // if you miss it, all the other options in fallback, specified
|
||||||
// by next.js will be dropped. Doesn't make much sense, but how it is
|
// by next.js will be dropped. Doesn't make much sense, but how it is
|
||||||
|
|||||||
+17
-8
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@calcom/web",
|
"name": "@calcom/web",
|
||||||
"version": "2.5.12",
|
"version": "2.6.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"analyze": "ANALYZE=true next build",
|
"analyze": "ANALYZE=true next build",
|
||||||
@@ -44,13 +44,14 @@
|
|||||||
"@hookform/error-message": "^2.0.0",
|
"@hookform/error-message": "^2.0.0",
|
||||||
"@hookform/resolvers": "^2.9.7",
|
"@hookform/resolvers": "^2.9.7",
|
||||||
"@next-auth/prisma-adapter": "^1.0.4",
|
"@next-auth/prisma-adapter": "^1.0.4",
|
||||||
"@next/bundle-analyzer": "^12.2.5",
|
"@next/bundle-analyzer": "^13.1.6",
|
||||||
|
"@next/font": "^13.1.1",
|
||||||
"@radix-ui/react-avatar": "^1.0.0",
|
"@radix-ui/react-avatar": "^1.0.0",
|
||||||
"@radix-ui/react-collapsible": "^1.0.0",
|
"@radix-ui/react-collapsible": "^1.0.0",
|
||||||
"@radix-ui/react-dialog": "^1.0.0",
|
"@radix-ui/react-dialog": "^1.0.0",
|
||||||
"@radix-ui/react-dropdown-menu": "^1.0.0",
|
"@radix-ui/react-dropdown-menu": "^1.0.0",
|
||||||
"@radix-ui/react-id": "^1.0.0",
|
"@radix-ui/react-id": "^1.0.0",
|
||||||
"@radix-ui/react-popover": "^1.0.0",
|
"@radix-ui/react-popover": "^1.0.2",
|
||||||
"@radix-ui/react-radio-group": "^1.0.0",
|
"@radix-ui/react-radio-group": "^1.0.0",
|
||||||
"@radix-ui/react-slider": "^1.0.0",
|
"@radix-ui/react-slider": "^1.0.0",
|
||||||
"@radix-ui/react-switch": "^1.0.0",
|
"@radix-ui/react-switch": "^1.0.0",
|
||||||
@@ -69,7 +70,7 @@
|
|||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"dotenv-cli": "^6.0.0",
|
"dotenv-cli": "^6.0.0",
|
||||||
"eslint-config-next": "^13.1.1",
|
"eslint-config-next": "^13.1.6",
|
||||||
"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",
|
||||||
@@ -80,11 +81,12 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lottie-react": "^2.3.1",
|
"lottie-react": "^2.3.1",
|
||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
|
"md5": "^2.3.0",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
"micro": "^9.4.1",
|
"micro": "^10.0.1",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"next": "^13.1.1",
|
"next": "^13.1.1",
|
||||||
"next-auth": "^4.10.3",
|
"next-auth": "^4.18.8",
|
||||||
"next-axiom": "^0.16.0",
|
"next-axiom": "^0.16.0",
|
||||||
"next-collect": "^0.2.1",
|
"next-collect": "^0.2.1",
|
||||||
"next-i18next": "^11.3.0",
|
"next-i18next": "^11.3.0",
|
||||||
@@ -139,6 +141,7 @@
|
|||||||
"@types/glidejs__glide": "^3.4.2",
|
"@types/glidejs__glide": "^3.4.2",
|
||||||
"@types/lodash": "^4.14.182",
|
"@types/lodash": "^4.14.182",
|
||||||
"@types/markdown-it": "^12.2.3",
|
"@types/markdown-it": "^12.2.3",
|
||||||
|
"@types/md5": "^2.3.2",
|
||||||
"@types/memory-cache": "^0.2.2",
|
"@types/memory-cache": "^0.2.2",
|
||||||
"@types/micro": "7.3.7",
|
"@types/micro": "7.3.7",
|
||||||
"@types/mime-types": "^2.1.1",
|
"@types/mime-types": "^2.1.1",
|
||||||
@@ -146,7 +149,7 @@
|
|||||||
"@types/node": "16.9.1",
|
"@types/node": "16.9.1",
|
||||||
"@types/nodemailer": "^6.4.5",
|
"@types/nodemailer": "^6.4.5",
|
||||||
"@types/qrcode": "^1.4.3",
|
"@types/qrcode": "^1.4.3",
|
||||||
"@types/react": "^18.0.17",
|
"@types/react": "18.0.26",
|
||||||
"@types/react-phone-number-input": "^3.0.14",
|
"@types/react-phone-number-input": "^3.0.14",
|
||||||
"@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",
|
||||||
@@ -157,7 +160,7 @@
|
|||||||
"copy-webpack-plugin": "^11.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"detect-port": "^1.3.0",
|
"detect-port": "^1.3.0",
|
||||||
"env-cmd": "^10.1.0",
|
"env-cmd": "^10.1.0",
|
||||||
"eslint": "^8.22.0",
|
"eslint": "^8.34.0",
|
||||||
"mockdate": "^3.0.5",
|
"mockdate": "^3.0.5",
|
||||||
"module-alias": "^2.2.2",
|
"module-alias": "^2.2.2",
|
||||||
"msw": "^0.42.3",
|
"msw": "^0.42.3",
|
||||||
@@ -166,5 +169,11 @@
|
|||||||
"ts-jest": "^28.0.8",
|
"ts-jest": "^28.0.8",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.9.4"
|
"typescript": "^4.9.4"
|
||||||
|
},
|
||||||
|
"nextBundleAnalysis": {
|
||||||
|
"budget": 358400,
|
||||||
|
"budgetPercentIncreaseRed": 20,
|
||||||
|
"minimumChangeThreshold": 500,
|
||||||
|
"showDetails": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GetStaticPropsContext } from "next";
|
import type { GetStaticPropsContext } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import MarkdownIt from "markdown-it";
|
import MarkdownIt from "markdown-it";
|
||||||
import { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
@@ -30,8 +30,8 @@ import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
|||||||
import { HeadSeo, AvatarGroup, Avatar } from "@calcom/ui";
|
import { HeadSeo, AvatarGroup, Avatar } from "@calcom/ui";
|
||||||
import { BadgeCheckIcon, FiArrowRight } from "@calcom/ui/components/icon";
|
import { BadgeCheckIcon, FiArrowRight } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
import { EmbedProps } from "@lib/withEmbedSsr";
|
import type { EmbedProps } from "@lib/withEmbedSsr";
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
|
|||||||
{eventTypes.map((type, index) => (
|
{eventTypes.map((type, index) => (
|
||||||
<li
|
<li
|
||||||
key={index}
|
key={index}
|
||||||
className="dark:bg-darkgray-100 group relative border-b border-gray-200 bg-white first:rounded-t-md last:rounded-b-md last:border-b-0 hover:bg-gray-50 dark:border-gray-700 dark:hover:border-gray-600">
|
className="dark:bg-darkgray-100 dark:border-darkgray-300 group relative border-b border-gray-200 bg-white first:rounded-t-md last:rounded-b-md last:border-b-0 hover:bg-gray-50">
|
||||||
<FiArrowRight className="absolute right-3 top-3 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
|
<FiArrowRight className="absolute right-3 top-3 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
|
||||||
<Link
|
<Link
|
||||||
href={getUsernameSlugLink({ users: props.users, slug: type.slug })}
|
href={getUsernameSlugLink({ users: props.users, slug: type.slug })}
|
||||||
@@ -156,7 +156,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
|
|||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"rounded-md ",
|
"rounded-md ",
|
||||||
!isEventListEmpty && "border border-gray-200 dark:border-gray-700 dark:hover:border-gray-600"
|
!isEventListEmpty && "dark:border-darkgray-300 border border-gray-200"
|
||||||
)}
|
)}
|
||||||
data-testid="event-types">
|
data-testid="event-types">
|
||||||
{user.away ? (
|
{user.away ? (
|
||||||
@@ -175,7 +175,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
|
|||||||
<div
|
<div
|
||||||
key={type.id}
|
key={type.id}
|
||||||
style={{ display: "flex", ...eventTypeListItemEmbedStyles }}
|
style={{ display: "flex", ...eventTypeListItemEmbedStyles }}
|
||||||
className="dark:bg-darkgray-100 group relative border-b border-gray-200 bg-white first:rounded-t-md last:rounded-b-md last:border-b-0 hover:bg-gray-50 dark:border-gray-700 dark:hover:border-gray-600">
|
className="dark:bg-darkgray-100 dark:hover:bg-darkgray-200 dark:border-darkgray-300 group relative border-b border-gray-200 bg-white first:rounded-t-md last:rounded-b-md last:border-b-0 hover:bg-gray-50">
|
||||||
<FiArrowRight className="absolute right-4 top-4 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
|
<FiArrowRight className="absolute right-4 top-4 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
|
||||||
{/* Don't prefetch till the time we drop the amount of javascript in [user][type] page which is impacting score for [user] page */}
|
{/* Don't prefetch till the time we drop the amount of javascript in [user][type] page which is impacting score for [user] page */}
|
||||||
<Link
|
<Link
|
||||||
@@ -291,6 +291,15 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||||||
}
|
}
|
||||||
const isDynamicGroup = users.length > 1;
|
const isDynamicGroup = users.length > 1;
|
||||||
|
|
||||||
|
if (isDynamicGroup) {
|
||||||
|
// sort and be in the same order as usernameList so first user is the first user in the list
|
||||||
|
users.sort((a, b) => {
|
||||||
|
const aIndex = (a.username && usernameList.indexOf(a.username)) || 0;
|
||||||
|
const bIndex = (b.username && usernameList.indexOf(b.username)) || 0;
|
||||||
|
return aIndex - bIndex;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const dynamicNames = isDynamicGroup
|
const dynamicNames = isDynamicGroup
|
||||||
? users.map((user) => {
|
? users.map((user) => {
|
||||||
return user.name || "";
|
return user.name || "";
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
import MarkdownIt from "markdown-it";
|
import type { GetStaticPaths, GetStaticPropsContext } from "next";
|
||||||
import { GetStaticPaths, GetStaticPropsContext } from "next";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { privacyFilteredLocations, LocationObject } from "@calcom/app-store/locations";
|
import type { LocationObject } from "@calcom/app-store/locations";
|
||||||
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
|
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defaultEvents";
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent";
|
import type { User } from "@calcom/prisma/client";
|
||||||
import prisma from "@calcom/prisma";
|
|
||||||
import { User } from "@calcom/prisma/client";
|
|
||||||
import { EventTypeMetaDataSchema, teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
|
||||||
|
|
||||||
import { isBrandingHidden } from "@lib/isBrandingHidden";
|
import { isBrandingHidden } from "@lib/isBrandingHidden";
|
||||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
import { EmbedProps } from "@lib/withEmbedSsr";
|
import type { EmbedProps } from "@lib/withEmbedSsr";
|
||||||
|
|
||||||
import AvailabilityPage from "@components/booking/pages/AvailabilityPage";
|
import AvailabilityPage from "@components/booking/pages/AvailabilityPage";
|
||||||
|
|
||||||
@@ -59,10 +54,19 @@ export default function Type(props: AvailabilityPageProps) {
|
|||||||
|
|
||||||
Type.isThemeSupported = true;
|
Type.isThemeSupported = true;
|
||||||
|
|
||||||
|
const paramsSchema = z.object({ type: z.string(), user: z.string() });
|
||||||
async function getUserPageProps(context: GetStaticPropsContext) {
|
async function getUserPageProps(context: GetStaticPropsContext) {
|
||||||
const { type: slug, user: username } = paramsSchema.parse(context.params);
|
// load server side dependencies
|
||||||
|
const MarkdownIt = await import("markdown-it").then((mod) => mod.default);
|
||||||
|
const prisma = await import("@calcom/prisma").then((mod) => mod.default);
|
||||||
|
const { privacyFilteredLocations } = await import("@calcom/app-store/locations");
|
||||||
|
const { parseRecurringEvent } = await import("@calcom/lib/isRecurringEvent");
|
||||||
|
const { EventTypeMetaDataSchema, teamMetadataSchema } = await import("@calcom/prisma/zod-utils");
|
||||||
const { ssgInit } = await import("@server/lib/ssg");
|
const { ssgInit } = await import("@server/lib/ssg");
|
||||||
|
|
||||||
|
const { type: slug, user: username } = paramsSchema.parse(context.params);
|
||||||
const ssg = await ssgInit(context);
|
const ssg = await ssgInit(context);
|
||||||
|
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
username,
|
username,
|
||||||
@@ -127,7 +131,7 @@ async function getUserPageProps(context: GetStaticPropsContext) {
|
|||||||
|
|
||||||
if (!user || !user.eventTypes.length) return { notFound: true };
|
if (!user || !user.eventTypes.length) return { notFound: true };
|
||||||
|
|
||||||
const [eventType]: (typeof user.eventTypes[number] & {
|
const [eventType]: ((typeof user.eventTypes)[number] & {
|
||||||
users: Pick<User, "name" | "username" | "hideBranding" | "timeZone">[];
|
users: Pick<User, "name" | "username" | "hideBranding" | "timeZone">[];
|
||||||
})[] = [
|
})[] = [
|
||||||
{
|
{
|
||||||
@@ -185,7 +189,17 @@ async function getUserPageProps(context: GetStaticPropsContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
|
async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
|
||||||
|
// load server side dependencies
|
||||||
|
const { getDefaultEvent, getGroupName, getUsernameList } = await import("@calcom/lib/defaultEvents");
|
||||||
|
const { privacyFilteredLocations } = await import("@calcom/app-store/locations");
|
||||||
|
const { parseRecurringEvent } = await import("@calcom/lib/isRecurringEvent");
|
||||||
|
const prisma = await import("@calcom/prisma").then((mod) => mod.default);
|
||||||
|
const { EventTypeMetaDataSchema, userMetadata: userMetadataSchema } = await import(
|
||||||
|
"@calcom/prisma/zod-utils"
|
||||||
|
);
|
||||||
const { ssgInit } = await import("@server/lib/ssg");
|
const { ssgInit } = await import("@server/lib/ssg");
|
||||||
|
const { getAppFromSlug } = await import("@calcom/app-store/utils");
|
||||||
|
|
||||||
const ssg = await ssgInit(context);
|
const ssg = await ssgInit(context);
|
||||||
const { type: typeParam, user: userParam } = paramsSchema.parse(context.params);
|
const { type: typeParam, user: userParam } = paramsSchema.parse(context.params);
|
||||||
const usernameList = getUsernameList(userParam);
|
const usernameList = getUsernameList(userParam);
|
||||||
@@ -215,6 +229,7 @@ async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
|
|||||||
darkBrandColor: true,
|
darkBrandColor: true,
|
||||||
defaultScheduleId: true,
|
defaultScheduleId: true,
|
||||||
allowDynamicBooking: true,
|
allowDynamicBooking: true,
|
||||||
|
metadata: true,
|
||||||
away: true,
|
away: true,
|
||||||
schedules: {
|
schedules: {
|
||||||
select: {
|
select: {
|
||||||
@@ -233,7 +248,32 @@ async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const locations = eventType.locations ? (eventType.locations as LocationObject[]) : [];
|
// sort and be in the same order as usernameList so first user is the first user in the list
|
||||||
|
let sortedUsers: typeof users = [];
|
||||||
|
if (users.length > 1) {
|
||||||
|
sortedUsers = users.sort((a, b) => {
|
||||||
|
const aIndex = (a.username && usernameList.indexOf(a.username)) || 0;
|
||||||
|
const bIndex = (b.username && usernameList.indexOf(b.username)) || 0;
|
||||||
|
return aIndex - bIndex;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let locations = eventType.locations ? (eventType.locations as LocationObject[]) : [];
|
||||||
|
|
||||||
|
// Get the prefered location type from the first user
|
||||||
|
const firstUsersMetadata = userMetadataSchema.parse(sortedUsers[0].metadata || {});
|
||||||
|
const preferedLocationType = firstUsersMetadata?.defaultConferencingApp;
|
||||||
|
|
||||||
|
if (preferedLocationType?.appSlug) {
|
||||||
|
const foundApp = getAppFromSlug(preferedLocationType.appSlug);
|
||||||
|
const appType = foundApp?.appData?.location?.type;
|
||||||
|
if (appType) {
|
||||||
|
// Replace the location with the prefered location type
|
||||||
|
// This will still be default to daily if the app is not found
|
||||||
|
locations = [{ type: appType, link: preferedLocationType.appLink }] as LocationObject[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const eventTypeObject = Object.assign({}, eventType, {
|
const eventTypeObject = Object.assign({}, eventType, {
|
||||||
metadata: EventTypeMetaDataSchema.parse(eventType.metadata || {}),
|
metadata: EventTypeMetaDataSchema.parse(eventType.metadata || {}),
|
||||||
recurringEvent: parseRecurringEvent(eventType.recurringEvent),
|
recurringEvent: parseRecurringEvent(eventType.recurringEvent),
|
||||||
@@ -278,8 +318,6 @@ async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const paramsSchema = z.object({ type: z.string(), user: z.string() });
|
|
||||||
|
|
||||||
export const getStaticProps = async (context: GetStaticPropsContext) => {
|
export const getStaticProps = async (context: GetStaticPropsContext) => {
|
||||||
const { user: userParam } = paramsSchema.parse(context.params);
|
const { user: userParam } = paramsSchema.parse(context.params);
|
||||||
// dynamic groups are not generated at build time, but otherwise are probably cached until infinity.
|
// dynamic groups are not generated at build time, but otherwise are probably cached until infinity.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GetStaticPropsContext } from "next";
|
import type { GetStaticPropsContext } from "next";
|
||||||
|
|
||||||
import { getStaticProps as _getStaticProps } from "../[type]";
|
import { getStaticProps as _getStaticProps } from "../[type]";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
|
||||||
import { LocationObject, privacyFilteredLocations } from "@calcom/app-store/locations";
|
import type { LocationObject } from "@calcom/app-store/locations";
|
||||||
|
import { privacyFilteredLocations } from "@calcom/app-store/locations";
|
||||||
|
import { getAppFromSlug } from "@calcom/app-store/utils";
|
||||||
import { parseRecurringEvent } from "@calcom/lib";
|
import { parseRecurringEvent } from "@calcom/lib";
|
||||||
import {
|
import {
|
||||||
getDefaultEvent,
|
getDefaultEvent,
|
||||||
@@ -11,11 +13,16 @@ import {
|
|||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { bookEventTypeSelect } from "@calcom/prisma";
|
import { bookEventTypeSelect } from "@calcom/prisma";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
import {
|
||||||
|
customInputSchema,
|
||||||
|
EventTypeMetaDataSchema,
|
||||||
|
userMetadata as userMetadataSchema,
|
||||||
|
} from "@calcom/prisma/zod-utils";
|
||||||
|
|
||||||
import { asStringOrNull, asStringOrThrow } from "@lib/asStringOrNull";
|
import { asStringOrNull, asStringOrThrow } from "@lib/asStringOrNull";
|
||||||
import getBooking, { GetBookingType } from "@lib/getBooking";
|
import type { GetBookingType } from "@lib/getBooking";
|
||||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
import getBooking from "@lib/getBooking";
|
||||||
|
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
|
|
||||||
import BookingPage from "@components/booking/pages/BookingPage";
|
import BookingPage from "@components/booking/pages/BookingPage";
|
||||||
|
|
||||||
@@ -85,6 +92,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||||||
darkBrandColor: true,
|
darkBrandColor: true,
|
||||||
allowDynamicBooking: true,
|
allowDynamicBooking: true,
|
||||||
away: true,
|
away: true,
|
||||||
|
metadata: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -117,8 +125,38 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||||||
recurringEvent: parseRecurringEvent(eventTypeRaw.recurringEvent),
|
recurringEvent: parseRecurringEvent(eventTypeRaw.recurringEvent),
|
||||||
};
|
};
|
||||||
|
|
||||||
const eventTypeObject = [eventType].map((e) => {
|
const getLocations = () => {
|
||||||
let locations = eventTypeRaw.locations || [];
|
let locations = eventTypeRaw.locations || [];
|
||||||
|
if (!isDynamicGroupBooking) return locations;
|
||||||
|
|
||||||
|
let sortedUsers: typeof users = [];
|
||||||
|
// sort and be in the same order as usernameList so first user is the first user in the list
|
||||||
|
if (users.length > 1) {
|
||||||
|
sortedUsers = users.sort((a, b) => {
|
||||||
|
const aIndex = (a.username && usernameList.indexOf(a.username)) || 0;
|
||||||
|
const bIndex = (b.username && usernameList.indexOf(b.username)) || 0;
|
||||||
|
return aIndex - bIndex;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the prefered location type from the first user
|
||||||
|
const firstUsersMetadata = userMetadataSchema.parse(sortedUsers[0].metadata || {});
|
||||||
|
const preferedLocationType = firstUsersMetadata?.defaultConferencingApp;
|
||||||
|
|
||||||
|
if (preferedLocationType?.appSlug) {
|
||||||
|
const foundApp = getAppFromSlug(preferedLocationType.appSlug);
|
||||||
|
const appType = foundApp?.appData?.location?.type;
|
||||||
|
if (appType) {
|
||||||
|
// Replace the location with the prefered location type
|
||||||
|
// This will still be default to daily if the app is not found
|
||||||
|
locations = [{ type: appType, link: preferedLocationType.appLink }] as LocationObject[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return locations;
|
||||||
|
};
|
||||||
|
|
||||||
|
const eventTypeObject = [eventType].map((e) => {
|
||||||
|
let locations = getLocations();
|
||||||
locations = privacyFilteredLocations(locations as LocationObject[]);
|
locations = privacyFilteredLocations(locations as LocationObject[]);
|
||||||
return {
|
return {
|
||||||
...e,
|
...e,
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* This page is empty for the user, it is used only to take advantage of the
|
||||||
|
* caching system that NextJS uses SSG pages.
|
||||||
|
* TODO: Redirect to user profile on browser
|
||||||
|
*/
|
||||||
|
import type { GetStaticPaths, GetStaticProps } from "next";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { getCachedResults } from "@calcom/core";
|
||||||
|
import dayjs from "@calcom/dayjs";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
|
const CalendarCache = () => <div />;
|
||||||
|
|
||||||
|
const paramsSchema = z.object({ user: z.string(), month: z.string() });
|
||||||
|
export const getStaticProps: GetStaticProps<
|
||||||
|
{ results: Awaited<ReturnType<typeof getCachedResults>> },
|
||||||
|
{ user: string }
|
||||||
|
> = async (context) => {
|
||||||
|
const { user: username, month } = paramsSchema.parse(context.params);
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
username,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
credentials: true,
|
||||||
|
selectedCalendars: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const startDate = (
|
||||||
|
dayjs(month, "YYYY-MM").isSame(dayjs(), "month") ? dayjs() : dayjs(month, "YYYY-MM")
|
||||||
|
).startOf("day");
|
||||||
|
const endDate = startDate.endOf("month");
|
||||||
|
try {
|
||||||
|
const results = user?.credentials
|
||||||
|
? await getCachedResults(
|
||||||
|
user?.credentials,
|
||||||
|
startDate.format(),
|
||||||
|
endDate.format(),
|
||||||
|
user?.selectedCalendars
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
return {
|
||||||
|
props: { results, date: new Date().toISOString() },
|
||||||
|
revalidate: 1,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
let message = "Unknown error while fetching calendarƒ";
|
||||||
|
if (error instanceof Error) message = error.message;
|
||||||
|
console.error(error, message);
|
||||||
|
return {
|
||||||
|
props: { results: [], date: new Date().toISOString(), message },
|
||||||
|
revalidate: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStaticPaths: GetStaticPaths = () => {
|
||||||
|
return {
|
||||||
|
paths: [],
|
||||||
|
fallback: "blocking",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CalendarCache;
|
||||||
+20
-86
@@ -1,26 +1,29 @@
|
|||||||
|
import { Inter } from "@next/font/google";
|
||||||
|
import localFont from "@next/font/local";
|
||||||
import { DefaultSeo } from "next-seo";
|
import { DefaultSeo } from "next-seo";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Script from "next/script";
|
import Script from "next/script";
|
||||||
import superjson from "superjson";
|
|
||||||
|
|
||||||
import "@calcom/embed-core/src/embed-iframe";
|
import "@calcom/embed-core/src/embed-iframe";
|
||||||
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
|
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
|
||||||
import { httpBatchLink } from "@calcom/trpc/client/links/httpBatchLink";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { httpLink } from "@calcom/trpc/client/links/httpLink";
|
|
||||||
import { loggerLink } from "@calcom/trpc/client/links/loggerLink";
|
|
||||||
import { splitLink } from "@calcom/trpc/client/links/splitLink";
|
|
||||||
import { withTRPC } from "@calcom/trpc/next";
|
|
||||||
import type { TRPCClientErrorLike } from "@calcom/trpc/react";
|
|
||||||
import { Maybe } from "@calcom/trpc/server";
|
|
||||||
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
|
||||||
|
|
||||||
import AppProviders, { AppProps } from "@lib/app-providers";
|
import type { AppProps } from "@lib/app-providers";
|
||||||
|
import AppProviders from "@lib/app-providers";
|
||||||
import { seoConfig } from "@lib/config/next-seo.config";
|
import { seoConfig } from "@lib/config/next-seo.config";
|
||||||
|
|
||||||
import I18nLanguageHandler from "@components/I18nLanguageHandler";
|
import I18nLanguageHandler from "@components/I18nLanguageHandler";
|
||||||
|
|
||||||
import "../styles/globals.css";
|
import "../styles/globals.css";
|
||||||
|
|
||||||
|
const interFont = Inter({ subsets: ["latin"], variable: "--font-inter", preload: true, display: "swap" });
|
||||||
|
const calFont = localFont({
|
||||||
|
src: "../fonts/CalSans-SemiBold.woff2",
|
||||||
|
variable: "--font-cal",
|
||||||
|
preload: true,
|
||||||
|
display: "swap",
|
||||||
|
});
|
||||||
|
|
||||||
function MyApp(props: AppProps) {
|
function MyApp(props: AppProps) {
|
||||||
const { Component, pageProps, err, router } = props;
|
const { Component, pageProps, err, router } = props;
|
||||||
let pageStatus = "200";
|
let pageStatus = "200";
|
||||||
@@ -53,6 +56,12 @@ function MyApp(props: AppProps) {
|
|||||||
id="page-status"
|
id="page-status"
|
||||||
dangerouslySetInnerHTML={{ __html: `window.CalComPageStatus = '${pageStatus}'` }}
|
dangerouslySetInnerHTML={{ __html: `window.CalComPageStatus = '${pageStatus}'` }}
|
||||||
/>
|
/>
|
||||||
|
<style jsx global>{`
|
||||||
|
:root {
|
||||||
|
--font-inter: ${interFont.style.fontFamily};
|
||||||
|
--font-cal: ${calFont.style.fontFamily};
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
<Head>
|
<Head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||||
</Head>
|
</Head>
|
||||||
@@ -70,79 +79,4 @@ function MyApp(props: AppProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTRPC<AppRouter>({
|
export default trpc.withTRPC(MyApp);
|
||||||
config() {
|
|
||||||
const url =
|
|
||||||
typeof window !== "undefined"
|
|
||||||
? "/api/trpc"
|
|
||||||
: process.env.VERCEL_URL
|
|
||||||
? `https://${process.env.VERCEL_URL}/api/trpc`
|
|
||||||
: `http://${process.env.NEXT_PUBLIC_WEBAPP_URL}/api/trpc`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If you want to use SSR, you need to use the server's full URL
|
|
||||||
* @link https://trpc.io/docs/ssr
|
|
||||||
*/
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* @link https://trpc.io/docs/links
|
|
||||||
*/
|
|
||||||
links: [
|
|
||||||
// adds pretty logs to your console in development and logs errors in production
|
|
||||||
loggerLink({
|
|
||||||
enabled: (opts) =>
|
|
||||||
!!process.env.NEXT_PUBLIC_DEBUG || (opts.direction === "down" && opts.result instanceof Error),
|
|
||||||
}),
|
|
||||||
splitLink({
|
|
||||||
// check for context property `skipBatch`
|
|
||||||
condition: (op) => {
|
|
||||||
return op.context.skipBatch === true;
|
|
||||||
},
|
|
||||||
// when condition is true, use normal request
|
|
||||||
true: httpLink({ url }),
|
|
||||||
// when condition is false, use batching
|
|
||||||
false: httpBatchLink({
|
|
||||||
url,
|
|
||||||
/** @link https://github.com/trpc/trpc/issues/2008 */
|
|
||||||
// maxBatchSize: 7
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
/**
|
|
||||||
* @link https://react-query.tanstack.com/reference/QueryClient
|
|
||||||
*/
|
|
||||||
queryClientConfig: {
|
|
||||||
defaultOptions: {
|
|
||||||
queries: {
|
|
||||||
/**
|
|
||||||
* 1s should be enough to just keep identical query waterfalls low
|
|
||||||
* @example if one page components uses a query that is also used further down the tree
|
|
||||||
*/
|
|
||||||
staleTime: 1000,
|
|
||||||
/**
|
|
||||||
* Retry `useQuery()` calls depending on this function
|
|
||||||
*/
|
|
||||||
retry(failureCount, _err) {
|
|
||||||
const err = _err as never as Maybe<TRPCClientErrorLike<AppRouter>>;
|
|
||||||
const code = err?.data?.code;
|
|
||||||
if (code === "BAD_REQUEST" || code === "FORBIDDEN" || code === "UNAUTHORIZED") {
|
|
||||||
// if input data is wrong or you're not authorized there's no point retrying a query
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const MAX_QUERY_RETRIES = 3;
|
|
||||||
return failureCount < MAX_QUERY_RETRIES;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @link https://trpc.io/docs/data-transformers
|
|
||||||
*/
|
|
||||||
transformer: superjson,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @link https://trpc.io/docs/ssr
|
|
||||||
*/
|
|
||||||
ssr: false,
|
|
||||||
})(MyApp);
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import Document, { DocumentContext, Head, Html, Main, NextScript, DocumentProps } from "next/document";
|
import type { DocumentContext, DocumentProps } from "next/document";
|
||||||
|
import Document, { Head, Html, Main, NextScript } from "next/document";
|
||||||
import Script from "next/script";
|
import Script from "next/script";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@@ -38,14 +39,6 @@ class MyDocument extends Document<Props> {
|
|||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#000000" />
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#000000" />
|
||||||
<meta name="msapplication-TileColor" content="#ff0000" />
|
<meta name="msapplication-TileColor" content="#ff0000" />
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff" />
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
href="/fonts/Inter-roman.var.woff2"
|
|
||||||
as="font"
|
|
||||||
type="font/woff2"
|
|
||||||
crossOrigin="anonymous"
|
|
||||||
/>
|
|
||||||
<link rel="preload" href="/fonts/cal.ttf" as="font" type="font/ttf" crossOrigin="anonymous" />
|
|
||||||
<Script src="/embed-init-iframe.js" strategy="beforeInteractive" />
|
<Script src="/embed-init-iframe.js" strategy="beforeInteractive" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
* @link https://nextjs.org/docs/advanced-features/custom-error-page
|
* @link https://nextjs.org/docs/advanced-features/custom-error-page
|
||||||
*/
|
*/
|
||||||
import * as Sentry from "@sentry/nextjs";
|
import * as Sentry from "@sentry/nextjs";
|
||||||
import { NextPage, NextPageContext } from "next";
|
import type { NextPage, NextPageContext } from "next";
|
||||||
import NextError, { ErrorProps } from "next/error";
|
import type { ErrorProps } from "next/error";
|
||||||
|
import NextError from "next/error";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||||
|
|||||||
Loaded 100 of 521 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user