Compare commits

..
85 Commits
Author SHA1 Message Date
Peer Richelsen 0a0c400333 Update package.json
release.yml / release (push) Canceled after 0s
2023-06-23 10:58:34 +02:00
Anit Jha 0eb05aa540 fix: Public Link of pending members is now disabled (#9730) 2023-06-23 10:53:00 +02:00
Crowdin Bot efe18ebdcd New Crowdin translations by Github Action 2023-06-23 08:51:18 +00:00
Crowdin Bot ce097b89ba New Crowdin translations by Github Action 2023-06-23 08:50:29 +00:00
Deepak Rudra PaulandDeepak Paul bb0e6389fa fix: Fixed delete event-type button (#9739)
Co-authored-by: Deepak Paul <rudra.ad7@gmail.com>
2023-06-23 10:49:53 +02:00
Nafees Nazik 19a7cd1a50 fix: invite modal (#9713)
* fix: invite modal

* fix: truncate text
2023-06-23 10:48:20 +02:00
Hariom Balhara 49bcd5fec7 Event Analytics not working for new booker (#9740) 2023-06-23 08:42:02 +00:00
Udit Takkar 000a6e0d35 fix: location details on new booker (#9710)
* fix: location details on new booker

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* chore

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-06-23 11:53:00 +05:30
Alex van Andel 52936f60b4 fix: Various errors & 500 error on duplicate url when editing (#9731) 2023-06-23 07:03:45 +02:00
Omar López 2d6eebcd8a chore: Sync packages mismatches (#9725) 2023-06-22 15:25:37 -07:00
Keith Williamsandalannnc cfa6a5b996 chore: Added unit tests for converting availability to string (#9617)
* Added unit tests for availability to string

* Testing to see if extra toString() removes unicode

* Replacing unicode space

* Updated to regex

* Reverted files that were not supposed to change

* Reverted schema changes

* Added comment

* Type check

* Change function call structure

---------

Co-authored-by: alannnc <alannnc@gmail.com>
2023-06-22 22:19:50 +00:00
sean-brydon 7413ba6aa0 Fixes overflow + zindex of cal icon (#9707) 2023-06-22 19:46:42 +00:00
Deepak Rudra PaulandDeepak Paul e9cbe5d353 fix:copy-invite-link button (#9721)
Co-authored-by: Deepak Paul <rudra.ad7@gmail.com>
2023-06-22 21:35:09 +02:00
Alex van Andel d8bc9acbbc feat: remove member from hosts on removal (#9722)
* feat: When a member is removed we should remove it from the hosts

* Add migration for fixing old records affected
2023-06-22 21:33:03 +02:00
Hariom Balhara 4f0e843ac6 test: Make embed tests stable. (#9718)
* Wait for /embed/embed.js to be servable

* Change something in embeds/ to make embed tests run
2023-06-22 17:38:51 +02:00
sean-brydon 02f1c50bb2 Fix settings layout + org headings (#9712) 2023-06-22 11:57:00 +01:00
GitStart-Cal.comandgitstart-calcom 5952a38ab9 feat: [CAL-910] Pre-select country for phone number (CALCOM-5759) (#9526)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-06-22 10:18:05 +02:00
Efraín Rochín e5fb19163e perf: Remove useless data on EvenTypes getByViewer endpoint (#9704) 2023-06-22 10:16:53 +02:00
79f34a4fc7 perf: [CAL-1928] Deduplicate eventTypes.getByViewer query (#9700)
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-06-22 10:16:34 +02:00
Crowdin Bot 3e90c08d55 New Crowdin translations by Github Action 2023-06-22 07:34:42 +00:00
Hariom Balhara 697d8eca43 Allow closing the embed on clicking the black transparent area (#9706) 2023-06-22 07:33:50 +00:00
Omar López 5cecf3104f refactor: Skip license checks for development (#9375) 2023-06-21 23:13:26 -03:00
Crowdin Bot a3a6caa6dd New Crowdin translations by Github Action 2023-06-21 23:30:55 +00:00
Omar López d3586c9039 Revert "fix: redirect to 404 page for invalid email verification token" (#9703) 2023-06-21 23:29:53 +00:00
71eafa287a feat: Organizations no middleware rewrite (#9548)
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-21 21:12:12 +00:00
Keith Williams 828ef8580e v3.0.5
release.yml / release (push) Canceled after 0s
2023-06-21 18:39:37 +02:00
Jeroen Reumkens a348c3a20a CAL-1892: Show booker layout dark mode preview based on theme the user selects (#9664) 2023-06-21 17:14:23 +02:00
Omar López 3c68c7f7cb chore: Upgrades prisma to 4.16 (#9670)
* Upgrades prisma to 4.16

* typefix
2023-06-21 17:07:54 +02:00
Hariom Balhara 820b2da16d test: Add RegExp tests for Next.config.js rewrites (#9681)
* Add RegExp tests

* Update apps/web/test/lib/next-config.test.ts

* Dont avoid rewrite for /booked event slug

* Add /embed/embed.js test

* Add dynamic group booking link test
2023-06-21 17:06:44 +02:00
sean-brydon 6bdb2b94eb feat: Adds sheet component (#9680)
* Adds sheet component

* Fix overlay

* Calc math
2023-06-21 15:29:23 +01:00
Hariom Balhara 3c3db0b3a6 fix: Hotfix/ Dynamic Group Booking Links not working in new booker (#9685)
* Debuglogs

* Derive group usernames correctly

* Revert "Debuglogs"

This reverts commit d93f031589b34bb7fee32a34f96eb89805c20275.
2023-06-21 15:22:13 +01:00
Hariom Balhara 4748641c22 fix: Hotfix/main - 404 workflows (#9678) 2023-06-21 15:39:03 +05:30
Jeroen Reumkens 804787f2a3 Get brand colors from DB for owner, so it's reflected in booker (#9679) 2023-06-21 09:04:15 +00:00
Alex van Andel b7c3b31e0c fix: eventType-getByViewer subselect on children->users[] (#9668) 2023-06-21 08:56:42 +00:00
Jeroen ReumkensandHariom Balhara ab6781cd72 fix: Booker fix for team + user with same event + event slug (#9654)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-06-21 13:01:55 +05:30
Crowdin Bot b070cfb227 New Crowdin translations by Github Action 2023-06-21 07:29:17 +00:00
Hariom Balhara 5acb3a09e5 fix: Hide EventTypeDetails when configured (#9633) 2023-06-21 12:58:21 +05:30
Crowdin Bot 69c9f690cb New Crowdin translations by Github Action 2023-06-20 17:55:04 +00:00
Ayush Mainali d2a12ea834 refactor: Convert card to cva (#9651) 2023-06-20 19:54:08 +02:00
16f24b4831 feat: [CAL-1435] Emptyscreen for App Store if no external app has been configured (CALCOM-8154) (#9516)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-20 19:53:33 +02:00
sean-brydon a3eeeab259 feat: Handle deleting vercel domain when org is deleted (#9661) 2023-06-20 17:57:21 +02:00
Alex van Andel f69c254fbc v3.0.4
release.yml / release (push) Canceled after 0s
2023-06-20 17:37:53 +02:00
Hariom Balhara f0f6bd4846 fix: Team links and username starting with team rewrites (#9665)
* Fix

* Fix private link /book page as well
2023-06-20 16:48:15 +02:00
Udit TakkarandPeer Richelsen 8816a18854 feat: team filters (#9543)
* feat: team filers

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: type error

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: remove filter from mobile

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: show heading when one item selected

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-20 16:39:18 +02:00
sean-brydon 4e529e5c6c test: Adds permission tests to impersonation (#9660)
* Adds permission tests to impersonation

* NIT + new test for none numbers
2023-06-20 13:42:37 +00:00
Alex van Andel e521bf6f0b v3.0.3
release.yml / release (push) Canceled after 0s
2023-06-20 12:26:14 +02:00
Jeroen Reumkensandalannnc a91a0f86e0 CAL-1904: Fixed bg color for all booked days as per design (#9630)
Co-authored-by: alannnc <alannnc@gmail.com>
2023-06-20 12:20:40 +02:00
GitStart-Cal.comandgitstart-calcom f654003f95 fix: settings: skeleton issues (CALCOM-9627) (#9641)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-06-20 12:20:13 +02:00
Hariom Balhara 4cc8a6fbf4 fix: embed booking 404 (#9652) 2023-06-20 15:42:39 +05:30
Peer Richelsenandalannnc 5167608404 chore: added commercial API to LICENSE (#9635)
Co-authored-by: alannnc <alannnc@gmail.com>
2023-06-20 09:33:43 +00:00
Crowdin Bot 9c93685431 New Crowdin translations by Github Action 2023-06-20 08:40:54 +00:00
Hariom Balhara a380087c9b reschedule_reason is used by Booking Form (#9656) 2023-06-20 10:39:46 +02:00
alannnc 3559e0d082 Improved with some try catch and removed old behaviour (#9650) 2023-06-20 00:10:47 +00:00
4323b0c2d1 chore: Upgrades Next.js to 13.4.6 (#9578)
* Upgrades Next.js to 13.4.6

* API handlers no longer receive a payload with req.method=DELETE

* Use DELETE with query params in Calendar Switch component

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
2023-06-19 14:22:30 -07:00
Crowdin Bot 3a800f7511 New Crowdin translations by Github Action 2023-06-19 21:16:33 +00:00
Ujwal Kumarandalannnc c203e5a38b Fix margins in booking page (#9639)
Co-authored-by: alannnc <alannnc@gmail.com>
2023-06-19 21:15:44 +00:00
Crowdin Bot 3f5b3f06a2 New Crowdin translations by Github Action 2023-06-19 20:33:22 +00:00
Leo Giovanetti d52635efbb Org labels and typos (#9646) 2023-06-19 13:32:21 -07:00
Omar López 4add4df08e fix: regression of username case sensitivity (#9642) 2023-06-19 17:34:31 +00:00
Crowdin Bot df6d7e8df7 New Crowdin translations by Github Action 2023-06-19 17:16:51 +00:00
Samuel StroscheinandPeer Richelsen dc61640118 chore: add inlang ide extension for i18n (#9599)
* add: inlang for i18n

* chore: format file

* chore: re-add comments

VSCode removed them for unknown reasons.

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-19 10:15:45 -07:00
Alex van Andel 361108246e Created secondary function to read the owner from users array (#9638) 2023-06-19 08:53:31 -07:00
Abdurrahman RajabandKeith Williams a5e25b9b28 docs: improving developer experience (#9432)
* feat: add feedback to semantic commits

* feat: welcome new contributors

* feat: add triggers welcome message

* chore: fix lint issue

* chore: typos

* Fixed spacing

* Temporarily making this runnable via Actions page

* Removed workflow_call

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-06-19 16:45:41 +02:00
Leo Giovanetti ccff528765 fix: Creating teams for organizations in wizard (#9632)
* Expanding org slug check and adding proper error msg

* Duh &&
2023-06-19 14:37:23 +00:00
Peer Richelsen 24a1ef6721 Create LICENSE 2023-06-19 16:18:02 +02:00
Ninad Trivediandsean-brydon b89919129e Copy link button repositioned (#9613)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-06-19 16:15:48 +02:00
Keith Williams cbf3edefc7 chore: Added unit tests for CalendarManager->getCalendarCredentials (#9618)
* Added unit tests for CalendarManager->getCalendarCredentials

* Fixed spacing

* Made test data more accurate
2023-06-19 16:10:06 +02:00
Crowdin Bot 909ae5f2fb New Crowdin translations by Github Action 2023-06-19 14:01:31 +00:00
Syed Ali Shahbaz 0bbe5173dc docs: Update API documentation for Bookings endpoint (_post & _patch) (#9511)
* Fix swagger in _post bookings

* Update swagger doc for _patch

* Resolves two NITs

* Resolves metadata mistake
2023-06-19 14:00:36 +00:00
Jeroen Reumkens 84ad133077 Removed full height which incorrectly limited height in booker sidebar. Tested in Chrome, FF and Safari. (#9628) 2023-06-19 13:14:51 +00:00
Udit Takkar 43647a797f fix: remove enable_new_call_ui prop (#9625)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-06-19 15:03:40 +02:00
Ritik KumarandUdit Takkar f469a9c127 fix: redirect to 404 page for invalid email verification token (#9499)
* fix:redirect to 404 page for invalid email token

* update 404 page to include invalid token error

* fix: use i18n

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-06-19 13:24:10 +01:00
Carina WollendorferandCarinaWolli 212f7a374f fix name dropdown (#9621)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-06-19 11:44:47 +00:00
Alex van Andel 1929d5c7d4 v3.0.2
release.yml / release (push) Canceled after 0s
2023-06-19 13:33:13 +02:00
Papageorgiou Nikos 8ae54cd7cc fix: Storybook build and fix cal brand colors (#9588)
Storybook failed with satisfies operator
Fix cal brand colors in storybook
2023-06-19 13:28:49 +02:00
Hariom Balhara bb13009e64 + is %2B as well (#9624) 2023-06-19 11:22:13 +00:00
Jeroen Reumkens 648dd862f5 Tweaks to booker grid sizing to prevent buttons from overflow booker wrapper in Safari (#9620) 2023-06-19 11:34:49 +01:00
sean-brydonandzomars 8eccd3658e feat: Upstash implementation for rate limiting/redis (#9514)
* Introduce rate limiting that works on the edge

* typo

* Log once on init

* Update rateLimit.ts

---------

Co-authored-by: zomars <zomars@me.com>
2023-06-19 10:01:06 +00:00
Ujwal KumarandJeroen Reumkens ed65b2a3ab fix: Hide layout switcher when only 1 layout is enabled (#9610)
* Hide layout switcher when only 1 layouts is enabled

* Ran prettier

---------

Co-authored-by: Jeroen Reumkens <hello@jeroenreumkens.nl>
2023-06-19 08:55:25 +00:00
Manav Guptaandsean-brydon 0930121680 fix: event type filter in Mobile view (#9619)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-06-18 21:11:41 +00:00
Crowdin Bot 1068873f27 New Crowdin translations by Github Action 2023-06-18 20:55:42 +00:00
Udit TakkarandPeer Richelsen a1ece0be63 fix: sidebar ui (#9581)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-18 20:54:57 +00:00
Crowdin Bot c550b4c8fc New Crowdin translations by Github Action 2023-06-18 19:22:51 +00:00
VIvek ChavanandKeith Williams e95650637c fix: changes "cancelled" to canceled (#9505)
* fixed #9502 changes cancelled to cenceled

* fixed cancelled to cancelled from all places in common.json

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-06-18 19:21:52 +00:00
0f3e0a1554 fix: Alert component fixed (#9605)
* fix: Alert Component Fixed

* Update packages/ui/components/alert/Alert.tsx

---------

Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-17 20:57:15 +00:00
288 changed files with 2954 additions and 2275 deletions
+2
View File
@@ -23,6 +23,8 @@ CALCOM_LICENSE_KEY=
# - DATABASE ************************************************************************************************
DATABASE_URL="postgresql://postgres:@localhost:5450/calendso"
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
# Uncomment to enable a dedicated connection pool for Prisma using Prisma Data Proxy
# Cold boots will be faster and you'll be able to scale your DB independently of your app.
+2
View File
@@ -26,11 +26,13 @@ Fixes # (issue)
- [ ] Test B
## Mandatory Tasks
- [ ] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.
## Checklist
<!-- Please remove all the irrelevant bullets to your PR -->
- I haven't read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
- My code doesn't follow the style guidelines of this project
- I haven't commented my code, particularly in hard-to-understand areas
+3
View File
@@ -34,12 +34,15 @@ jobs:
app-store:
- 'apps/web/**'
- 'packages/app-store/**'
- 'playwright.config.ts'
embed:
- 'apps/web/**'
- 'packages/embeds/**'
- 'playwright.config.ts'
embed-react:
- 'apps/web/**'
- 'packages/embeds/**'
- 'playwright.config.ts'
env:
name: Create env file
+28 -2
View File
@@ -9,7 +9,7 @@ on:
- synchronize
permissions:
pull-requests: read
pull-requests: write
jobs:
validate-pr:
@@ -17,5 +17,31 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
id: lint_pr_title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: marocchino/sticky-pull-request-comment@v2
# When the previous steps fails, the workflow would stop. By adding this
# condition you can continue the execution with the populated error message.
if: always() && (steps.lint_pr_title.outputs.error_message != null)
with:
header: pr-title-lint-error
message: |
Hey there and thank you for opening this pull request! 👋🏼
We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted.
Details:
```
${{ steps.lint_pr_title.outputs.error_message }}
```
# Delete a previous comment when the issue has been resolved
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
uses: marocchino/sticky-pull-request-comment@v2
with:
header: pr-title-lint-error
message: |
Thank you for following the naming conventions! 🙏
@@ -0,0 +1,28 @@
name: "Welcome new contributors"
on:
issues:
types: opened
pull_request:
types: opened
permissions:
pull-requests: write
issues: write
jobs:
welcome-message:
name: Welcoming New Users
runs-on: ubuntu-latest
timeout-minutes: 10
needs: semantics
if: github.event.action == 'opened'
steps:
- uses: actions/first-interaction@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-message: |-
Thank you for making your first Pull Request and taking the time to improve Cal.com ! ❤️🎉
Feel free to join the conversation at [slack](https://calendso.slack.com/)
issue-message: |
Thank you for opening your first issue, one of our team members will review it as soon as it possible. ❤️🎉
+2 -1
View File
@@ -8,6 +8,7 @@
"ban.spellright", // Spell check for docs
"stripe.vscode-stripe", // stripe VSCode extension
"Prisma.prisma", // syntax|format|completion for prisma
"rebornix.project-snippets" // Share useful snippets between collaborators
"rebornix.project-snippets", // Share useful snippets between collaborators
"inlang.vs-code-extension" // improved i18n DX
]
}
+1 -1
View File
@@ -2,7 +2,7 @@ Copyright (c) 2020-present Cal.com, Inc.
Portions of this software are licensed as follows:
* All content that resides under https://github.com/calcom/cal.com/tree/main/packages/features/ee directory of this repository (Commercial License) is licensed under the license defined in "ee/LICENSE".
* All content that resides under https://github.com/calcom/cal.com/tree/main/packages/features/ee and https://github.com/calcom/cal.com/tree/main/apps/api/ directory of this repository (Commercial License) is licensed under the license defined in "ee/LICENSE".
* All third party components incorporated into the Cal.com Software are licensed under the original license provided by the owner of the applicable component.
* Content outside of the above mentioned directories or restrictions above is available under the "AGPLv3" license as defined below.
+14 -10
View File
@@ -121,9 +121,9 @@ Here is what you need to be able to run Cal.com.
```sh
git clone https://github.com/calcom/cal.com.git
```
> If you are on windows, run the following command on `gitbash` with admin privileges: <br>
```git clone -c core.symlinks=true https://github.com/calcom/cal.com.git``` <br>
See [docs](https://cal.com/docs/how-to-guides/how-to-troubleshoot-symbolic-link-issues-on-windows#enable-symbolic-links) for more details.
> If you are on windows, run the following command on `gitbash` with admin privileges: <br> > `git clone -c core.symlinks=true https://github.com/calcom/cal.com.git` <br>
> See [docs](https://cal.com/docs/how-to-guides/how-to-troubleshoot-symbolic-link-issues-on-windows#enable-symbolic-links) for more details.
1. Go to the project folder
@@ -158,6 +158,7 @@ yarn dx
```sh
echo 'NEXT_PUBLIC_DEBUG=1' >> .env
```
#### Gitpod Setup
1. Click the button below to open this project in Gitpod.
@@ -166,8 +167,6 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/calcom/cal.com)
#### Manual setup
1. Configure environment variables in the `.env` file. Replace `<user>`, `<pass>`, `<db-host>`, and `<db-port>` with their applicable values
@@ -175,24 +174,26 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env
```
DATABASE_URL='postgresql://<user>:<pass>@<db-host>:<db-port>'
```
<details>
<summary>If you don't know how to configure the DATABASE_URL, then follow the steps here to create a quick local DB</summary>
1. [Download](https://www.postgresql.org/download/) and install postgres in your local (if you don't have it already).
2. Create your own local db by executing `createDB <DB name>`
2. Create your own local db by executing `createDB <DB name>`
3. Now open your psql shell with the DB you created: `psql -h localhost -U postgres -d <DB name>`
4. Inside the psql shell execute `\conninfo`. And you will get the following info.
4. Inside the psql shell execute `\conninfo`. And you will get the following info.
![image](https://user-images.githubusercontent.com/39329182/236612291-51d87f69-6dc1-4a23-bf4d-1ca1754e0a35.png)
5. Now extract all the info and add it to your DATABASE_URL. The url would look something like this
`postgresql://postgres:postgres@localhost:5432/Your-DB-Name`.
5. Now extract all the info and add it to your DATABASE_URL. The url would look something like this
`postgresql://postgres:postgres@localhost:5432/Your-DB-Name`.
</details>
If you don't want to create a local DB. Then you can also consider using services like railway.app or render.
- [Setup postgres DB with railway.app](https://arctype.com/postgres/setup/railway-postgres)
- [Setup postgres DB with render](https://render.com/docs/databases)
@@ -208,10 +209,11 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env
```
In a production environment, run:
```sh
yarn workspace @calcom/prisma db-deploy
```
1. Run [mailhog](https://github.com/mailhog/MailHog) to view emails sent during development
```sh
@@ -490,7 +492,9 @@ following
10. You're good to go. Now you can easily add your ZohoCRM integration in the Cal.com settings.
### Obtaining Zoho Bigin Client ID and Secret
[Follow these steps](./packages/app-store/zoho-bigin/)
## Workflows
### Setting up SendGrid for Email reminders
+42
View File
@@ -0,0 +1,42 @@
The Cal.com Commercial License (the “Commercial License”)
Copyright (c) 2020-present Cal.com, Inc
With regard to the Cal.com Software:
This software and associated documentation files (the "Software") may only be
used in production, if you (and any entity that you represent) have agreed to,
and are in compliance with, the Cal.com Subscription Terms available
at https://cal.com/terms, or other agreements governing
the use of the Software, as mutually agreed by you and Cal.com, Inc ("Cal.com"),
and otherwise have a valid Cal.com Enterprise Edition subscription ("Commercial Subscription")
for the correct number of hosts as defined in the "Commercial Terms ("Hosts"). Subject to the foregoing sentence,
you are free to modify this Software and publish patches to the Software. You agree
that Cal.com and/or its licensors (as applicable) retain all right, title and interest in
and to all such modifications and/or patches, and all such modifications and/or
patches may only be used, copied, modified, displayed, distributed, or otherwise
exploited with a valid Commercial Subscription for the correct number of hosts.
Notwithstanding the foregoing, you may copy and modify the Software for development
and testing purposes, without requiring a subscription. You agree that Cal.com and/or
its licensors (as applicable) retain all right, title and interest in and to all such
modifications. You are not granted any other rights beyond what is expressly stated herein.
Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
and/or sell the Software.
This Commercial License applies only to the part of this Software that is not distributed under
the AGPLv3 license. Any part of this Software distributed under the MIT license or which
is served client-side as an image, font, cascading stylesheet (CSS), file which produces
or is compiled, arranged, augmented, or combined into client-side JavaScript, in whole or
in part, is copyrighted under the AGPLv3 license. The full text of this Commercial License shall
be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
For all third party components incorporated into the Cal.com Software, those
components are licensed under the original license provided by the owner of the
applicable component.
+1 -1
View File
@@ -7,7 +7,7 @@
<a href="https://cal.com/docs/enterprise-features/api#api-server-specifications">Read the API docs</a>
</div>
# Cal.com Public API
# Cal.com Public API
Welcome to the Public API ("/apps/api") of the Cal.com.
+2 -2
View File
@@ -32,9 +32,9 @@
"@sentry/nextjs": "^7.20.0",
"bcryptjs": "^2.4.3",
"memory-cache": "^0.2.0",
"next": "~13.2.1",
"next": "^13.4.6",
"next-api-middleware": "^1.0.1",
"next-axiom": "^0.16.0",
"next-axiom": "^0.17.0",
"next-swagger-doc": "^0.3.6",
"next-validations": "^0.2.0",
"typescript": "^4.9.4",
+12 -1
View File
@@ -35,6 +35,9 @@ import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransform
* status:
* type: string
* description: 'Acceptable values one of ["ACCEPTED", "PENDING", "CANCELLED", "REJECTED"]'
* description:
* type: string
* description: 'Description of the meeting'
* examples:
* editBooking:
* value:
@@ -79,7 +82,15 @@ import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransform
* "startTime": "2023-05-24T13:00:00.000Z",
* "endTime": "2023-05-24T13:30:00.000Z",
* "metadata": {},
* "status": "CANCELLED"
* "status": "CANCELLED",
* "responses": {
* "email": "john.doe@example.com",
* "name": "John Doe",
* "location": {
* "optionValue": "",
* "value": "inPerson"
* }
* }
* }
* }
* 400:
+69 -70
View File
@@ -26,13 +26,10 @@ import { defaultResponder } from "@calcom/lib/server";
* required:
* - eventTypeId
* - start
* - name
* - email
* - responses
* - timeZone
* - language
* - metadata
* - customInputs
* - location
* properties:
* eventTypeId:
* type: integer
@@ -45,29 +42,33 @@ import { defaultResponder } from "@calcom/lib/server";
* type: string
* format: date-time
* description: 'End time of the Event'
* name:
* type: string
* description: 'Name of the Attendee'
* email:
* type: string
* format: email
* description: 'Email ID of the Attendee'
* responses:
* type: object
* required:
* - name
* - email
* - location
* properties:
* name:
* type: string
* description: 'Attendee full name'
* email:
* type: string
* format: email
* description: 'Attendee email address'
* location:
* type: string
* description: 'Meeting location'
* metadata:
* type: object
* properties: {}
* description: 'Any metadata associated with the booking'
* timeZone:
* type: string
* description: 'TimeZone of the Attendee'
* language:
* type: string
* description: 'Language of the Attendee'
* metadata:
* type: object
* properties: {}
* description: 'Any metadata associated with the booking'
* customInputs:
* type: array
* items: {}
* location:
* type: string
* description: 'Meeting location'
* title:
* type: string
* description: 'Booking event title'
@@ -96,13 +97,14 @@ import { defaultResponder } from "@calcom/lib/server";
* "eventTypeId": 2323232,
* "start": "2023-05-24T13:00:00.000Z",
* "end": "2023-05-24T13:30:00.000Z",
* "name": "Hello Hello",
* "email": "hello@gmail.com",
* "responses":{
* "name": "Hello Hello",
* "email": "hello@gmail.com",
* "metadata": {},
* "location": "Calcom HQ",
* },
* "timeZone": "Europe/London",
* "language": "en",
* "metadata": {},
* "customInputs": [],
* "location": "Calcom HQ",
* "title": "Debugging between Syed Ali Shahbaz and Hello Hello",
* "description": null,
* "status": "PENDING",
@@ -117,53 +119,50 @@ import { defaultResponder } from "@calcom/lib/server";
* content:
* application/json:
* examples:
* bookings:
* booking created successfully example:
* value:
* {
* "id": 11223344,
* "uid": "5yUjmAYTDF6MXo98re8SkX",
* "userId": 123,
* "eventTypeId": 2323232,
* "title": "Debugging between Syed Ali Shahbaz and Hello Hello",
* "description": null,
* "customInputs": {},
* "responses": null,
* "startTime": "2023-05-24T13:00:00.000Z",
* "endTime": "2023-05-24T13:30:00.000Z",
* "location": "Calcom HQ",
* "createdAt": "2023-04-19T10:17:58.580Z",
* "updatedAt": null,
* "status": "PENDING",
* "paid": false,
* "destinationCalendarId": 2180,
* "cancellationReason": null,
* "rejectionReason": null,
* "dynamicEventSlugRef": null,
* "dynamicGroupSlugRef": null,
* "rescheduled": null,
* "fromReschedule": null,
* "recurringEventId": null,
* "smsReminderNumber": null,
* "scheduledJobs": [],
* "metadata": {},
* "isRecorded": false,
* "user": {
* "email": "test@cal.com",
* "name": "Syed Ali Shahbaz",
* "timeZone": "Asia/Calcutta"
* },
* "attendees": [
* {
* "id": 12345,
* "email": "hello@gmail.com",
* "name": "Hello Hello",
* "timeZone": "Europe/London",
* "locale": "en",
* "bookingId": 11223344
* "booking": {
* "id": 91,
* "userId": 5,
* "description": "",
* "eventTypeId": 7,
* "uid": "bFJeNb2uX8ANpT3JL5EfXw",
* "title": "60min between Pro Example and John Doe",
* "startTime": "2023-05-25T09:30:00.000Z",
* "endTime": "2023-05-25T10:30:00.000Z",
* "attendees": [
* {
* "email": "john.doe@example.com",
* "name": "John Doe",
* "timeZone": "Asia/Kolkata",
* "locale": "en"
* }
* ],
* "user": {
* "email": "pro@example.com",
* "name": "Pro Example",
* "timeZone": "Asia/Kolkata",
* "locale": "en"
* },
* "payment": [
* {
* "id": 1,
* "success": true,
* "paymentOption": "ON_BOOKING"
* }
* ],
* "metadata": {},
* "status": "ACCEPTED",
* "responses": {
* "email": "john.doe@example.com",
* "name": "John Doe",
* "location": {
* "optionValue": "",
* "value": "inPerson"
* }
* }
* ],
* "payment": [],
* "references": []
* }
* }
* 400:
* description: |
@@ -2,7 +2,7 @@ import { CAL_URL } from "@calcom/lib/constants";
export default function getCalLink(eventType: {
team?: { slug: string | null } | null;
owner: { username: string | null } | null;
owner?: { username: string | null } | null;
users?: { username: string | null }[];
slug: string;
}) {
+5 -5
View File
@@ -26,15 +26,15 @@ export function VariantsTable({
<table>
<RowTitles titles={titles} />
{columns.map((column) => (
<tr className="p-2 pr-6 pb-6" key={column.props.variant}>
<tr className="p-2 pb-6 pr-6" key={column.props.variant}>
<th
className="p-2 pr-6 pb-6 text-left text-sm font-normal text-[#8F8F8F]"
className="p-2 pb-6 pr-6 text-left text-sm font-normal text-[#8F8F8F]"
key={column.props.variant}>
{column.props.variant}
</th>
{React.Children.count(column.props.children) &&
React.Children.map(column.props.children, (cell) => (
<td className="p-2 pr-6 pb-6" style={{ minWidth: `${columnMinWidth}px` }}>
<td className="p-2 pb-6 pr-6" style={{ minWidth: `${columnMinWidth}px` }}>
{cell}
</td>
))}
@@ -70,9 +70,9 @@ export function VariantRow({ children }: RowProps) {
export function RowTitles({ titles }: { titles: string[] }) {
return (
<tr>
<th className="p-2 pr-6 pb-6 text-left text-sm font-normal text-[#8F8F8F]" />
<th className="p-2 pb-6 pr-6 text-left text-sm font-normal text-[#8F8F8F]" />
{titles.map((title) => (
<th className="p-2 pr-6 pb-6 text-left text-sm font-normal text-[#8F8F8F]" key={title}>
<th className="p-2 pb-6 pr-6 text-left text-sm font-normal text-[#8F8F8F]" key={title}>
{title}
</th>
))}
+5 -5
View File
@@ -13,14 +13,14 @@
"@radix-ui/react-avatar": "^1.0.0",
"@radix-ui/react-collapsible": "^1.0.0",
"@radix-ui/react-dialog": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-id": "^1.0.0",
"@radix-ui/react-popover": "^1.0.2",
"@radix-ui/react-radio-group": "^1.0.0",
"@radix-ui/react-slider": "^1.0.0",
"@radix-ui/react-switch": "^1.0.0",
"@radix-ui/react-tooltip": "^1.0.0",
"next": "^13.2.1",
"next": "^13.4.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"storybook-addon-rtl-direction": "^0.0.19"
@@ -37,8 +37,8 @@
"@storybook/react": "^6.5.13",
"@storybook/testing-library": "^0.0.13",
"@types/react": "18.0.26",
"@types/react-dom": "18.0.9",
"@vitejs/plugin-react": "^2.1.0",
"@types/react-dom": "^18.0.9",
"@vitejs/plugin-react": "^2.2.0",
"autoprefixer": "^10.4.12",
"babel-loader": "^8.2.5",
"fs": "^0.0.1-security",
@@ -48,7 +48,7 @@
"storybook-addon-designs": "^6.3.1",
"storybook-addon-next": "^1.6.9",
"storybook-react-i18next": "^1.1.2",
"tailwindcss": "^3.2.1",
"tailwindcss": "^3.3.1",
"typescript": "^4.9.4",
"vite": "^4.1.2"
}
+4 -2
View File
@@ -235,6 +235,7 @@
*/
--cal-brand:#111827;
--cal-brand-emphasis:#101010;
--cal-brand-text: white;
}
.dark {
/* background */
@@ -274,7 +275,8 @@
/* Brand shenanigans
-> These will be computed for the users theme at runtime.
*/
--cal-brand: #111827;
--cal-brand-emphasis: #101010;
--cal-brand: white;
--cal-brand-emphasis: #e1e1e1;
--cal-brand-text: black;
}
}
+2 -2
View File
@@ -14,7 +14,7 @@
"dependencies": {
"highlight.js": "^11.6.0",
"isarray": "2.0.5",
"next": "^13.2.1",
"next": "^13.4.6",
"openapi-snippet": "^0.13.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -23,7 +23,7 @@
"devDependencies": {
"@types/node": "16.9.1",
"@types/react": "18.0.26",
"@types/react-dom": "18.0.9",
"@types/react-dom": "^18.0.9",
"typescript": "^4.9.4"
}
}
+1 -1
View File
@@ -1000,7 +1000,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
</div>
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
<div className="whitespace-nowrap">Text color</div>
<div className="mt-2 mb-6 w-40 xl:mt-0 xl:w-full">
<div className="mb-6 mt-2 w-40 xl:mt-0 xl:w-full">
<ColorPicker
className="w-[130px]"
popoverAlign="start"
+1 -1
View File
@@ -71,7 +71,7 @@ const NavTabs: FC<NavTabProps> = ({ tabs, linkProps, ...props }) => {
isCurrent
? "text-emphasis border-gray-900"
: "hover:border-default hover:text-default text-subtle border-transparent",
"group inline-flex items-center border-b-2 py-4 px-1 text-sm font-medium",
"group inline-flex items-center border-b-2 px-1 py-4 text-sm font-medium",
className
)}
aria-current={isCurrent ? "page" : undefined}>
+7 -7
View File
@@ -85,7 +85,7 @@ const Component = ({
return (
<div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0">
{hasDescriptionItems && (
<div className="align-center bg-subtle mb-4 -ml-4 -mr-4 flex min-h-[450px] w-auto basis-3/5 snap-x snap-mandatory flex-row overflow-auto whitespace-nowrap p-4 md:mb-8 md:-ml-8 md:-mr-8 md:p-8 lg:mx-0 lg:mb-0 lg:max-w-2xl lg:flex-col lg:justify-center lg:rounded-md">
<div className="align-center bg-subtle -ml-4 -mr-4 mb-4 flex min-h-[450px] w-auto basis-3/5 snap-x snap-mandatory flex-row overflow-auto whitespace-nowrap p-4 md:-ml-8 md:-mr-8 md:mb-8 md:p-8 lg:mx-0 lg:mb-0 lg:max-w-2xl lg:flex-col lg:justify-center lg:rounded-md">
{descriptionItems ? (
descriptionItems.map((descriptionItem, index) =>
typeof descriptionItem === "object" ? (
@@ -260,7 +260,7 @@ const Component = ({
)}
</span>
<h4 className="text-emphasis mt-8 mb-2 font-semibold ">{t("contact")}</h4>
<h4 className="text-emphasis mb-2 mt-8 font-semibold ">{t("contact")}</h4>
<ul className="prose-sm -ml-1 -mr-1 leading-5">
{docs && (
<li>
@@ -269,7 +269,7 @@ const Component = ({
rel="noreferrer"
className="text-emphasis text-sm font-normal no-underline hover:underline"
href={docs}>
<BookOpen className="text-subtle mr-1 -mt-1 inline h-4 w-4" />
<BookOpen className="text-subtle -mt-1 mr-1 inline h-4 w-4" />
{t("documentation")}
</a>
</li>
@@ -281,7 +281,7 @@ const Component = ({
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={website}>
<ExternalLink className="text-subtle mr-1 -mt-px inline h-4 w-4" />
<ExternalLink className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{website.replace("https://", "")}
</a>
</li>
@@ -293,7 +293,7 @@ const Component = ({
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={"mailto:" + email}>
<Mail className="text-subtle mr-1 -mt-px inline h-4 w-4" />
<Mail className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{email}
</a>
@@ -306,7 +306,7 @@ const Component = ({
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={tos}>
<File className="text-subtle mr-1 -mt-px inline h-4 w-4" />
<File className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{t("terms_of_service")}
</a>
</li>
@@ -318,7 +318,7 @@ const Component = ({
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={privacy}>
<Shield className="text-subtle mr-1 -mt-px inline h-4 w-4" />
<Shield className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{t("privacy_policy")}
</a>
</li>
@@ -28,8 +28,8 @@ export default function AppsLayout({ children, actions, emptyStore, ...rest }: A
{emptyStore ? (
<EmptyScreen
Icon={AlertCircle}
headline={t("no_apps")}
description={isAdmin ? "You can enable apps in the settings" : ""}
headline={isAdmin ? t("no_apps") : t("no_apps_configured")}
description={isAdmin ? t("enable_in_settings") : t("please_contact_admin")}
buttonText={isAdmin ? t("apps_settings") : ""}
buttonOnClick={() => router.push("/settings/admin/apps/calendar")}
/>
@@ -1,6 +1,4 @@
import { z } from "zod";
import { getEventLocationType, locationKeyToString } from "@calcom/app-store/locations";
import { getEventLocationType, getTranslatedLocation } from "@calcom/app-store/locations";
import { classNames } from "@calcom/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Tooltip } from "@calcom/ui";
@@ -23,20 +21,7 @@ export function AvailableEventLocations({ locations }: { locations: Props["event
return null;
}
const translateAbleKeys = [
"in_person_attendee_address",
"in_person",
"attendee_phone_number",
"link_meeting",
"organizer_phone_number",
];
const locationKey = z.string().default("").parse(locationKeyToString(location));
const translatedLocation = location.type.startsWith("integrations:")
? eventLocationType.label
: translateAbleKeys.includes(locationKey)
? t(locationKey)
: locationKey;
const translatedLocation = getTranslatedLocation(location, eventLocationType, t);
return (
<div key={`${location.type}-${index}`} className="flex flex-row items-center text-sm font-medium">
@@ -83,7 +83,7 @@ const BookingDescription: FC<Props> = (props) => {
size="sm"
truncateAfter={3}
/>
<h2 className="text-default mt-1 mb-2 break-words text-sm font-medium ">
<h2 className="text-default mb-2 mt-1 break-words text-sm font-medium ">
{eventType.team?.parent?.name} {profile.name}
</h2>
<h1 className="font-cal text-emphasis mb-6 break-words text-2xl font-semibold leading-none">
@@ -19,7 +19,7 @@ const BookingDescriptionPayment = (props: {
return (
<p className="text-bookinglight -ml-2 px-2 text-sm ">
<CreditCard className="ml-[2px] -mt-1 inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
<CreditCard className="-mt-1 ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
{paymentAppData.paymentOption === "HOLD" ? (
<>{props.t("no_show_fee_amount", params)}</>
) : (
@@ -374,7 +374,7 @@ function BookingListItem(booking: BookingItemProps) {
</td>
<td className={"w-full px-4" + (isRejected ? " line-through" : "")} onClick={onClickTableData}>
{/* Time and Badges for mobile */}
<div className="w-full pt-4 pb-2 sm:hidden">
<div className="w-full pb-2 pt-4 sm:hidden">
<div className="flex w-full items-center justify-between sm:hidden">
<div className="text-emphasis text-sm leading-6">{startTime}</div>
<div className="text-subtle pr-2 text-sm">
@@ -423,7 +423,7 @@ function BookingListItem(booking: BookingItemProps) {
<span> </span>
{paymentAppData.enabled && !booking.paid && booking.payment.length && (
<Badge className="ms-2 me-2 hidden sm:inline-flex" variant="orange">
<Badge className="me-2 ms-2 hidden sm:inline-flex" variant="orange">
{t("pending_payment")}
</Badge>
)}
@@ -64,7 +64,7 @@ export default function CancelBooking(props: Props) {
placeholder={t("cancellation_reason_placeholder")}
value={cancellationReason}
onChange={(e) => setCancellationReason(e.target.value)}
className="mt-2 mb-4 w-full "
className="mb-4 mt-2 w-full "
rows={3}
/>
<div className="flex flex-col-reverse rtl:space-x-reverse ">
@@ -93,7 +93,7 @@ export default function CancelBooking(props: Props) {
headers: {
"Content-Type": "application/json",
},
method: "DELETE",
method: "POST",
});
if (res.status >= 200 && res.status < 300) {
@@ -25,7 +25,7 @@ function SkeletonItem() {
</div>
</div>
</div>
<div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 lg:flex">
<div className="mt-4 hidden flex-shrink-0 sm:ml-5 sm:mt-0 lg:flex">
<div className="flex justify-between space-x-2 rtl:space-x-reverse">
<SkeletonText className="h-6 w-16" />
<SkeletonText className="h-6 w-32" />
@@ -191,9 +191,9 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
)}
{!rescheduleUid && eventType.recurringEvent && (
<div className="flex items-start text-sm font-medium">
<RefreshCcw className="float-left mt-[7px] ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px] " />
<RefreshCcw className="float-left ml-[2px] mt-[7px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px] " />
<div>
<p className="mb-1 -ml-2 inline px-2 py-1">
<p className="-ml-2 mb-1 inline px-2 py-1">
{getRecurringFreq({ t, recurringEvent: eventType.recurringEvent })}
</p>
@@ -218,7 +218,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
)}
{paymentAppData.price > 0 && (
<p className="-ml-2 px-2 text-sm font-medium">
<CreditCard className="ml-[2px] -mt-1 inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
<CreditCard className="-mt-1 ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
{paymentAppData.paymentOption === "HOLD" ? (
<>
{t("no_show_fee_amount", {
@@ -534,7 +534,7 @@ const BookingPage = ({
)}>
<div className="sm:flex">
{showEventTypeDetails && (
<div className="sm:border-subtle text-default flex flex-col px-6 pt-6 pb-0 sm:w-1/2 sm:border-r sm:pb-6">
<div className="sm:border-subtle text-default flex flex-col px-6 pb-0 pt-6 sm:w-1/2 sm:border-r sm:pb-6">
<BookingDescription isBookingPage profile={profile} eventType={eventType}>
<BookingDescriptionPayment eventType={eventType} t={t} i18n={i18n} />
{!rescheduleUid && eventType.recurringEvent?.freq && recurringEventCount && (
@@ -577,11 +577,11 @@ const BookingPage = ({
</div>
{booking?.startTime && rescheduleUid && (
<div>
<p className="mt-8 mb-2 text-sm " data-testid="former_time_p">
<p className="mb-2 mt-8 text-sm " data-testid="former_time_p">
{t("former_time")}
</p>
<p className="line-through ">
<Calendar className="ml-[2px] -mt-1 inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
<Calendar className="-mt-1 ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
{isClientTimezoneAvailable &&
typeof booking.startTime === "string" &&
parseDateTimeWithTimeZone(booking.startTime, i18n.language, timezone, {
@@ -259,7 +259,7 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
{booking && (
<>
<p className="text-emphasis mt-6 mb-2 ml-1 text-sm font-bold">{t("current_location")}:</p>
<p className="text-emphasis mb-2 ml-1 mt-6 text-sm font-bold">{t("current_location")}:</p>
<p className="text-emphasis mb-2 ml-1 text-sm">
{getHumanReadableLocationValue(booking.location, t)}
</p>
@@ -49,7 +49,7 @@ export const RescheduleDialog = (props: IRescheduleDialog) => {
<div className="pt-1">
<DialogHeader title={t("send_reschedule_request")} />
<p className="text-subtle text-sm">{t("reschedule_modal_description")}</p>
<p className="text-emphasis mt-6 mb-2 text-sm font-bold">
<p className="text-emphasis mb-2 mt-6 text-sm font-bold">
{t("reason_for_reschedule_request")}
<span className="text-subtle font-normal"> (Optional)</span>
</p>
+1 -1
View File
@@ -35,7 +35,7 @@ const ErrorDebugPanel: React.FC<{ error: Props["error"]; children?: never }> = (
{debugMap.map(([key, value]) => {
if (value !== undefined) {
return (
<div key={key} className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
<div key={key} className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
<dt className="text-emphasis text-sm font-bold">{key}</dt>
<dd className="text-emphasis mt-1 text-sm sm:col-span-2 sm:mt-0">{value}</dd>
</div>
@@ -84,7 +84,7 @@ const CustomEventTypeModalForm: FC<CustomEventTypeModalFormProps> = (props) => {
backgroundImage: "url(/calendar-preview.svg)",
}}>
<div className="m-auto flex items-center justify-center self-stretch">
<div className="bg-subtle mt-3 ml-11 box-border h-[110px] w-[120px] flex-col items-start gap-1 rounded-md border border-solid border-black text-[12px] leading-3">
<div className="bg-subtle ml-11 mt-3 box-border h-[110px] w-[120px] flex-col items-start gap-1 rounded-md border border-solid border-black text-[12px] leading-3">
<p className="text-emphasis overflow-hidden text-ellipsis p-1.5 font-medium">{previewText}</p>
<p className="text-default ml-1.5 text-[10px] font-normal">8 - 10 AM</p>
</div>
@@ -38,6 +38,7 @@ const generateHashedLink = (id: number) => {
export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps, "eventType" | "team">) => {
const connectedCalendarsQuery = trpc.viewer.connectedCalendars.useQuery();
const { data: user } = trpc.viewer.me.useQuery();
const formMethods = useFormContext<FormValues>();
const { t } = useLocale();
@@ -49,6 +50,9 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
const bookingFields: Prisma.JsonObject = {};
const workflows = eventType.workflows.map((workflowOnEventType) => workflowOnEventType.workflow);
const selectedThemeIsDark =
user?.theme === "dark" ||
(!user?.theme && typeof document !== "undefined" && document.documentElement.classList.contains("dark"));
eventType.bookingFields.forEach(({ name }) => {
bookingFields[name] = name + " input";
@@ -153,7 +157,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
color="minimal"
size="sm"
aria-label="edit custom name"
className="hover:stroke-3 hover:text-emphasis min-w-fit px-0 !py-0 hover:bg-transparent"
className="hover:stroke-3 hover:text-emphasis min-w-fit !py-0 px-0 hover:bg-transparent"
onClick={() => setShowEventNameTip((old) => !old)}>
<Edit className="h-4 w-4" />
</Button>
@@ -162,7 +166,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
</div>
<hr className="border-subtle [&:has(+div:empty)]:hidden" />
<div>
<BookerLayoutSelector fallbackToUserSettings />
<BookerLayoutSelector fallbackToUserSettings isDark={selectedThemeIsDark} />
</div>
<hr className="border-subtle" />
<FormBuilder
@@ -216,7 +220,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
onChange(e ? value : "");
}}>
{/* Textfield has some margin by default we remove that so we can keep consistent alignment */}
<div className="lg:-ml-2 lg:-mb-2">
<div className="lg:-mb-2 lg:-ml-2">
<TextField
className="w-full"
label={t("redirect_success_booking")}
@@ -268,7 +272,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
color="minimal"
size="sm"
type="button"
className="hover:stroke-3 hover:text-emphasis min-w-fit px-0 !py-0 hover:bg-transparent"
className="hover:stroke-3 hover:text-emphasis min-w-fit !py-0 px-0 hover:bg-transparent"
aria-label="copy link"
onClick={() => {
navigator.clipboard.writeText(placeholderHashedLink);
@@ -171,7 +171,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
return (
<div className="space-y-8">
<div className="space-y-4 lg:space-y-8">
<div className="flex flex-col space-y-4 lg:flex-row lg:space-y-0 lg:space-x-4">
<div className="flex flex-col space-y-4 lg:flex-row lg:space-x-4 lg:space-y-0">
<div className="w-full">
<Label htmlFor="beforeBufferTime">
{t("before_event")}
@@ -245,7 +245,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
/>
</div>
</div>
<div className="flex flex-col space-y-4 lg:flex-row lg:space-y-0 lg:space-x-4">
<div className="flex flex-col space-y-4 lg:flex-row lg:space-x-4 lg:space-y-0">
<div className="w-full">
<Label htmlFor="minimumBookingNotice">
{t("minimum_booking_notice")}
@@ -416,7 +416,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
</div>
)}
{period.type === "RANGE" && (
<div className="ms-2 me-2 inline-flex space-x-2 rtl:space-x-reverse">
<div className="me-2 ms-2 inline-flex space-x-2 rtl:space-x-reverse">
<Controller
name="periodDates"
control={formMethods.control}
@@ -437,7 +437,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
/>
</div>
)}
{period.suffix ? <span className="ms-2 me-2">&nbsp;{period.suffix}</span> : null}
{period.suffix ? <span className="me-2 ms-2">&nbsp;{period.suffix}</span> : null}
</div>
);
})}
@@ -284,7 +284,7 @@ export const EventSetupTab = (
return (
<li
key={`${location.type}${index}`}
className="border-default text-default mb-2 h-9 rounded-md border py-1.5 px-2 hover:cursor-pointer">
className="border-default text-default mb-2 h-9 rounded-md border px-2 py-1.5 hover:cursor-pointer">
<div className="flex items-center justify-between">
<div className="flex items-center">
<img
@@ -298,7 +298,7 @@ export const EventSetupTab = (
)}
alt={`${eventLocationType.label} logo`}
/>
<span className="line-clamp-1 ms-1 text-sm">{eventLabel}</span>
<span className="ms-1 line-clamp-1 text-sm">{eventLabel}</span>
</div>
<div className="flex">
<button
@@ -332,7 +332,7 @@ export const EventSetupTab = (
location.type === MeetLocationType && destinationCalendar?.integration !== "google_calendar"
) && (
<div className="text-default flex text-sm">
<Check className="mt-0.5 mr-1.5 h-2 w-2.5" />
<Check className="mr-1.5 mt-0.5 h-2 w-2.5" />
<Trans i18nKey="event_type_requres_google_cal">
<p>
The “Add to calendar” for this event type needs to be a Google Calendar for Meet to work.
@@ -369,7 +369,7 @@ function EventTypeSingleLayout({
</DropdownItem>
</DropdownMenuItem>
<DropdownMenuSeparator />
<div className="hover:bg-subtle flex h-9 cursor-pointer flex-row items-center justify-between py-2 px-4">
<div className="hover:bg-subtle flex h-9 cursor-pointer flex-row items-center justify-between px-4 py-2">
<Skeleton
as={Label}
htmlFor="hiddenSwitch"
@@ -32,11 +32,11 @@ function SkeletonItem() {
<div className="">
<ul className="mt-2 flex space-x-4 rtl:space-x-reverse ">
<li className="flex items-center whitespace-nowrap">
<Clock className="text-subtle mt-0.5 mr-1.5 inline h-4 w-4" />
<Clock className="text-subtle mr-1.5 mt-0.5 inline h-4 w-4" />
<SkeletonText className="h-4 w-12" />
</li>
<li className="flex items-center whitespace-nowrap">
<User className="text-subtle mt-0.5 mr-1.5 inline h-4 w-4" />
<User className="text-subtle mr-1.5 mt-0.5 inline h-4 w-4" />
<SkeletonText className="h-4 w-12" />
</li>
</ul>
@@ -82,7 +82,7 @@ const DisableTwoFactorAuthModal = ({ onDisable, onCancel }: DisableTwoFactorAuth
</div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<Button type="submit" className="ms-2 me-2" disabled={isDisabling}>
<Button type="submit" className="me-2 ms-2" disabled={isDisabling}>
{t("disable")}
</Button>
<Button color="secondary" onClick={onCancel}>
@@ -19,7 +19,7 @@ const DisableUserImpersonation = ({ disableImpersonation }: { disableImpersonati
return (
<>
<div className="flex flex-col justify-between pt-9 pl-2 sm:flex-row">
<div className="flex flex-col justify-between pl-2 pt-9 sm:flex-row">
<div>
<div className="flex flex-row items-center">
<h2 className="font-cal text-emphasis text-lg font-medium leading-6">
@@ -178,19 +178,19 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
<WithStep step={SetupStep.ConfirmPassword} current={step}>
<Button
type="submit"
className="ms-2 me-2"
className="me-2 ms-2"
onClick={handleSetup}
disabled={password.length === 0 || isSubmitting}>
{t("continue")}
</Button>
</WithStep>
<WithStep step={SetupStep.DisplayQrCode} current={step}>
<Button type="submit" className="ms-2 me-2" onClick={() => setStep(SetupStep.EnterTotpCode)}>
<Button type="submit" className="me-2 ms-2" onClick={() => setStep(SetupStep.EnterTotpCode)}>
{t("continue")}
</Button>
</WithStep>
<WithStep step={SetupStep.EnterTotpCode} current={step}>
<Button type="submit" className="ms-2 me-2" disabled={isSubmitting}>
<Button type="submit" className="me-2 ms-2" disabled={isSubmitting}>
{t("enable")}
</Button>
</WithStep>
@@ -14,7 +14,7 @@ const TwoFactorAuthSection = ({ twoFactorEnabled }: { twoFactorEnabled: boolean
return (
<>
<div className="flex flex-col justify-between pt-9 pl-2 sm:flex-row">
<div className="flex flex-col justify-between pl-2 pt-9 sm:flex-row">
<div>
<div className="flex flex-row items-center">
<h2 className="font-cal text-emphasis text-lg font-medium leading-6">{t("2fa")}</h2>
@@ -6,7 +6,7 @@ const TwoFactorModalHeader = ({ title, description }: { title: string; descripti
<div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10">
<Shield className="text-inverted h-6 w-6" />
</div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
<h3 className="font-cal text-emphasis text-lg font-medium leading-6" id="modal-title">
{title}
</h3>
@@ -90,7 +90,7 @@ const DisableTwoFactorAuthModal = ({
</div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<Button type="submit" className="ms-2 me-2" data-testid="disable-2fa" disabled={isDisabling}>
<Button type="submit" className="me-2 ms-2" data-testid="disable-2fa" disabled={isDisabling}>
{t("disable")}
</Button>
<Button color="secondary" onClick={onCancel}>
@@ -177,7 +177,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
<WithStep step={SetupStep.ConfirmPassword} current={step}>
<Button
type="submit"
className="ms-2 me-2"
className="me-2 ms-2"
onClick={handleSetup}
disabled={password.length === 0 || isSubmitting}>
{t("continue")}
@@ -187,13 +187,13 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
<Button
type="submit"
data-testid="goto-otp-screen"
className="ms-2 me-2"
className="me-2 ms-2"
onClick={() => setStep(SetupStep.EnterTotpCode)}>
{t("continue")}
</Button>
</WithStep>
<WithStep step={SetupStep.EnterTotpCode} current={step}>
<Button type="submit" className="ms-2 me-2" data-testid="enable-2fa" disabled={isSubmitting}>
<Button type="submit" className="me-2 ms-2" data-testid="enable-2fa" disabled={isSubmitting}>
{t("enable")}
</Button>
</WithStep>
+1 -1
View File
@@ -111,7 +111,7 @@ export const AdminUser = (props: { onSubmit: () => void; onError: () => void; on
<Label htmlFor="username" className={classNames(longWebsiteUrl && "mb-0")}>
<span className="block">{t("username")}</span>
{longWebsiteUrl && (
<small className="items-centerpx-3 bg-subtle border-default text-subtle mt-2 inline-flex rounded-t-md border border-b-0 py-1 px-3">
<small className="items-centerpx-3 bg-subtle border-default text-subtle mt-2 inline-flex rounded-t-md border border-b-0 px-3 py-1">
{process.env.NEXT_PUBLIC_WEBSITE_URL}
</small>
)}
+1 -1
View File
@@ -27,7 +27,7 @@ const StepDone = (props: {
<Check className="text-inverted dark:bg-default dark:text-default inline-block h-10 w-10" />
</div>
<div className="max-w-[420px] text-center">
<h2 className="mt-6 mb-1 text-lg font-medium dark:text-gray-300">{t("all_done")}</h2>
<h2 className="mb-1 mt-6 text-lg font-medium dark:text-gray-300">{t("all_done")}</h2>
</div>
</div>
</form>
+2 -2
View File
@@ -30,9 +30,9 @@ const Member = ({ member, teamName }: { member: MemberType; teamName: string | n
alt={member.name || ""}
imageSrc={WEBAPP_URL + "/" + member.username + "/avatar.png"}
/>
<section className="line-clamp-4 mt-2 w-full space-y-1">
<section className="mt-2 line-clamp-4 w-full space-y-1">
<p className="text-default font-medium">{member.name}</p>
<div className="line-clamp-3 text-subtle overflow-ellipsis text-sm font-normal">
<div className="text-subtle line-clamp-3 overflow-ellipsis text-sm font-normal">
{!isBioEmpty ? (
<>
<div
+2 -2
View File
@@ -23,7 +23,7 @@ const EditableHeading = function EditableHeading({
<label className="min-w-8 relative inline-block">
<span className="whitespace-pre text-xl tracking-normal text-transparent">{value}&nbsp;</span>
{!isEditing && isReady && (
<Edit2 className=" text-subtle group-hover:text-subtle ml-1 -mt-px inline h-3 w-3" />
<Edit2 className=" text-subtle group-hover:text-subtle -mt-px ml-1 inline h-3 w-3" />
)}
<input
{...passThroughProps}
@@ -31,7 +31,7 @@ const EditableHeading = function EditableHeading({
value={value}
required
className={classNames(
"text-emphasis hover:text-default focus:text-emphasis absolute top-0 left-0 w-full cursor-pointer truncate border-none bg-transparent p-0 align-top text-xl focus:outline-none focus:ring-0"
"text-emphasis hover:text-default focus:text-emphasis absolute left-0 top-0 w-full cursor-pointer truncate border-none bg-transparent p-0 align-top text-xl focus:outline-none focus:ring-0"
)}
onFocus={(e) => {
setIsEditing(true);
+1 -1
View File
@@ -6,7 +6,7 @@ export default function InfoBadge({ content }: { content: string }) {
<>
<Tooltip side="top" content={content}>
<span title={content}>
<Info className="text-subtle relative top-px left-1 right-1 mt-px h-4 w-4" />
<Info className="text-subtle relative left-1 right-1 top-px mt-px h-4 w-4" />
</span>
</Tooltip>
</>
+1 -1
View File
@@ -14,7 +14,7 @@ export default function ModalContainer(
}>
) {
return (
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
<div className="flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0">
<Dialog open={props.isOpen} onOpenChange={props.onExit}>
<DialogContent>
<div
@@ -224,7 +224,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
}}
data-testid="username-input"
/>
<div className="absolute top-0 right-2 flex flex-row">
<div className="absolute right-2 top-0 flex flex-row">
<span
className={classNames(
"mx-2 py-2",
@@ -80,7 +80,7 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.Component
const ActionButtons = () => {
return usernameIsAvailable && currentUsername !== inputUsernameValue ? (
<div className="ms-2 me-2 flex flex-row space-x-2">
<div className="me-2 ms-2 flex flex-row space-x-2">
<Button
type="button"
onClick={() => setOpenDialogSaveUsername(true)}
@@ -24,9 +24,9 @@ interface UsernameAvailabilityFieldProps {
function useUserNamePrefix(organization: RouterOutputs["viewer"]["me"]["organization"]): string {
return organization
? organization.slug
? `${organization.slug}.${subdomainSuffix()}`
? `${organization.slug}.${subdomainSuffix()}/`
: organization.metadata && organization.metadata.requestedSlug
? `${organization.metadata.requestedSlug}.${subdomainSuffix()}`
? `${organization.metadata.requestedSlug}.${subdomainSuffix()}/`
: process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")
: process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "");
}
+2 -1
View File
@@ -5,7 +5,8 @@ import type { GetServerSidePropsContext } from "next";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import type { Maybe } from "@calcom/trpc/server";
import { i18n } from "../../../next-i18next.config";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { i18n } = require("@calcom/config/next-i18next.config");
export function getLocaleFromHeaders(req: IncomingMessage): string {
let preferredLocale: string | null | undefined;
-30
View File
@@ -3,7 +3,6 @@ import { collectEvents } from "next-collect/server";
import type { NextMiddleware } from "next/server";
import { NextResponse, userAgent } from "next/server";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { CONSOLE_URL, WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants";
import { isIpInBanlist } from "@calcom/lib/getIP";
import { extendEventData, nextCollectBasicSettings } from "@calcom/lib/telemetry";
@@ -11,20 +10,11 @@ import { extendEventData, nextCollectBasicSettings } from "@calcom/lib/telemetry
const middleware: NextMiddleware = async (req) => {
const url = req.nextUrl;
const requestHeaders = new Headers(req.headers);
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(req.headers.get("host") ?? "");
/**
* We are using env variable to toggle new-booker because using flags would be an unnecessary delay for booking pages
* Also, we can't easily identify the booker page requests here(to just fetch the flags for those requests)
*/
// Make sure we are in the presence of an organization
if (isValidOrgDomain && url.pathname === "/") {
// In the presence of an organization, cover its profile page at "/"
// rewrites for org profile page using team profile page
url.pathname = `/org/${currentOrgDomain}`;
return NextResponse.rewrite(url);
}
if (isIpInBanlist(req) && url.pathname !== "/api/nope") {
// DDOS Prevention: Immediately end request with no response - Avoids a redirect as well initiated by NextAuth on invalid callback
req.nextUrl.pathname = "/api/nope";
@@ -83,26 +73,6 @@ const middleware: NextMiddleware = async (req) => {
requestHeaders.set("x-csp-enforce", "true");
}
if (isValidOrgDomain) {
// Match /:slug to determine if it corresponds to org subteam slug or org user slug
const slugs = /^\/([^/]+)(\/[^/]+)?$/.exec(url.pathname);
// In the presence of an organization, if not team profile, a user or team is being accessed
if (slugs) {
const [_, teamName, eventType] = slugs;
// Fetch the corresponding subteams for the entered organization
const getSubteams = await fetch(`${WEBAPP_URL}/api/organizations/${currentOrgDomain}/subteams`);
if (getSubteams.ok) {
const data = await getSubteams.json();
// Treat entered slug as a team if found in the subteams fetched
if (data.slugs.includes(teamName)) {
// Rewriting towards /team/:slug to bring up the team profile within the org
url.pathname = `/team/${teamName}${eventType ?? ""}`;
return NextResponse.rewrite(url);
}
}
}
}
return NextResponse.next({
request: {
headers: requestHeaders,
+69 -25
View File
@@ -1,10 +1,10 @@
require("dotenv").config({ path: "../../.env" });
const CopyWebpackPlugin = require("copy-webpack-plugin");
const os = require("os");
const glob = require("glob");
const englishTranslation = require("./public/static/locales/en/common.json");
const { withAxiom } = require("next-axiom");
const { i18n } = require("./next-i18next.config");
const { pages } = require("./pages");
if (!process.env.NEXTAUTH_SECRET) throw new Error("Please set NEXTAUTH_SECRET");
if (!process.env.CALENDSO_ENCRYPTION_KEY) throw new Error("Please set CALENDSO_ENCRYPTION_KEY");
@@ -71,6 +71,13 @@ const informAboutDuplicateTranslations = () => {
informAboutDuplicateTranslations();
const getSubdomain = () => {
const _url = new URL(process.env.NEXT_PUBLIC_WEBAPP_URL);
const regex = new RegExp(/^([a-z]+\:\/{2})?((?<subdomain>[\w-]+)\.[\w-]+\.\w+)$/);
//console.log(_url.hostname, _url.hostname.match(regex));
return _url.hostname.match(regex)?.groups?.subdomain || null;
};
const plugins = [];
if (process.env.ANALYZE === "true") {
// only load dependency if env `ANALYZE` was set
@@ -82,16 +89,19 @@ if (process.env.ANALYZE === "true") {
plugins.push(withAxiom);
/** Needed to rewrite public booking page, gets all static pages but [user] */
const pages = glob
.sync("pages/**/[^_]*.{tsx,js,ts}", { cwd: __dirname })
.map((filename) =>
filename
.substr(6)
.replace(/(\.tsx|\.js|\.ts)/, "")
.replace(/\/.*/, "")
)
.filter((v, i, self) => self.indexOf(v) === i && !v.startsWith("[user]"));
// .* matches / as well(Note: *(i.e wildcard) doesn't match / but .*(i.e. RegExp) does)
// It would match /free/30min but not /bookings/upcoming because 'bookings' is an item in pages
// It would also not match /free/30min/embed because we are ensuring just two slashes
// ?!book ensures it doesn't match /free/book page which doesn't have a corresponding new-booker page.
// [^/]+ makes the RegExp match the full path, it seems like a partial match doesn't work.
// book$ ensures that only /book is excluded from rewrite(which is at the end always) and not /booked
// Important Note: When modifying these RegExps update apps/web/test/lib/next-config.test.ts as well
const userTypeRouteRegExp = `/:user((?!${pages.join("/|")})[^/]*)/:type((?!book$)[^/]+)`;
const teamTypeRouteRegExp = "/team/:slug/:type((?!book$)[^/]+)";
const privateLinkRouteRegExp = "/d/:link/:slug((?!book$)[^/]+)";
const embedUserTypeRouteRegExp = `/:user((?!${pages.join("|")}).*)/:type/embed`;
const embedTeamTypeRouteRegExp = "/team/:slug/:type/embed";
/** @type {import("next").NextConfig} */
const nextConfig = {
@@ -183,12 +193,43 @@ const nextConfig = {
return config;
},
async rewrites() {
// .* matches / as well(Note: *(i.e wildcard) doesn't match / but .*(i.e. RegExp) does)
// It would match /free/30min but not /bookings/upcoming because 'bookings' is an item in pages
// It would also not match /free/30min/embed because we are ensuring just two slashes
const userTypeRouteRegExp = `/:user((?!${pages.join("|")})[^/]*)/:type`;
const defaultSubdomain = getSubdomain();
const subdomain = defaultSubdomain ? `(?!${defaultSubdomain})[^.]+` : "[^.]+";
let rewrites = [
const beforeFiles = [
{
has: [
{
type: "host",
value: `^(?<orgSlug>${subdomain})\\..*`,
},
],
source: "/",
destination: "/team/:orgSlug",
},
{
has: [
{
type: "host",
value: `^(?<orgSlug>${subdomain})\\..*`,
},
],
source: `/:user((?!${pages.join("|")}|_next|public)[a-zA-Z0-9\-_]+)`,
destination: "/org/:orgSlug/:user",
},
{
has: [
{
type: "host",
value: `^(?<orgSlug>${subdomain}[^.]+)\\..*`,
},
],
source: `/:user((?!${pages.join("|")}|_next|public))/:path*`,
destination: "/:user/:path*",
},
];
let afterFiles = [
{
source: "/org/:slug",
destination: "/team/:slug",
@@ -232,12 +273,12 @@ const nextConfig = {
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type",
source: teamTypeRouteRegExp,
destination: "/new-booker/team/:slug/:type",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/d/:link/:slug",
source: privateLinkRouteRegExp,
destination: "/new-booker/d/:link/:slug",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
@@ -245,12 +286,12 @@ const nextConfig = {
// Keep cookie based booker enabled to test new-booker embed in production
...[
{
source: `/:user((?!${pages.join("|")}).*)/:type/embed`,
source: embedUserTypeRouteRegExp,
destination: "/new-booker/:user/:type/embed",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type/embed",
source: embedTeamTypeRouteRegExp,
destination: "/new-booker/team/:slug/:type/embed",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
@@ -268,7 +309,7 @@ const nextConfig = {
// Enable New Booker for all Embed Requests
if (process.env.NEW_BOOKER_ENABLED_FOR_EMBED === "1") {
console.log("Enabling New Booker for Embed");
rewrites.push(
afterFiles.push(
...[
{
source: `/:user((?!${pages.join("|")}).*)/:type/embed`,
@@ -285,24 +326,27 @@ const nextConfig = {
// Enable New Booker for All but embed Requests
if (process.env.NEW_BOOKER_ENABLED_FOR_NON_EMBED === "1") {
console.log("Enabling New Booker for Non-Embed");
rewrites.push(
afterFiles.push(
...[
{
source: userTypeRouteRegExp,
destination: "/new-booker/:user/:type",
},
{
source: "/team/:slug/:type",
source: teamTypeRouteRegExp,
destination: "/new-booker/team/:slug/:type",
},
{
source: "/d/:link/:slug",
source: privateLinkRouteRegExp,
destination: "/new-booker/d/:link/:slug",
},
]
);
}
return rewrites;
return {
beforeFiles,
afterFiles,
};
},
async headers() {
return [
+9 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "3.0.1",
"version": "3.0.6",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
@@ -48,7 +48,7 @@
"@radix-ui/react-avatar": "^1.0.0",
"@radix-ui/react-collapsible": "^1.0.0",
"@radix-ui/react-dialog": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-id": "^1.0.0",
"@radix-ui/react-popover": "^1.0.2",
"@radix-ui/react-radio-group": "^1.0.0",
@@ -61,6 +61,8 @@
"@tanstack/react-query": "^4.3.9",
"@tremor/react": "^2.0.0",
"@types/turndown": "^5.0.1",
"@upstash/ratelimit": "^0.4.3",
"@upstash/redis": "^1.21.0",
"@vercel/edge-config": "^0.1.1",
"@vercel/edge-functions-ui": "^0.2.1",
"@vercel/og": "^0.5.0",
@@ -86,12 +88,12 @@
"memory-cache": "^0.2.0",
"micro": "^10.0.1",
"mime-types": "^2.1.35",
"next": "^13.2.1",
"next": "^13.4.6",
"next-auth": "^4.20.1",
"next-axiom": "^0.16.0",
"next-axiom": "^0.17.0",
"next-collect": "^0.2.1",
"next-i18next": "^11.3.0",
"next-seo": "^4.26.0",
"next-i18next": "^13.2.2",
"next-seo": "^6.0.0",
"next-themes": "^0.2.0",
"nodemailer": "^6.7.8",
"otplib": "^12.0.1",
@@ -107,7 +109,7 @@
"react-hook-form": "^7.43.3",
"react-hot-toast": "^2.3.0",
"react-intl": "^5.25.1",
"react-live-chat-loader": "^2.7.3",
"react-live-chat-loader": "^2.8.1",
"react-multi-email": "^0.5.3",
"react-phone-input-2": "^2.15.1",
"react-phone-number-input": "^3.2.7",
+14
View File
@@ -0,0 +1,14 @@
const glob = require("glob");
/** Needed to rewrite public booking page, gets all static pages but [user] */
const pages = glob
.sync("pages/**/[^_]*.{tsx,js,ts}", { cwd: __dirname })
.map((filename) =>
filename
.substr(6)
.replace(/(\.tsx|\.js|\.ts)/, "")
.replace(/\/.*/, "")
)
.filter((v, i, self) => self.indexOf(v) === i && !v.startsWith("[user]"));
exports.pages = pages;
+2 -2
View File
@@ -59,7 +59,7 @@ export default function Custom404() {
}}
/>
<div className="min-h-screen bg-white px-4" data-testid="404-page">
<main className="mx-auto max-w-xl pt-16 pb-6 sm:pt-24">
<main className="mx-auto max-w-xl pb-6 pt-16 sm:pt-24">
<div className="text-center">
<p className="text-sm font-semibold uppercase tracking-wide text-black">{t("error_404")}</p>
<h1 className="font-cal mt-2 text-4xl font-extrabold text-gray-900 sm:text-5xl">
@@ -91,7 +91,7 @@ export default function Custom404() {
}}
/>
<div className="bg-default min-h-screen px-4" data-testid="404-page">
<main className="mx-auto max-w-xl pt-16 pb-6 sm:pt-24">
<main className="mx-auto max-w-xl pb-6 pt-16 sm:pt-24">
{isSignup && process.env.NEXT_PUBLIC_WEBAPP_URL !== "https://app.cal.com" ? (
<div>
<div>
+1 -1
View File
@@ -21,7 +21,7 @@ export default function Error500() {
<div className="rtl: bg-default m-auto rounded-md p-10 text-right ltr:text-left">
<h1 className="font-cal text-emphasis text-6xl">500</h1>
<h2 className="text-emphasis mt-6 text-2xl font-medium">It&apos;s not you, it&apos;s us.</h2>
<p className="text-default mt-4 mb-6 max-w-2xl text-sm">
<p className="text-default mb-6 mt-4 max-w-2xl text-sm">
Something went wrong on our end. Get in touch with our support team, and we’ll get it fixed right
away for you.
</p>
+9 -4
View File
@@ -37,7 +37,8 @@ import PageWrapper from "@components/PageWrapper";
import { ssrInit } from "@server/lib/ssr";
export default function User(props: inferSSRProps<typeof getServerSideProps> & EmbedProps) {
export type UserPageProps = inferSSRProps<typeof getServerSideProps> & EmbedProps;
export function UserPage(props: UserPageProps) {
const {
users,
profile,
@@ -102,6 +103,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed;
const query = { ...router.query };
delete query.user; // So it doesn't display in the Link (and make tests fail)
delete query.orgSlug;
const nameOrUsername = user.name || user.username || "";
/*
@@ -132,7 +134,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
className={classNames(
shouldAlignCentrally ? "mx-auto" : "",
isEmbed ? "border-booker border-booker-width bg-default rounded-md border" : "",
"max-w-3xl py-24 px-4"
"max-w-3xl px-4 py-24"
)}>
{isSingleUser && ( // When we deal with a single user, not dynamic group
<div className="mb-8 text-center">
@@ -207,8 +209,8 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
);
}
User.isBookingPage = true;
User.PageWrapper = PageWrapper;
UserPage.isBookingPage = true;
UserPage.PageWrapper = PageWrapper;
const getEventTypesWithHiddenFromDB = async (userId: number) => {
return (
@@ -360,6 +362,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
const safeBio = markdownToSafeHTML(user.bio) || "";
const markdownStrippedBio = stripMarkdown(user?.bio || "");
return {
props: {
users,
@@ -385,3 +388,5 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
},
};
};
export default UserPage;
+9 -9
View File
@@ -5,15 +5,12 @@ import { z } from "zod";
import dayjs from "@calcom/dayjs";
import { sendPasswordResetEmail } from "@calcom/emails";
import { PASSWORD_RESET_EXPIRY_HOURS } from "@calcom/emails/templates/forgot-password-email";
import rateLimit from "@calcom/lib/rateLimit";
import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode";
import rateLimiter from "@calcom/lib/rateLimit";
import { defaultHandler } from "@calcom/lib/server";
import { getTranslation } from "@calcom/lib/server/i18n";
import prisma from "@calcom/prisma";
const limiter = rateLimit({
intervalInMs: 60 * 1000, // 1 minute
});
async function handler(req: NextApiRequest, res: NextApiResponse) {
const t = await getTranslation(req.body.language ?? "en", "common");
@@ -37,10 +34,13 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
// 10 requests per minute
try {
limiter.check(10, ip);
} catch (e) {
return res.status(429).json({ message: "Too Many Requests." });
const limiter = await rateLimiter();
const limit = await limiter({
identifier: ip,
});
if (!limit.success) {
throw new Error(ErrorCode.RateLimitExceeded);
}
try {
+14 -6
View File
@@ -1,4 +1,5 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import { getCalendarCredentials, getConnectedCalendars } from "@calcom/core/CalendarManager";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
@@ -6,6 +7,11 @@ import notEmpty from "@calcom/lib/notEmpty";
import { revalidateCalendarCache } from "@calcom/lib/server/revalidateCalendarCache";
import prisma from "@calcom/prisma";
const selectedCalendarSelectSchema = z.object({
integration: z.string(),
externalId: z.string(),
});
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const session = await getServerSession({ req, res });
@@ -32,18 +38,19 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const { credentials, ...user } = userWithCredentials;
if (req.method === "POST") {
const { integration, externalId } = selectedCalendarSelectSchema.parse(req.body);
await prisma.selectedCalendar.upsert({
where: {
userId_integration_externalId: {
userId: user.id,
integration: req.body.integration,
externalId: req.body.externalId,
integration,
externalId,
},
},
create: {
userId: user.id,
integration: req.body.integration,
externalId: req.body.externalId,
integration,
externalId,
},
// already exists
update: {},
@@ -52,12 +59,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
if (req.method === "DELETE") {
const { integration, externalId } = selectedCalendarSelectSchema.parse(req.query);
await prisma.selectedCalendar.delete({
where: {
userId_integration_externalId: {
userId: user.id,
externalId: req.body.externalId,
integration: req.body.integration,
externalId,
integration,
},
},
});
+2 -1
View File
@@ -1,6 +1,7 @@
import type { GetStaticPropsContext } from "next";
import Link from "next/link";
import { useRouter } from "next/router";
import type { ReactElement } from "react";
import z from "zod";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -21,7 +22,7 @@ export default function Error() {
const router = useRouter();
const { error } = querySchema.parse(router.query);
const isTokenVerificationError = error?.toLowerCase() === "verification";
let errorMsg = <SkeletonText />;
let errorMsg: string | ReactElement = <SkeletonText />;
if (router.isReady) {
errorMsg = isTokenVerificationError ? t("token_invalid_expired") : t("error_during_login");
}
+1 -1
View File
@@ -254,7 +254,7 @@ export default function Availability() {
</ConfirmationDialogContent>
</Dialog>
<DropdownMenuSeparator />
<div className="flex h-9 flex-row items-center justify-between py-2 px-4 hover:bg-gray-100">
<div className="flex h-9 flex-row items-center justify-between px-4 py-2 hover:bg-gray-100">
<Skeleton
as={Label}
htmlFor="hiddenSwitch"
+8 -8
View File
@@ -306,7 +306,7 @@ export default function Success(props: SuccessProps) {
/>
)}
{userIsOwner && !isEmbed && (
<div className="mt-2 ml-4 -mb-4">
<div className="-mb-4 ml-4 mt-2">
<Link
href={allRemainingBookings ? "/bookings/recurring" : "/bookings/upcoming"}
className="hover:bg-subtle text-subtle hover:text-default mt-2 inline-flex px-1 py-2 text-sm dark:hover:bg-transparent">
@@ -321,7 +321,7 @@ export default function Success(props: SuccessProps) {
<div
className={classNames(
shouldAlignCentrally ? "text-center" : "",
"flex items-end justify-center px-4 pt-4 pb-20 sm:block sm:p-0"
"flex items-end justify-center px-4 pb-20 pt-4 sm:block sm:p-0"
)}>
<div
className={classNames("my-4 transition-opacity sm:my-0", isEmbed ? "" : " inset-0")}
@@ -330,7 +330,7 @@ export default function Success(props: SuccessProps) {
className={classNames(
"main inline-block transform overflow-hidden rounded-lg border sm:my-8 sm:max-w-xl",
!isBackgroundTransparent && " bg-default dark:bg-muted border-booker border-booker-width",
"px-8 pt-5 pb-4 text-left align-bottom transition-all sm:w-full sm:py-8 sm:align-middle"
"px-8 pb-4 pt-5 text-left align-bottom transition-all sm:w-full sm:py-8 sm:align-middle"
)}
role="dialog"
aria-modal="true"
@@ -356,7 +356,7 @@ export default function Success(props: SuccessProps) {
{needsConfirmation && !isCancelled && <Calendar className="text-emphasis h-5 w-5" />}
{isCancelled && <X className="h-5 w-5 text-red-600" />}
</div>
<div className="mt-6 mb-8 text-center last:mb-0">
<div className="mb-8 mt-6 text-center last:mb-0">
<h3
className="text-emphasis text-2xl font-semibold leading-6"
data-testid={isCancelled ? "cancelled-headline" : ""}
@@ -394,7 +394,7 @@ export default function Success(props: SuccessProps) {
{(isCancelled || reschedule) && cancellationReason && (
<>
<div className="font-medium">
{isCancelled ? t("reason") : t("reschedule_reason_success_page")}
{isCancelled ? t("reason") : t("reschedule_reason")}
</div>
<div className="col-span-2 mb-6 last:mb-0">{cancellationReason}</div>
</>
@@ -615,7 +615,7 @@ export default function Success(props: SuccessProps) {
className="border-subtle text-default mx-2 h-10 w-10 rounded-sm border px-3 py-2"
target="_blank">
<svg
className="mr-1 -mt-1.5 inline-block h-4 w-4"
className="-mt-1.5 mr-1 inline-block h-4 w-4"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24">
@@ -642,7 +642,7 @@ export default function Success(props: SuccessProps) {
className="text-default border-subtle mx-2 h-10 w-10 rounded-sm border px-3 py-2"
target="_blank">
<svg
className="mr-1 -mt-1.5 inline-block h-4 w-4"
className="-mt-1.5 mr-1 inline-block h-4 w-4"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24">
@@ -659,7 +659,7 @@ export default function Success(props: SuccessProps) {
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1000 1000"
className="mr-1 -mt-1.5 inline-block h-4 w-4">
className="-mt-1.5 mr-1 inline-block h-4 w-4">
<title>{t("other")}</title>
<path d="M971.3,154.9c0-34.7-28.2-62.9-62.9-62.9H611.7c-1.3,0-2.6,0.1-3.9,0.2V10L28.7,87.3v823.4L607.8,990v-84.6c1.3,0.1,2.6,0.2,3.9,0.2h296.7c34.7,0,62.9-28.2,62.9-62.9V154.9z M607.8,636.1h44.6v-50.6h-44.6v-21.9h44.6v-50.6h-44.6v-92h277.9v230.2c0,3.8-3.1,7-7,7H607.8V636.1z M117.9,644.7l-50.6-2.4V397.5l50.6-2.2V644.7z M288.6,607.3c17.6,0.6,37.3-2.8,49.1-7.2l9.1,48c-11,5.1-35.6,9.9-66.9,8.3c-85.4-4.3-127.5-60.7-127.5-132.6c0-86.2,57.8-136.7,133.2-140.1c30.3-1.3,53.7,4,64.3,9.2l-12.2,48.9c-12.1-4.9-28.8-9.2-49.5-8.6c-45.3,1.2-79.5,30.1-79.5,87.4C208.8,572.2,237.8,605.7,288.6,607.3z M455.5,665.2c-32.4-1.6-63.7-11.3-79.1-20.5l12.6-50.7c16.8,9.1,42.9,18.5,70.4,19.4c30.1,1,46.3-10.7,46.3-29.3c0-17.8-14-28.1-48.8-40.6c-46.9-16.4-76.8-41.7-76.8-81.5c0-46.6,39.3-84.1,106.8-87.1c33.3-1.5,58.3,4.2,76.5,11.2l-15.4,53.3c-12.1-5.3-33.5-12.8-62.3-12c-28.3,0.8-41.9,13.6-41.9,28.1c0,17.8,16.1,25.5,53.6,39c52.9,18.5,78.4,45.3,78.4,86.4C575.6,629.7,536.2,669.2,455.5,665.2z M935.3,842.7c0,14.9-12.1,27-27,27H611.7c-1.3,0-2.6-0.2-3.9-0.4V686.2h270.9c19.2,0,34.9-15.6,34.9-34.9V398.4c0-19.2-15.6-34.9-34.9-34.9h-47.1v-32.3H808v32.3h-44.8v-32.3h-22.7v32.3h-43.3v-32.3h-22.7v32.3H628v-32.3h-20.2v-203c1.31.2,2.6-0.4,3.9-0.4h296.7c14.9,0,27,12.1,27,27L935.3,842.7L935.3,842.7z" />
</svg>
+3 -7
View File
@@ -168,18 +168,14 @@ const EventTypePage = (props: EventTypeSetupProps) => {
}
if (err.data?.code === "UNAUTHORIZED") {
message = `${err.data.code}: You are not able to update this event`;
message = `${err.data.code}: ${t("error_event_type_unauthorized_update")}`;
}
if (err.data?.code === "PARSE_ERROR" || err.data?.code === "BAD_REQUEST") {
message = `${err.data.code}: ${err.message}`;
message = `${err.data.code}: ${t(err.message)}`;
}
if (message) {
showToast(message, "error");
} else {
showToast(err.message, "error");
}
showToast(message ? t(message) : t(err.message), "error");
},
});
+64 -56
View File
@@ -13,7 +13,8 @@ import useIntercom from "@calcom/features/ee/support/lib/intercom/useIntercom";
import { EventTypeDescriptionLazy as EventTypeDescription } from "@calcom/features/eventtypes/components";
import CreateEventTypeDialog from "@calcom/features/eventtypes/components/CreateEventTypeDialog";
import { DuplicateDialog } from "@calcom/features/eventtypes/components/DuplicateDialog";
import { OrganizationEventTypeFilter } from "@calcom/features/eventtypes/components/OrganizationEventTypeFilter";
import { TeamsFilter } from "@calcom/features/filters/components/TeamsFilter";
import { getTeamsFiltersFromQuery } from "@calcom/features/filters/lib/getTeamsFiltersFromQuery";
import Shell from "@calcom/features/shell/Shell";
import { APP_NAME, CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -47,7 +48,6 @@ import {
HeadSeo,
Skeleton,
Label,
VerticalDivider,
Alert,
} from "@calcom/ui";
import {
@@ -383,7 +383,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
<div className="group flex w-full max-w-full items-center justify-between overflow-hidden px-4 py-4 sm:px-6">
{!(firstItem && firstItem.id === type.id) && (
<button
className="bg-default text-muted hover:text-emphasis border-default hover:border-emphasis invisible absolute left-[5px] -mt-4 mb-4 -ml-4 hidden h-6 w-6 scale-0 items-center justify-center rounded-md border p-1 transition-all group-hover:visible group-hover:scale-100 sm:ml-0 sm:flex lg:left-[36px]"
className="bg-default text-muted hover:text-emphasis border-default hover:border-emphasis invisible absolute left-[5px] -ml-4 -mt-4 mb-4 hidden h-6 w-6 scale-0 items-center justify-center rounded-md border p-1 transition-all group-hover:visible group-hover:scale-100 sm:ml-0 sm:flex lg:left-[36px]"
onClick={() => moveEventType(index, -1)}>
<ArrowUp className="h-5 w-5" />
</button>
@@ -391,7 +391,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
{!(lastItem && lastItem.id === type.id) && (
<button
className="bg-default text-muted border-default hover:text-emphasis hover:border-emphasis invisible absolute left-[5px] mt-8 -ml-4 hidden h-6 w-6 scale-0 items-center justify-center rounded-md border p-1 transition-all group-hover:visible group-hover:scale-100 sm:ml-0 sm:flex lg:left-[36px]"
className="bg-default text-muted border-default hover:text-emphasis hover:border-emphasis invisible absolute left-[5px] -ml-4 mt-8 hidden h-6 w-6 scale-0 items-center justify-center rounded-md border p-1 transition-all group-hover:visible group-hover:scale-100 sm:ml-0 sm:flex lg:left-[36px]"
onClick={() => moveEventType(index, 1)}>
<ArrowDown className="h-5 w-5" />
</button>
@@ -401,7 +401,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
<div className="flex justify-between space-x-2 rtl:space-x-reverse">
{type.team && !isManagedEventType && (
<AvatarGroup
className="relative top-1 right-3"
className="relative right-3 top-1"
size="sm"
truncateAfter={4}
items={type.users.map(
@@ -415,12 +415,12 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
)}
{isManagedEventType && (
<AvatarGroup
className="relative top-1 right-3"
className="relative right-3 top-1"
size="sm"
truncateAfter={4}
items={type.children
.flatMap((ch) => ch.users)
.map((user: User) => ({
.map((user: Pick<User, "name" | "username">) => ({
alt: user.name || "",
image: `${WEBAPP_URL}/${user.username}/avatar.png`,
title: user.name || "",
@@ -640,7 +640,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
)}
<DropdownMenuSeparator />
{!isManagedEventType && (
<div className="hover:bg-subtle flex h-9 cursor-pointer flex-row items-center justify-between py-2 px-4">
<div className="hover:bg-subtle flex h-9 cursor-pointer flex-row items-center justify-between px-4 py-2">
<Skeleton
as={Label}
htmlFor="hiddenSwitch"
@@ -728,10 +728,10 @@ const EventTypeListHeading = ({
{profile?.name || ""}
</Link>
{membershipCount && teamId && (
<span className="text-subtle ms-2 me-2 relative -top-px text-xs">
<span className="text-subtle relative -top-px me-2 ms-2 text-xs">
<Link href={`/settings/teams/${teamId}/members`}>
<Badge variant="gray">
<Users className="mr-1 -mt-px inline h-3 w-3" />
<Users className="-mt-px mr-1 inline h-3 w-3" />
{membershipCount}
</Badge>
</Link>
@@ -747,7 +747,7 @@ const EventTypeListHeading = ({
</div>
{!profile?.slug && !!teamId && (
<button onClick={() => publishTeamMutation.mutate({ teamId })}>
<Badge variant="gray" className="mb-1 -ml-2">
<Badge variant="gray" className="-ml-2 mb-1">
{t("upgrade")}
</Badge>
</button>
@@ -799,9 +799,8 @@ const CTA = () => {
const Actions = () => {
return (
<div className="flex items-center">
<OrganizationEventTypeFilter />
<VerticalDivider />
<div className="hidden items-center md:flex">
<TeamsFilter popoverTriggerClassNames="mb-0" showVerticalDivider={true} />
</div>
);
};
@@ -834,9 +833,6 @@ const SetupProfileBanner = ({ closeAction }: { closeAction: () => void }) => {
);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const WithQuery = withQuery(trpc.viewer.eventTypes.getByViewer as any);
const EventTypesPage = () => {
const { t } = useLocale();
const router = useRouter();
@@ -862,6 +858,10 @@ const EventTypesPage = () => {
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const filters = getTeamsFiltersFromQuery(router.query);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const WithQuery = withQuery(trpc.viewer.eventTypes.getByViewer as any, filters && { filters });
return (
<div>
@@ -879,47 +879,55 @@ const EventTypesPage = () => {
CTA={<CTA />}>
<WithQuery
customLoader={<SkeletonLoader />}
success={({ data }) => (
<>
{data.eventTypeGroups.length > 1 ? (
<>
{isMobile ? (
<MobileTeamsTab eventTypeGroups={data.eventTypeGroups} />
) : (
data.eventTypeGroups.map((group: EventTypeGroup, index: number) => (
<div className="flex flex-col" key={group.profile.slug}>
<EventTypeListHeading
profile={group.profile}
membershipCount={group.metadata.membershipCount}
teamId={group.teamId}
orgSlug={orgBranding?.slug}
/>
success={({ data }) => {
const isFilteredByOnlyOneItem =
(filters?.teamIds?.length === 1 || filters?.userIds?.length === 1) &&
data.eventTypeGroups.length === 1;
<EventTypeList
types={group.eventTypes}
group={group}
groupIndex={index}
readOnly={group.metadata.readOnly}
/>
</div>
))
)}
</>
) : data.eventTypeGroups.length === 1 ? (
<EventTypeList
types={data.eventTypeGroups[0].eventTypes}
group={data.eventTypeGroups[0]}
groupIndex={0}
readOnly={data.eventTypeGroups[0].metadata.readOnly}
/>
) : (
<CreateFirstEventTypeView />
)}
return (
<>
{data.eventTypeGroups.length > 1 || isFilteredByOnlyOneItem ? (
<>
{isMobile ? (
<MobileTeamsTab eventTypeGroups={data.eventTypeGroups} />
) : (
data.eventTypeGroups.map((group: EventTypeGroup, index: number) => (
<div className="flex flex-col" key={group.profile.slug}>
<EventTypeListHeading
profile={group.profile}
membershipCount={group.metadata.membershipCount}
teamId={group.teamId}
orgSlug={orgBranding?.slug}
/>
<EmbedDialog />
{router.query.dialog === "duplicate" && <DuplicateDialog />}
</>
)}
<EventTypeList
types={group.eventTypes}
group={group}
groupIndex={index}
readOnly={group.metadata.readOnly}
/>
</div>
))
)}
</>
) : (
data.eventTypeGroups.length === 1 && (
<EventTypeList
types={data.eventTypeGroups[0].eventTypes}
group={data.eventTypeGroups[0]}
groupIndex={0}
readOnly={data.eventTypeGroups[0].metadata.readOnly}
/>
)
)}
{data.eventTypeGroups.length === 0 && <CreateFirstEventTypeView />}
<EmbedDialog />
{router.query.dialog === "duplicate" && <DuplicateDialog />}
</>
);
}}
/>
</Shell>
</div>
+1 -1
View File
@@ -77,7 +77,7 @@ export default function InsightsPage() {
<></>
) : (
<FiltersProvider>
<div className="mb-8 mt-0 ml-auto flex w-full flex-wrap justify-between md:-mt-8">
<div className="mb-8 ml-auto mt-0 flex w-full flex-wrap justify-between md:-mt-8">
<Heading />
<Filters />
</div>
+1 -1
View File
@@ -19,7 +19,7 @@ export default function MaintenancePage() {
</Head>
<div className="bg-default m-auto rounded-md p-10 text-right ltr:text-left">
<h1 className="text-emphasis text-2xl font-medium">{t("under_maintenance")}</h1>
<p className="text-default mt-4 mb-6 max-w-2xl text-sm">
<p className="text-default mb-6 mt-4 max-w-2xl text-sm">
{t("under_maintenance_description", { appName: APP_NAME })}
</p>
<Button href={`${WEBSITE_URL}/support`}>{t("contact_support")}</Button>
+2 -1
View File
@@ -101,7 +101,8 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
const ssr = await ssrInit(context);
const user = await prisma.user.findFirst({
where: {
username,
/** TODO: We should standarize this */
username: username.toLowerCase().replace(/( |%20)/g, "+"),
organization: isValidOrgDomain
? {
slug: currentOrgDomain,
+13 -2
View File
@@ -14,7 +14,7 @@ import PageWrapper from "@components/PageWrapper";
type PageProps = inferSSRProps<typeof getServerSideProps>;
export default function Type({ slug, user, booking, away, isBrandingHidden }: PageProps) {
export default function Type({ slug, user, booking, away, isBrandingHidden, isTeamEvent }: PageProps) {
return (
<main className="flex h-full min-h-[100dvh] items-center justify-center">
<BookerSeo
@@ -29,6 +29,7 @@ export default function Type({ slug, user, booking, away, isBrandingHidden }: Pa
rescheduleBooking={booking}
isAway={away}
hideBranding={isBrandingHidden}
isTeamEvent={isTeamEvent}
/>
</main>
);
@@ -57,6 +58,11 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
username: true,
},
},
team: {
select: {
id: true,
},
},
},
},
},
@@ -96,9 +102,11 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
booking = await getBookingByUidOrRescheduleUid(`${rescheduleUid}`);
}
const isTeamEvent = !!hashedLink.eventType?.team?.id;
// We use this to both prefetch the query on the server,
// as well as to check if the event exist, so we c an show a 404 otherwise.
const eventData = await ssr.viewer.public.event.fetch({ username, eventSlug: slug });
const eventData = await ssr.viewer.public.event.fetch({ username, eventSlug: slug, isTeamEvent });
if (!eventData) {
return {
@@ -114,6 +122,9 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
slug,
trpcState: ssr.dehydrate(),
isBrandingHidden: user?.hideBranding,
// Sending the team event from the server, because this template file
// is reused for both team and user events.
isTeamEvent,
},
};
}
@@ -30,6 +30,7 @@ export default function Type({ slug, user, booking, away, isBrandingHidden }: Pa
rescheduleBooking={booking}
isAway={away}
hideBranding={isBrandingHidden}
isTeamEvent
/>
</main>
);
@@ -71,7 +72,11 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
// We use this to both prefetch the query on the server,
// as well as to check if the event exist, so we c an show a 404 otherwise.
const eventData = await ssr.viewer.public.event.fetch({ username: teamSlug, eventSlug: meetingSlug });
const eventData = await ssr.viewer.public.event.fetch({
username: teamSlug,
eventSlug: meetingSlug,
isTeamEvent: true,
});
if (!eventData) {
return {
@@ -0,0 +1 @@
export { getServerSideProps, default } from "../../../[user]/[type]";
@@ -0,0 +1,40 @@
import type { GetServerSidePropsContext } from "next";
import prisma from "@calcom/prisma";
import PageWrapper from "@components/PageWrapper";
import type { UserPageProps } from "../../../[user]";
import UserPage, { getServerSideProps as GSSUserPage } from "../../../[user]";
import type { TeamPageProps } from "../../../team/[slug]";
import TeamPage, { getServerSideProps as GSSTeamPage } from "../../../team/[slug]";
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
const team = await prisma.team.findFirst({
where: {
slug: ctx.query.user as string,
parentId: {
not: null,
},
parent: {
slug: ctx.query.orgSlug as string,
},
},
select: {
id: true,
},
});
if (team) {
return GSSTeamPage({ ...ctx, query: { slug: ctx.query.user } });
}
return GSSUserPage({ ...ctx, query: { user: ctx.query.user } });
};
type Props = UserPageProps | TeamPageProps;
export default function Page(props: Props) {
if ((props as TeamPageProps)?.team) return TeamPage(props as TeamPageProps);
return UserPage(props as UserPageProps);
}
Page.PageWrapper = PageWrapper;
+1
View File
@@ -0,0 +1 @@
export { getServerSideProps, default } from "../../team/[slug]";
@@ -0,0 +1 @@
export { getServerSideProps, default } from "../../../../team/[slug]/[type]";
@@ -0,0 +1 @@
export { getServerSideProps, default } from "../../../../team/[slug]";
+1 -1
View File
@@ -25,7 +25,7 @@ const CtaRow = ({ title, description, className, children }: CtaRowProps) => {
<h2 className="font-medium">{title}</h2>
<p>{description}</p>
</div>
<div className="flex-shrink-0 pt-3 sm:ml-auto sm:pt-0 sm:pl-3">{children}</div>
<div className="flex-shrink-0 pt-3 sm:ml-auto sm:pl-3 sm:pt-0">{children}</div>
</section>
<hr className="border-subtle" />
</>
@@ -57,7 +57,7 @@ const ApiKeysView = () => {
<div>
{isLoading ? null : data?.length ? (
<>
<div className="mt-6 mb-8 rounded-md border">
<div className="mb-8 mt-6 rounded-md border">
{data.map((apiKey, index) => (
<ApiKeyListItem
key={apiKey.id}
@@ -32,7 +32,7 @@ const SkeletonLoader = ({ title, description }: { title: string; description: st
return (
<SkeletonContainer>
<Meta title={title} description={description} />
<div className="mt-6 mb-8 space-y-6">
<div className="mb-8 mt-6 space-y-6">
<div className="flex items-center">
<SkeletonButton className="mr-6 h-32 w-48 rounded-md p-5" />
<SkeletonButton className="mr-6 h-32 w-48 rounded-md p-5" />
@@ -70,6 +70,13 @@ const AppearanceView = () => {
},
});
const selectedTheme = formMethods.watch("theme");
const selectedThemeIsDark =
selectedTheme === "dark" ||
(selectedTheme === "" &&
typeof document !== "undefined" &&
document.documentElement.classList.contains("dark"));
const {
formState: { isSubmitting, isDirty },
reset,
@@ -144,6 +151,7 @@ const AppearanceView = () => {
<hr className="border-subtle my-8 border [&:has(+hr)]:hidden" />
<BookerLayoutSelector
isDark={selectedThemeIsDark}
name="metadata.defaultBookerLayouts"
title={t("bookerlayout_user_settings_title")}
description={t("bookerlayout_user_settings_description")}
@@ -34,7 +34,7 @@ import PageWrapper from "@components/PageWrapper";
const SkeletonLoader = () => {
return (
<SkeletonContainer>
<div className="mt-6 mb-8 space-y-6">
<div className="mb-8 mt-6 space-y-6">
<SkeletonText className="h-8 w-full" />
<SkeletonText className="h-8 w-full" />
<SkeletonText className="h-8 w-full" />
@@ -33,7 +33,7 @@ const SkeletonLoader = ({ title, description }: { title: string; description: st
return (
<SkeletonContainer>
<Meta title={title} description={description} />
<div className="divide-subtle mt-6 mb-8 space-y-6">
<div className="divide-subtle mb-8 mt-6 space-y-6">
<SkeletonText className="h-8 w-full" />
<SkeletonText className="h-8 w-full" />
</div>
@@ -29,7 +29,7 @@ const SkeletonLoader = ({ title, description }: { title: string; description: st
return (
<SkeletonContainer>
<Meta title={title} description={description} />
<div className="mt-6 mb-8 space-y-6">
<div className="mb-8 mt-6 space-y-6">
<SkeletonText className="h-8 w-full" />
<SkeletonText className="h-8 w-full" />
<SkeletonText className="h-8 w-full" />
@@ -48,9 +48,9 @@ const SkeletonLoader = ({ title, description }: { title: string; description: st
return (
<SkeletonContainer>
<Meta title={title} description={description} />
<div className="mt-6 mb-8 space-y-6">
<div className="mb-8 mt-6 space-y-6">
<div className="flex items-center">
<SkeletonAvatar className="h-12 w-12 px-4" />
<SkeletonAvatar className="me-4 h-16 w-16 px-4" />
<SkeletonButton className="h-6 w-32 rounded-md p-5" />
</div>
<SkeletonText className="h-8 w-full" />
@@ -12,7 +12,7 @@ import EnableTwoFactorModal from "@components/settings/EnableTwoFactorModal";
const SkeletonLoader = () => {
return (
<SkeletonContainer>
<div className="mt-6 mb-8 space-y-6">
<div className="mb-8 mt-6 space-y-6">
<div className="flex items-center">
<SkeletonButton className="mr-6 h-8 w-20 rounded-md p-5" />
<SkeletonText className="h-8 w-full" />
+2 -1
View File
@@ -29,6 +29,7 @@ import Team from "@components/team/screens/Team";
import { ssrInit } from "@server/lib/ssr";
export type TeamPageProps = inferSSRProps<typeof getServerSideProps>;
function TeamPage({ team, isUnpublished, markdownStrippedBio, isValidOrgDomain }: TeamPageProps) {
useTheme(team.theme);
const showMembers = useToggleQuery("members");
@@ -170,7 +171,7 @@ function TeamPage({ team, isUnpublished, markdownStrippedBio, isValidOrgDomain }
profile: { name: `${team.name}`, image: getPlaceholderAvatar(team.logo, team.name) },
}}
/>
<main className="dark:bg-darkgray-50 bg-subtle mx-auto max-w-3xl rounded-md px-4 pt-12 pb-12">
<main className="dark:bg-darkgray-50 bg-subtle mx-auto max-w-3xl rounded-md px-4 pb-12 pt-12">
<div className="mx-auto mb-8 max-w-3xl text-center">
<div className="relative">
<Avatar
+1 -1
View File
@@ -205,7 +205,7 @@ export function VideoMeetingInfo(props: VideoMeetingInfo) {
<>
<aside
className={classNames(
"no-scrollbar fixed top-0 left-0 z-30 flex h-full w-64 transform justify-between overflow-x-hidden overflow-y-scroll transition-all duration-300 ease-in-out",
"no-scrollbar fixed left-0 top-0 z-30 flex h-full w-64 transform justify-between overflow-x-hidden overflow-y-scroll transition-all duration-300 ease-in-out",
open ? "translate-x-0" : "-translate-x-[232px]"
)}>
<main className="prose-sm prose max-w-64 prose-a:text-white prose-h3:text-white prose-h3:font-cal scroll-bar scrollbar-track-w-20 w-full overflow-scroll overflow-x-hidden border-r border-gray-300/20 bg-black/80 p-4 text-white shadow-sm backdrop-blur-lg">
+4 -4
View File
@@ -19,13 +19,13 @@ export default function MeetingUnavailable(props: inferSSRProps<typeof getServer
<HeadSeo title="Meeting Unavailable" description="Meeting Unavailable" />
<main className="mx-auto my-24 max-w-3xl">
<div className="fixed inset-0 z-50 overflow-y-auto">
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
<div className="flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0">
<div className="fixed inset-0 my-4 transition-opacity sm:my-0" aria-hidden="true">
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">
&#8203;
</span>
<div
className="bg-default inline-block transform overflow-hidden rounded-lg px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6 sm:align-middle"
className="bg-default inline-block transform overflow-hidden rounded-lg px-4 pb-4 pt-5 text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6 sm:align-middle"
role="dialog"
aria-modal="true"
aria-labelledby="modal-headline">
@@ -38,12 +38,12 @@ export default function MeetingUnavailable(props: inferSSRProps<typeof getServer
This meeting is in the past.
</h3>
</div>
<div className="mt-4 border-t border-b py-4">
<div className="mt-4 border-b border-t py-4">
<h2 className="font-cal text-default mb-2 text-center text-lg font-medium">
{props.booking.title}
</h2>
<p className="text-subtle text-center">
<Calendar className="mr-1 -mt-1 inline-block h-4 w-4" />
<Calendar className="-mt-1 mr-1 inline-block h-4 w-4" />
{dayjs(props.booking.startTime).format(detectBrowserTimeFormat + ", dddd DD MMMM YYYY")}
</p>
</div>
@@ -23,7 +23,7 @@ export default function MeetingNotStarted(props: inferSSRProps<typeof getServerS
<>
<h2 className="mb-2 text-center font-medium">{props.booking.title}</h2>
<p className="text-subtle text-center">
<Calendar className="mr-1 -mt-1 inline-block h-4 w-4" />
<Calendar className="-mt-1 mr-1 inline-block h-4 w-4" />
{dayjs(props.booking.startTime).format(detectBrowserTimeFormat + ", dddd DD MMMM YYYY")}
</p>
</>
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

+1 -16
View File
@@ -12,10 +12,8 @@
"have_any_questions": "هل لديك أسئلة؟ نحن هنا للمساعدة.",
"reset_password_subject": "{{appName}}: إرشادات إعادة تعيين كلمة المرور",
"event_declined_subject": "تم الرفض: {{title}} في {{date}}",
"event_cancelled_subject": "تم الإلغاء: {{title}} في {{date}}",
"event_request_declined": "تم رفض طلب الحدث الخاص بك",
"event_request_declined_recurring": "تم رفض طلب الحدث المتكرر الخاص بك",
"event_request_cancelled": "تم إلغاء حدثك المُجَدْوَل",
"organizer": "المنظِّم",
"need_to_reschedule_or_cancel": "هل تحتاج إلى إعادة جدولة أو إلغاء؟",
"cancellation_reason": "سبب الإلغاء (اختياري)",
@@ -77,9 +75,7 @@
"your_meeting_has_been_booked": "لقد تم حجز الاجتماع الخاص بك",
"event_type_has_been_rescheduled_on_time_date": "تم إعادة جدولة {{title}} إلى {{date}}.",
"event_has_been_rescheduled": "تم التحديث - تم إعادة جدولة الحدث الخاص بك",
"request_reschedule_subtitle": "ألغى {{organizer}} الحجز وطلب منك اختيار حجز في وقت آخر.",
"request_reschedule_title_organizer": "لقد طلبت من {{attendee}} إعادة جدولة موعد",
"request_reschedule_subtitle_organizer": "لقد ألغيت الحجز ويجب أن يختار {{attendee}} وقتًا جديداً للحجز معك.",
"rescheduled_event_type_subject": "تم إرسال طلب لإعادة الجدولة: {{eventType}} مع {{name}} في {{date}}",
"requested_to_reschedule_subject_attendee": "الإجراء المطلوب إعادة الجدولة: يُرجى حجز وقت جديد لمدة {{eventType}} مع {{name}}",
"hi_user_name": "مرحبا {{name}}",
@@ -303,7 +299,6 @@
"error_end_time_next_day": "لا يمكن أن يكون وقت الانتهاء أطول من 24 ساعة",
"back_to_bookings": "العودة إلى عمليات الحجز",
"free_to_pick_another_event_type": "لا تتردد في اختيار حدث آخر في أي وقت.",
"cancelled": "تم الإلغاء",
"cancellation_successful": "تم الإلغاء بنجاح",
"really_cancel_booking": "هل تريد حقًا إلغاء الحجز؟",
"cannot_cancel_booking": "لا يمكنك إلغاء هذا الحجز",
@@ -312,7 +307,6 @@
"cancelling_event_recurring": "إن هذا الحدث أحد الأمثلة على حدث متكرر.",
"cancelling_all_recurring": "هذه هي كل الأمثلة المتبقية في الحدث المتكرر.",
"error_with_status_code_occured": "حدث خطأ برمز الحالة {{status}}.",
"booking_already_cancelled": "تم إلغاء هذا الحجز بالفعل",
"booking_already_accepted_rejected": "تم بالفعل قبول هذا الحجز أو رفضه",
"go_back_home": "العودة إلى الشاشة الرئيسية",
"or_go_back_home": "أو العودة إلى الشاشة الرئيسية",
@@ -326,7 +320,6 @@
"upcoming_bookings": "بمجرد أن يحجز شخص ما موعدًا معك، سيظهر هنا.",
"recurring_bookings": "بمجرد أن يحجز شخص ما اجتماعًا متكررًا معك، سيظهر هنا.",
"past_bookings": "ستظهر هنا عمليات حجزك السابقة.",
"cancelled_bookings": "ستظهر هنا عمليات حجزك التي تم إلغاؤها.",
"unconfirmed_bookings": "ستظهر هنا كل حجوزات غير المؤكدة بعد.",
"unconfirmed_bookings_tooltip": "الحجوزات غير المؤكدة",
"on": "في",
@@ -366,7 +359,6 @@
"browse_api_documentation": "استعراض مستندات واجهة برمجة التطبيقات (API) لدينا",
"leverage_our_api": "استفد من واجهة برمجة التطبيقات (API) لدينا من أجل قدرة كاملة على التحكم والتخصيص.",
"create_webhook": "إنشاء الويب هوك",
"booking_cancelled": "تم إلغاء الحجز",
"booking_rescheduled": "تمت إعادة جدولة الحجز",
"recording_ready": "رابط تنزيل التسجيل جاهز",
"booking_created": "تم إنشاء الحجز",
@@ -379,8 +371,6 @@
"team_webhooks": "فريق ويبهوكس",
"create_new_webhook_to_account": "إنشاء ويب هوك جديد لحسابك",
"new_webhook": "ويب هوك جديد",
"receive_cal_meeting_data": "تلقَّ بيانات اجتماعات {{appName}} على عنوان URL محدد، وفي الوقت الفعلي، عندما تتم جدولة حدث ما أو يتم إلغاؤه.",
"receive_cal_event_meeting_data": "تلقي بيانات اجتماعات {{appName}} على عنوان URL محدد، وفي الوقت الحقيقي، عندما تتم جدولة حدث ما أو إلغاؤه.",
"responsive_fullscreen_iframe": "إطار iframe سريع الاستجابة بكامل الشاشة",
"loading": "يجري التحميل...",
"deleting": "جارٍ الحذف...",
@@ -1018,14 +1008,12 @@
"event_location": "موقع الحدث",
"reschedule_optional": "سبب إعادة الجدولة (اختياري)",
"reschedule_placeholder": "أخبر الآخرين لماذا تحتاج إلى إعادة جدولة",
"event_cancelled": "تم إلغاء هذا الحدث",
"emailed_information_about_cancelled_event": "لقد أرسلنا إليك وإلى الحاضرين الآخرين بريداً إلكترونياً لإعلامهم.",
"this_input_will_shown_booking_this_event": "سيُعرَض هذا المُدخَل عند حجز هذا الحدث",
"meeting_url_in_conformation_email": "عنوان رابط الاجتماع موجود في رسالة التأكيد الإلكترونية",
"url_start_with_https": "يجب أن يبدأ العنوان بـ http:// أو https://",
"number_provided": "سيتم توفير رقم الهاتف",
"before_event_trigger": "قبل بدء الحدث",
"event_cancelled_trigger": "متى تم إلغاء هذا الحدث",
"new_event_trigger": "متى تم حجز الحدث الجديد",
"email_host_action": "إرسال رسالة إلكترونية إلى المضيف",
"email_attendee_action": "إرسال رسالة إلكترونية إلى الحضور",
@@ -1420,12 +1408,11 @@
"team_url_required": "يجب إدخال عنوان URL للفريق",
"team_url_taken": "عنوان URL هذا مأخوذ بالفعل",
"team_publish": "نشر فريق",
"number_sms_notifications": "رقم الهاتف (إشعارات الرسائل النصية)",
"attendee_email_variable": "اسم الحاضر",
"attendee_email_info": "البريد الإلكتروني للشخص الحجز",
"kbar_search_placeholder": "اكتب أمرًا أو بحثًا...",
"invalid_credential": "لا! يبدو أن الصلاحية انتهت أو تم إلغاؤها. يرجى إعادة التثبيت مرة أخرى.",
"reschedule_reason_success_page": "سبب إعادة الجدولة",
"reschedule_reason": "سبب إعادة الجدولة",
"choose_common_schedule_team_event": "اختر جدولًا زمنيًا مشتركًا",
"choose_common_schedule_team_event_description": "قم بتمكين هذا الخيار إذا كنت ترغب في استخدام جدول زمني مشترك بين المضيفين. عند تعطيل هذا الخيار، سيتم حجز كل مضيف على أساس جدوله الافتراضي.",
"reason": "السبب",
@@ -1601,7 +1588,6 @@
"no_longer_attending": "لن تحضر هذا الحدث",
"attendee_no_longer_attending_subject": "أحد الحضور لن يحضر {{title}} في {{date}}",
"attendee_no_longer_attending": "أحد الحضور لن يحضر الحدث الخاص بك",
"attendee_no_longer_attending_subtitle": "تم إلغاء الحدث {{name}}، وهو ما يعني توافر معقد شاغر في هذا التوقيت",
"create_event_on": "إنشاء حدث في",
"default_app_link_title": "تعيين رابط تطبيق افتراضي",
"default_app_link_description": "يسمح تعيين رابط التطبيق الافتراضي لجميع أنواع الأحداث التي تم إنشاؤها حديثًا باستخدام رابط التطبيق الذي عينته.",
@@ -1712,7 +1698,6 @@
"least_booked_members": "الأعضاء الأقل حجزًا",
"events_created": "تم إنشاء الأحداث",
"events_completed": "تم اكتمال الأحداث",
"events_cancelled": "تم إلغاء الأحداث",
"events_rescheduled": "تمت إعادة جدولة الأحداث",
"from_last_period": "من آخر فترة",
"from_to_date_period": "من: {{startDate}} إلى: {{endDate}}",
+1 -16
View File
@@ -12,10 +12,8 @@
"have_any_questions": "Máte otázky? Jsme tu, abychom vám pomohli.",
"reset_password_subject": "{{appName}}: Pokyny pro obnovení hesla",
"event_declined_subject": "Odmítnuto: {{title}} v {{date}}",
"event_cancelled_subject": "Zrušeno: {{title}} v {{date}}",
"event_request_declined": "Žádost o událost byla zamítnuta",
"event_request_declined_recurring": "Žádost o opakovanou událost byla zamítnuta",
"event_request_cancelled": "Vaše naplánovaná událost byla zrušena",
"organizer": "Organizátor",
"need_to_reschedule_or_cancel": "Přeplánovat nebo zrušit?",
"cancellation_reason": "Důvod zrušení",
@@ -77,9 +75,7 @@
"your_meeting_has_been_booked": "Vaše schůzka byla rezervována",
"event_type_has_been_rescheduled_on_time_date": "Váš {{title}} byl přeplánován na {{date}}.",
"event_has_been_rescheduled": "Změna - Vaše událost byla přesunuta na jindy",
"request_reschedule_subtitle": "Organizátor {{organizer}} zrušil rezervaci a požádal vás, abyste si vybrali jiný čas.",
"request_reschedule_title_organizer": "Požádali jste uživatele {{attendee}} o změnu časového plánu",
"request_reschedule_subtitle_organizer": "Zrušili jste rezervaci a uživatel {{attendee}} by si s vámi měl zarezervovat nový čas.",
"rescheduled_event_type_subject": "Žádost o přesunutí schůzky: {{eventType}} s {{name}} dne {{date}}",
"requested_to_reschedule_subject_attendee": "Akce vyžadovaná k přeplánování: zarezervujte si nový čas pro událost {{eventType}} s osobou {{name}}",
"hi_user_name": "Hezký den, {{name}}",
@@ -303,7 +299,6 @@
"error_end_time_next_day": "Čas konce nemůže být delší než 24 hodin",
"back_to_bookings": "Zpět na rezervace",
"free_to_pick_another_event_type": "Kdykoliv si můžete vybrat jinou událost.",
"cancelled": "Zrušeno",
"cancellation_successful": "Zrušení proběhlo úspěšně",
"really_cancel_booking": "Opravdu chcete zrušit svou rezervaci?",
"cannot_cancel_booking": "Tuto rezervaci nelze zrušit",
@@ -312,7 +307,6 @@
"cancelling_event_recurring": "Tato událost je jedním z případů opakující se události.",
"cancelling_all_recurring": "Toto jsou všechny zbývající instance opakující se události.",
"error_with_status_code_occured": "Došlo k chybě se stavovým kódem {{status}}.",
"booking_already_cancelled": "Tato rezervace již byla zrušena",
"booking_already_accepted_rejected": "Tato rezervace již byla přijata nebo zamítnuta",
"go_back_home": "Přejít na úvod",
"or_go_back_home": "Nebo přejít na úvod",
@@ -326,7 +320,6 @@
"upcoming_bookings": "Jakmile si někdo rezervuje váš čas, zobrazí se to zde.",
"recurring_bookings": "Jakmile si někdo s vámi zarezervuje opakovanou schůzku, zobrazí se to zde.",
"past_bookings": "Vaše proběhlé rezervace se zobrazí zde.",
"cancelled_bookings": "Vaše zrušené rezervace se zobrazí zde.",
"unconfirmed_bookings": "Vaše nepotvrzené rezervace se zobrazí zde.",
"unconfirmed_bookings_tooltip": "Nepotvrzené rezervace",
"on": "dne",
@@ -366,7 +359,6 @@
"browse_api_documentation": "Procházejte naši API dokumentaci",
"leverage_our_api": "Pro plnou kontrolu a přizpůsobení využijte naše API.",
"create_webhook": "Vytvořit webhook",
"booking_cancelled": "Rezervace zrušena",
"booking_rescheduled": "Rezervace přesunuta",
"recording_ready": "Odkaz ke stažení záznamu je připraven",
"booking_created": "Rezervace vytvořena",
@@ -379,8 +371,6 @@
"team_webhooks": "Týmové Webhooky",
"create_new_webhook_to_account": "Vytvořit nový webhook pro váš účet",
"new_webhook": "Nový webhook",
"receive_cal_meeting_data": "Přijímat v reálném čase data schůzky na zadanou adresu URL, když je událost naplánována nebo zrušena.",
"receive_cal_event_meeting_data": "Přijmout real-time data o {{appName}} události na určené URL adrese, jakmile je událost naplánována nebo zrušena.",
"responsive_fullscreen_iframe": "Responzivní iframe na celou obrazovku",
"loading": "Načítám...",
"deleting": "Odstraňování...",
@@ -1018,14 +1008,12 @@
"event_location": "Místo události",
"reschedule_optional": "Důvod pro změnu plánu (volitelné)",
"reschedule_placeholder": "Dejte ostatním vědět, proč potřebujete změnit plán",
"event_cancelled": "Tato událost je zrušena",
"emailed_information_about_cancelled_event": "Poslali jsme vám a ostatním účastníkům informační e-mail.",
"this_input_will_shown_booking_this_event": "Toto pole se zobrazí při rezervaci této události",
"meeting_url_in_conformation_email": "Adresa URL schůzky je v potvrzovacím e-mailu",
"url_start_with_https": "Adresa URL musí začínat http:// nebo https://",
"number_provided": "Bude uvedeno telefonní číslo",
"before_event_trigger": "před zahájením události",
"event_cancelled_trigger": "při zrušení události",
"new_event_trigger": "při rezervaci nové události",
"email_host_action": "odeslání e-mailu hostiteli",
"email_attendee_action": "odeslat e-mail účastníkům",
@@ -1420,12 +1408,11 @@
"team_url_required": "Musíte zadat adresu URL týmu",
"team_url_taken": "Tato adresa URL je už obsazená",
"team_publish": "Zveřejnit tým",
"number_sms_notifications": "Číslo telefonu (SMS oznámení)",
"attendee_email_variable": "E-mail účastníka",
"attendee_email_info": "E-mail osoby provádějící rezervaci",
"kbar_search_placeholder": "Zadejte příkaz nebo vyhledejte...",
"invalid_credential": "Ale ne! Zdá se, že platnost oprávnění vypršela nebo byla zrušena. Proveďte novou instalaci.",
"reschedule_reason_success_page": "Důvod změny termínu",
"reschedule_reason": "Důvod změny termínu",
"choose_common_schedule_team_event": "Vyberte společný plán",
"choose_common_schedule_team_event_description": "Tuto možnost povolte, pokud chcete používat společný plán mezi hostiteli. Při zakázání této možnosti bude každý hostitel rezervován na základě svého výchozího plánu.",
"reason": "Důvod",
@@ -1601,7 +1588,6 @@
"no_longer_attending": "Této události se již neúčastníte",
"attendee_no_longer_attending_subject": "Účastník se již neúčastní události {{title}} v den {{date}}",
"attendee_no_longer_attending": "Účastník se již neúčastní vaší události",
"attendee_no_longer_attending_subtitle": "Uživatel {{name}} zrušil svou účast. V tomto časovém intervalu se tedy uvolnilo místo",
"create_event_on": "Vytvořit událost v:",
"default_app_link_title": "Nastavte výchozí odkaz na aplikaci",
"default_app_link_description": "Nastavení výchozího odkazu na aplikaci umožní, aby všechny nově vytvořené typy událostí používaly vámi nastavený odkaz na aplikaci.",
@@ -1712,7 +1698,6 @@
"least_booked_members": "Nejméně rezervovaných členů",
"events_created": "Události vytvořeny",
"events_completed": "Události dokončeny",
"events_cancelled": "Události zrušeny",
"events_rescheduled": "Události přesunuty",
"from_last_period": "z posledního období",
"from_to_date_period": "Od: {{startDate}} do: {{endDate}}",
+1 -14
View File
@@ -11,10 +11,8 @@
"have_any_questions": "Har du spørgsmål? Vi er her for at hjælpe.",
"reset_password_subject": "{{appName}}: Nulstil adgangskodeinstruktioner",
"event_declined_subject": "Afvist: {{title}} den {{date}}",
"event_cancelled_subject": "Aflyst: {{title}} den {{date}}",
"event_request_declined": "Din begivenhedsanmodning er blevet afvist",
"event_request_declined_recurring": "Din tilbagevendende begivenhedsanmodning er blevet afvist",
"event_request_cancelled": "Din planlagte begivenhed blev aflyst",
"organizer": "Arrangør",
"need_to_reschedule_or_cancel": "Behov for at omlægge eller annullere?",
"cancellation_reason": "Årsag til aflysning (valgfri)",
@@ -72,9 +70,7 @@
"your_meeting_has_been_booked": "Dit møde er blevet booket",
"event_type_has_been_rescheduled_on_time_date": "Din {{title}} er blevet flyttet til {{date}}.",
"event_has_been_rescheduled": "Opdateret - Din begivenhed er blevet flyttet",
"request_reschedule_subtitle": "{{organizer}} har annulleret bookingen og anmodet dig om at vælge et andet tidspunkt.",
"request_reschedule_title_organizer": "Du har anmodet {{attendee}} om at omlægge",
"request_reschedule_subtitle_organizer": "Du har annulleret bookingen og {{attendee}} bør vælge en ny bookingtid med dig.",
"rescheduled_event_type_subject": "Anmodning om omlægning sendt: {{eventType}} med {{name}} den {{date}}",
"requested_to_reschedule_subject_attendee": "Handling Påkrævet Omlægning: Reservér en ny tid for {{eventType}} med {{name}}",
"hi_user_name": "Hej {{name}}",
@@ -292,7 +288,6 @@
"error_end_time_next_day": "Sluttidspunktet kan ikke være længere end 24 timer",
"back_to_bookings": "Tilbage til bookinger",
"free_to_pick_another_event_type": "Du er til enhver tid velkommen til at vælge en anden begivenhed.",
"cancelled": "Annullerede",
"cancellation_successful": "Annullering lykkedes",
"really_cancel_booking": "Vil du virkelig annullere din booking?",
"cannot_cancel_booking": "Du kan ikke annullere denne booking",
@@ -301,7 +296,6 @@
"cancelling_event_recurring": "Begivenheden er en forekomst af en tilbagevendende begivenhed.",
"cancelling_all_recurring": "Disse er alle resterende forekomster i den tilbagevendende begivenhed.",
"error_with_status_code_occured": "Der opstod en fejl med statuskoden {{status}}.",
"booking_already_cancelled": "Denne booking er allerede aflyst",
"booking_already_accepted_rejected": "Denne booking er allerede accepteret eller afvist",
"go_back_home": "Gå tilbage til hjem",
"or_go_back_home": "Eller gå tilbage til hjem",
@@ -315,7 +309,6 @@
"upcoming_bookings": "Så snart nogen booker en tid med dig, vil det dukke op her.",
"recurring_bookings": "Så snart nogen booker et tilbagevendende møde med dig, vil det dukke op her.",
"past_bookings": "Dine tidligere bookinger vil blive vist her.",
"cancelled_bookings": "Dine annullerede bookinger vil blive vist her.",
"unconfirmed_bookings": "Dine ubekræftede bookinger vil dukke op her.",
"unconfirmed_bookings_tooltip": "Ubekræftede bookinger",
"on": "den",
@@ -355,7 +348,6 @@
"browse_api_documentation": "Gennemse vores API-dokumentation",
"leverage_our_api": "Udnyt vores API til fuld kontrol og tilpasning.",
"create_webhook": "Opret Webhook",
"booking_cancelled": "Booking Aflyst",
"booking_rescheduled": "Booking Omlagt",
"booking_created": "Booking Oprettet",
"meeting_ended": "Møde Afsluttet",
@@ -367,8 +359,6 @@
"team_webhooks": "Team Webhooks",
"create_new_webhook_to_account": "Opret en ny webhook til din konto",
"new_webhook": "Ny Webhook",
"receive_cal_meeting_data": "Modtag {{appName}} mødedata på en specificeret URL, i realtid, når en begivenhed bliver planlagt eller aflyst.",
"receive_cal_event_meeting_data": "Modtag {{appName}} mødedata på en specificeret URL, i realtid, når denne begivenhed bliver planlagt eller aflyst.",
"responsive_fullscreen_iframe": "Responsiv fuldskærms-iframe",
"loading": "Indlæser...",
"deleting": "Sletter...",
@@ -976,14 +966,12 @@
"event_location": "Begivenhedens placering",
"reschedule_optional": "Årsag til omlægning (valgfri)",
"reschedule_placeholder": "Lad andre vide, hvorfor du har brug for at omlægge",
"event_cancelled": "Denne begivenhed er aflyst",
"emailed_information_about_cancelled_event": "Vi har sendt en e-mail til dig og de andre deltagere for at give dem besked.",
"this_input_will_shown_booking_this_event": "Dette input vil blive vist ved booking af denne begivenhed",
"meeting_url_in_conformation_email": "Møde-url er i bekræftelses-mailen",
"url_start_with_https": "URL'en skal starte med http:// eller https://",
"number_provided": "Telefonnummer vil blive angivet",
"before_event_trigger": "før begivenheden starter",
"event_cancelled_trigger": "når begivenheden er aflyst",
"new_event_trigger": "når ny begivenhed er booket",
"email_host_action": "send e-mail til vært",
"sms_number_action": "send SMS til et specifikt nummer",
@@ -1356,12 +1344,11 @@
"team_url_required": "Der skal indtastes et team URL",
"team_url_taken": "Denne URL er allerede i brug",
"team_publish": "Publicer team",
"number_sms_notifications": "Telefonnummer (SMS notifikationer)",
"attendee_email_variable": "Deltagers e-mail",
"attendee_email_info": "Personen der booker's e-mail",
"kbar_search_placeholder": "Indtast en kommando eller søgning...",
"invalid_credential": "Åh nej! Det ser ud til at tilladelsen er udløbet eller blevet tilbagekaldt. Geninstaller venligst igen.",
"reschedule_reason_success_page": "Årsag til omlægning",
"reschedule_reason": "Årsag til omlægning",
"choose_common_schedule_team_event": "Vælg en fælles tidsplan",
"choose_common_schedule_team_event_description": "Aktivér dette hvis du ønsker at bruge en fælles tidsplan mellem værter. Når deaktiveret, vil hver vært blive booket baseret på deres standardtidsplan.",
"reason": "Årsag",
+2 -17
View File
@@ -12,10 +12,8 @@
"have_any_questions": "Haben Sie Fragen? Wir sind hier um Ihnen zu helfen.",
"reset_password_subject": "{{appName}}: Anleitung zum Zurücksetzen des Passworts",
"event_declined_subject": "Abgelehnt: {{title}} am {{date}}",
"event_cancelled_subject": "Storniert: {{title}} um {{date}}",
"event_request_declined": "Ihre Event-Anfrage wurde abgelehnt",
"event_request_declined_recurring": "Ihre wiederkehrende Terminanfrage wurde abgelehnt",
"event_request_cancelled": "Ihr geplanter Termin wurde abgesagt",
"organizer": "Organisator",
"need_to_reschedule_or_cancel": "Möchten Sie einen neuen Termin finden oder absagen?",
"cancellation_reason": "Grund für Ihre Absage (optional)",
@@ -77,9 +75,7 @@
"your_meeting_has_been_booked": "Dein Termin wurde gebucht",
"event_type_has_been_rescheduled_on_time_date": "Ihr {{title}} wurde auf den {{date}} verschoben.",
"event_has_been_rescheduled": "Ihr Termin wurde verschoben.",
"request_reschedule_subtitle": "{{organizer}} hat die Buchung storniert und Sie gebeten, eine andere Zeit auszuwählen.",
"request_reschedule_title_organizer": "Sie haben {{attendee}} zur Neuplanung aufgefordert",
"request_reschedule_subtitle_organizer": "Sie haben die Buchung storniert und {{attendee}} muss eine neue Buchungszeit mit Ihnen vereinbaren.",
"rescheduled_event_type_subject": "Anfrage für Neuplanung gesendet: {{eventType}} mit {{name}} am {{date}}",
"requested_to_reschedule_subject_attendee": "Aktion erforderlich - Neuplanung: Bitte buchen Sie eine neue Zeit für „{{eventType}}“ mit {{name}}",
"hi_user_name": "Hallo, {{name}}",
@@ -303,7 +299,6 @@
"error_end_time_next_day": "Die Endzeit darf nicht größer als 24 Stunden sein",
"back_to_bookings": "Zurück zu den Buchungen",
"free_to_pick_another_event_type": "Sie können jederzeit einen anderen Termin wählen.",
"cancelled": "Abgesagte",
"cancellation_successful": "Stornierung erfolgreich",
"really_cancel_booking": "Möchten Sie wirklich stornieren?",
"cannot_cancel_booking": "Sie können diese Buchung nicht stornieren",
@@ -312,7 +307,6 @@
"cancelling_event_recurring": "Der Termin ist eine Instanz eines wiederkehrenden Termins.",
"cancelling_all_recurring": "Dies sind alle verbleibende Instanzen im wiederkehrenden Termin.",
"error_with_status_code_occured": "Ein Fehler mit dem Statuscode {{status}} ist aufgetreten.",
"booking_already_cancelled": "Dieser Termin wurde bereits storniert",
"booking_already_accepted_rejected": "Diese Buchung wurde bereits angenommen oder abgelehnt",
"go_back_home": "Zurück zum Anfang",
"or_go_back_home": "Oder navigieren Sie zurück",
@@ -326,7 +320,6 @@
"upcoming_bookings": "Sobald jemand eine Zeit bei Ihnen bucht können Sie das hier sehen.",
"recurring_bookings": "Sobald jemand einen wiederkehrenden Termin mit Ihnen bucht wird er hier erscheinen.",
"past_bookings": "Ihre früheren Buchungen werden hier angezeigt.",
"cancelled_bookings": "Ihre stornierten Buchungen werden hier angezeigt.",
"unconfirmed_bookings": "Ihre unbestätigten Buchungen werden hier angezeigt.",
"unconfirmed_bookings_tooltip": "Unbestätigte Buchungen",
"on": "auf",
@@ -366,7 +359,6 @@
"browse_api_documentation": "Durchsuchen Sie unsere API Dokumentation",
"leverage_our_api": "Nutzen Sie unsere API für die volle Kontrolle und Anpassbarkeit.",
"create_webhook": "Webhook erstellen",
"booking_cancelled": "Termin abgesagt",
"booking_rescheduled": "Termin verlegt",
"recording_ready": "Download-Link der Aufnahme bereit",
"booking_created": "Termin erstellt",
@@ -379,8 +371,6 @@
"team_webhooks": "Team-Webhooks",
"create_new_webhook_to_account": "Erstellen Sie einen neuen Webhook in Ihrem Account",
"new_webhook": "Neuer Webhook",
"receive_cal_meeting_data": "Erhalte {{appName}} Termindaten unter einer bestimmten URL in Echtzeit, wenn ein Termin geplant oder abgesagt wird.",
"receive_cal_event_meeting_data": "Erhalte {{appName}} Termindaten unter einer bestimmten URL in Echtzeit, wenn dieser Termin geplant oder abgesagt wird.",
"responsive_fullscreen_iframe": "Responsives Vollbild-iFrame",
"loading": "Wird geladen...",
"deleting": "Lösche...",
@@ -757,7 +747,7 @@
"delete_managed_event_type": "Verwalteten Ereignistyp löschen?",
"delete_event_type_description": "Jede Person, mit der Sie diesen Link geteilt haben, kann nicht mehr über den Link buchen.",
"delete_managed_event_type_description": "<ul><li>Die Ereignistypen der diesem Ereignistypen zugewiesenen Mitglieder werden ebenfalls gelöscht.</li><li>Jeder, mit dem sie ihren Link geteilt haben, wird nicht mehr über diesen Link buchen können.</li></ul>",
"confirm_delete_event_type": "<ul><li>Die Ereignistypen der diesem Ereignistypen zugewiesenen Mitglieder werden ebenfalls gelöscht.</li><li>Jeder, mit dem sie ihren Link geteilt haben, wird nicht mehr über diesen Link buchen können.</li></ul>",
"confirm_delete_event_type": "Ja, löschen",
"delete_account": "Account löschen",
"confirm_delete_account": "Ja, Account löschen",
"delete_account_confirmation_message": "Sind Sie sicher, dass Sie Ihr {{appName}}-Konto löschen möchten? Sie können dann nicht mehr über Ihre geteilten Links gebucht werden und alle Einstellungen werden gelöscht.",
@@ -1018,14 +1008,12 @@
"event_location": "Ort des Ereignisses",
"reschedule_optional": "Grund für die Verschiebung (optional)",
"reschedule_placeholder": "Lassen Sie andere wissen, warum Sie den Termin verschieben müssen",
"event_cancelled": "Dieser Termin ist abgesagt",
"emailed_information_about_cancelled_event": "Wir haben Ihnen und den anderen Teilnehmern eine E-Mail gesendet, damit alle Bescheid wissen.",
"this_input_will_shown_booking_this_event": "Diese Eingabe wird bei der Buchung dieses Termins angezeigt",
"meeting_url_in_conformation_email": "Termin-URL ist in der Bestätigungsmail",
"url_start_with_https": "URL muss mit http:// oder https:// beginnen",
"number_provided": "Telefonnummer wird angegeben",
"before_event_trigger": "vor Beginn des Termins",
"event_cancelled_trigger": "wenn das Ereignis abgesagt wird",
"new_event_trigger": "wenn ein neer Termin gebucht wird",
"email_host_action": "E-Mail an den Veranstalter senden",
"email_attendee_action": "E-Mail an Teilnehmende senden",
@@ -1420,12 +1408,11 @@
"team_url_required": "Team-URL muss eingegeben werden",
"team_url_taken": "Diese URL ist bereits vergeben",
"team_publish": "Team veröffentlichen",
"number_sms_notifications": "Telefonnummer (SMS-Benachrichtigungen)",
"attendee_email_variable": "Teilnehmer E-Mail",
"attendee_email_info": "Die E-Mail-Adresse der buchenden Person",
"kbar_search_placeholder": "Geben Sie einen Befehl ein oder suchen Sie ...",
"invalid_credential": "Oh nein! Es sieht so aus, als ob die Berechtigung abgelaufen ist oder entzogen wurden. Bitte erneut installieren.",
"reschedule_reason_success_page": "Verlegungsgrund",
"reschedule_reason": "Verlegungsgrund",
"choose_common_schedule_team_event": "Gemeinsamen Zeitplan auswählen",
"choose_common_schedule_team_event_description": "Aktivieren Sie dies, wenn Sie einen gemeinsamen Zeitplan zwischen Hosts verwenden möchten. Wenn deaktiviert, ist jeder Host basierend auf seinen Standardplan buchbar.",
"reason": "Grund",
@@ -1601,7 +1588,6 @@
"no_longer_attending": "Sie nehmen an diesem Termin nicht mehr teil",
"attendee_no_longer_attending_subject": "Ein Teilnehmer nimmt nicht mehr an {{title}} am {{date}} teil",
"attendee_no_longer_attending": "Ein Teilnehmer nimmt nicht mehr an Ihrem Termin teil",
"attendee_no_longer_attending_subtitle": "{{name}} hat abgesagt. Das bedeutet, dass ein Platz für dieses Zeitfenster geöffnet wurde",
"create_event_on": "Erstelle Termin am",
"default_app_link_title": "Standard-App-Link festlegen",
"default_app_link_description": "Mit dem Festlegen eines Standard-App-Links können alle neu erstellten Ereignistypen den App-Link verwenden, den Sie gesetzt haben.",
@@ -1712,7 +1698,6 @@
"least_booked_members": "Am wenigsten gebuchte Mitglieder",
"events_created": "Ereignisse erstellt",
"events_completed": "Ereignisse abgeschlossen",
"events_cancelled": "Ereignisse abgesagt",
"events_rescheduled": "Ereignisse neu geplant",
"from_last_period": "seit dem letzten Zeitraum",
"from_to_date_period": "Von: {{startDate}} Bis: {{endDate}}",
@@ -8,7 +8,6 @@
"have_any_questions": "Έχετε ερωτήσεις? Είμαστε εδώ για να βοηθήσουμε.",
"reset_password_subject": "{{appName}}: Οδηγίες επαναφοράς κωδικού πρόσβασης",
"event_declined_subject": "Απορρίφθηκε: {{title}} στις {{date}}",
"event_cancelled_subject": "Ακύρωση: {{title}} στις {{date}}",
"need_to_reschedule_or_cancel": "Χρειάζεται να επαναπρογραμματίσετε ή να ακυρώσετε;",
"cancellation_reason": "Λόγος ακύρωσης (προαιρετικό)",
"rejection_reason": "Λόγος απόρριψης",
@@ -152,7 +151,6 @@
"friday_time_error": "Μη έγκυρη ώρα την Παρασκευή",
"saturday_time_error": "Μη έγκυρη ώρα το Σάββατο",
"error_with_status_code_occured": "Παρουσιάστηκε ένα σφάλμα με κωδικό κατάστασης {{status}}.",
"booking_already_cancelled": "Αυτή η κράτηση είχε ήδη ακυρωθεί",
"booking_already_accepted_rejected": "Αυτή η κράτηση έγινε ήδη αποδεκτή ή απορρίφθηκε",
"go_back_home": "Επιστροφή στην αρχική σελίδα",
"or_go_back_home": "Ή επιστροφή στην αρχική σελίδα",

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