Compare commits

..
327 Commits
Author SHA1 Message Date
Peer Richelsen 761dfac033 3.0.1
release.yml / release (push) Canceled after 0s
2023-06-17 22:48:38 +02:00
Crowdin Bot 9fda4acc5b New Crowdin translations by Github Action 2023-06-17 20:45:08 +00:00
Jaideep Guntupalli 0470708aad refactor: translated request_password_reset in dutch (#9601) 2023-06-17 22:44:36 +02:00
Crowdin Bot bee392ba1a New Crowdin translations by Github Action 2023-06-17 20:43:01 +00:00
Hariom BalharaandPeer Richelsen 448f57442b Use the old booker redirect approach (#9589)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-17 20:42:15 +00:00
Crowdin Bot 856b090b4b New Crowdin translations by Github Action 2023-06-17 20:28:18 +00:00
Ujwal Kumar b99661a411 Fix typo in email verification banner (#9595) 2023-06-17 22:27:24 +02:00
Crowdin Bot d96a26eb3c New Crowdin translations by Github Action 2023-06-17 15:48:23 +00:00
Eric Dantas 9d08f5c7c8 chore: Create add-PRs-to-project-reviewing-PRs.yml (#9597)
* Create add-PRs-to-project-eric.yml

* Update and rename add-PRs-to-project-eric.yml to add-PRs-to-project-reviewing-PRs.yml

* Update add-PRs-to-project-reviewing-PRs.yml
2023-06-17 17:47:29 +02:00
Crowdin Bot 4fe5f6ca6f New Crowdin translations by Github Action 2023-06-17 01:06:55 +00:00
Esaú MoraisandAlex van Andel af505935bd fix: update top banner (#9580)
* fix: update wrong banner message in english

* fix: update `resend_email` translation in pt-BR

* chore: add `Icon` prop to `TopBanner` component

* fix: remove unnecessary variant scope

* Remove translations so they can be retranslated, complete design

* Changed key of already translated entry

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-06-17 01:06:04 +00:00
Leo Giovanetti 4a30f60622 Configuring totp code when verifying too (#9584) 2023-06-16 20:08:12 +00:00
Alex van Andel 24ed04e6c7 v3.0.0.2
release.yml / release (push) Canceled after 0s
2023-06-16 19:21:26 +02:00
Hariom Balhara bed3595314 fix: Dynamic Group Links on vercel (#9576)
* Potential fix

* Debug

* Debug

* Use the same logic in new-booker
2023-06-16 19:00:23 +02:00
Alex van Andel 74671263bf fix: New booker selected date issues (#9570)
* Pick correct selected date

* Commented out unused booking page revalidation

* Fixed selected default bookerLayout not being applied

* Use the right weekdayOfFirst - date(1) of browsingDate
2023-06-16 18:08:58 +02:00
Crowdin Bot bdc5e9c7e9 New Crowdin translations by Github Action 2023-06-16 14:39:35 +00:00
Leo Giovanetti 8fa1c70a97 Adding more time to org admin email verif step (#9574) 2023-06-16 14:38:40 +00:00
Hariom Balhara 1343c8fa8d fix: Booking Page crash with prefilling guests (#9566)
* Fix Booking Page crash with prefilling guests

* Update packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx
2023-06-16 15:02:59 +02:00
Crowdin Bot 5c0f9510a1 New Crowdin translations by Github Action 2023-06-16 12:46:06 +00:00
b2801170fa fix: Default tab selected issue (#9546)
* default tab selected issue

* refactor: improve cde

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

* fix: use onSuccess

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

* fix: move onsuccess and onerror

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: Vinoth Kumar V <vinoth_kumar_v@Vinoths-MacBook-Pro.local>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-06-16 12:45:20 +00:00
Alex van Andel 6ac19e8307 v3.0.0.1
release.yml / release (push) Canceled after 0s
2023-06-16 12:48:20 +02:00
Hariom Balhara 344241cfbf fix: New Booker Rewrites (#9557)
* Solve the problem using Next.config.js only

* Debug

* Avoid infinite rewritees

* Apply suggestions from code review

* Apply suggestions from code review

* Remove stale comments

* Update packages/embeds/embed-core/src/embed-iframe.ts
2023-06-16 12:31:57 +02:00
Carina WollendorferandCarinaWolli 66ebf18fa9 add team slug to team event link (#9558)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-06-16 11:05:47 +02:00
Crowdin Bot e6d6f3317e New Crowdin translations by Github Action 2023-06-16 08:38:03 +00:00
Carina WollendorferandCarinaWolli 55f66e6bcd remove slash (#9556)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-06-16 08:37:06 +00:00
Crowdin Bot 22e2b0a6a9 New Crowdin translations by Github Action 2023-06-16 06:27:50 +00:00
sydwardrae e24f3828cb Spelling chore common.json (#9550)
Fixed occured to occurred
2023-06-16 08:26:59 +02:00
Crowdin Bot 70f5bdc499 New Crowdin translations by Github Action 2023-06-15 22:38:02 +00:00
zomars 8731a39c3c fix: api build 2023-06-15 15:36:59 -07:00
Omar López e2d9e75475 v3.0.0
release.yml / release (push) Canceled after 0s
2023-06-15 15:15:39 -07:00
Crowdin Bot 55c7707b94 New Crowdin translations by Github Action 2023-06-15 22:15:20 +00:00
Crowdin Bot 98944d4815 New Crowdin translations by Github Action 2023-06-15 17:29:48 +00:00
Crowdin Bot faba25acbd New Crowdin translations by Github Action 2023-06-15 17:28:46 +00:00
aab5ee344c feat: Org settings - profile,appearance, child teams, create new child (#9231)
* Initial commit

* Adding feature flag

* Desktop first banner, mobile pending

* Removing dead code and img

* AppInstallButtonBase

* WIP

* Adds Email verification template+translations for organizations (#9202)

* feat: Orgs Schema Changing `scopedMembers` to `orgUsers` (#9209)

* Change scopedMembers to orgMembers

* Change to orgUsers

* First step done

* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding

* Session logic to show org label

* Step 2 done, avatar not working

* List orgs and list teams specific if orgs exist

* Conditionally show org - fix settings layout - add labels for all pages

* Profile Page + update

* Org specific team creation

* appearance page

* Ensure members cant of org cant update settings in UI

* Fix update handler imports

* hide billing on sub teams

* Update profile slug page

* Letting duplicate slugs for teams to support orgs

* Add slug coliisions for org

* Covering null on unique clauses

* Covering null on unique clauses

* Extract to utils

* Update settings to use subdomain path in team url , team + org

* Supporting having the orgId in the session cookie

* Onboarding admins step

* Last step to create teams

* Update handler comments

* Upgrade ORG banner - disabled team banner for child teams

* Handle publishing ORGS

* Fix licenese issue

* Update packages/trpc/server/routers/viewer/teams/create.handler.ts

* Moving change password handler, improving verifying code flow

* Clearing error before submitting

* Reverting email testing api changes

* Reverting having the banner for now

* Consistent exported components

* Remove unneeded files from banner

* Removing uneeded code

* Fixing avatar selector

* Using meta component for head/descr

* Missing i18n strings

* A11ly

* Feedback

* Making an org avatar (temp)

* Add slug colission detection for user and team name

* Fix Import

* Remove update password func

* Fix module import over relative

* feat: organization event type filter (#9253)

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

* Missing changes to support orgs schema changes

* Remove app install button sa its in 9337

* Remove i18n key not being used

* feat: Onboarding process to create an organization (#9184)

* Desktop first banner, mobile pending

* Removing dead code and img

* WIP

* Adds Email verification template+translations for organizations (#9202)

* First step done

* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding

* Step 2 done, avatar not working

* Covering null on unique clauses

* Onboarding admins step

* Last step to create teams

* Moving change password handler, improving verifying code flow

* Clearing error before submitting

* Reverting email testing api changes

* Reverting having the banner for now

* Consistent exported components

* Remove unneeded files from banner

* Removing uneeded code

* Fixing avatar selector

* Using meta component for head/descr

* Missing i18n strings

* Feedback

* Making an org avatar (temp)

* Check for subteams slug clashes with usernames

* Fixing create teams onsuccess

* feedback

* Making sure we check requestedSlug now

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* feat: [CAL-1816] Organization subdomain support (#9345)

* Desktop first banner, mobile pending

* Removing dead code and img

* WIP

* Adds Email verification template+translations for organizations (#9202)

* First step done

* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding

* Step 2 done, avatar not working

* Covering null on unique clauses

* Onboarding admins step

* Last step to create teams

* Moving change password handler, improving verifying code flow

* Clearing error before submitting

* Reverting email testing api changes

* Reverting having the banner for now

* Consistent exported components

* Remove unneeded files from banner

* Removing uneeded code

* Fixing avatar selector

* Using meta component for head/descr

* Missing i18n strings

* Feedback

* Making an org avatar (temp)

* Check for subteams slug clashes with usernames

* Fixing create teams onsuccess

* Covering users and subteams, excluding non-org users

* Unpublished teams shows correctly

* Create subdomain in Vercel

* feedback

* Renaming Vercel env vars

* Vercel domain check before creation

* Supporting cal-staging.com

* Change to have vercel detect it

* vercel domain check data message error

* Remove check domain

* Making sure we check requestedSlug now

* Feedback and unneeded code

* Reverting unneeded changes

* Unneeded changes

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Vercel subdomain creation in PROD only

* feat: organization settings general and members page (#9266)

* feat: organization settings general page

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

* feat: add members page

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

* chore: remove

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

* fix: use invalidate

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

* fix: delete mutation

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

* fix: remove organization id

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

* chore

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

* fix: use zod schema

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

---------

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

* Type fixes

* Reverting changes

* Update UsernameTextfield.tsx

* More reverts

* Update next-auth-options.ts

* Update common.json

* Type fixes

* Include invite token for orgs

* Update org schema

* Make token settings optional as it isnt used in orgs yet

* Fix missing prop

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-06-15 10:27:39 -07:00
Leo Giovanettiandzomars 6880f88dbe Org teams (#9544)
Co-authored-by: zomars <zomars@me.com>
2023-06-15 16:55:45 +00:00
Crowdin Bot 9177fe86dc New Crowdin translations by Github Action 2023-06-15 16:37:15 +00:00
bf3f8acbc0 feat: Enable new booker globally for 3.0 (#9541)
* Update migration to be on by default - remove cookie check

* Use env variable to control enabling NEW_BOOKER for all

* Update packages/prisma/migrations/20230524105015_added_newbooker_feature_flag/migration.sql

* Update turbo.json

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2023-06-15 16:36:25 +00:00
b4fa9826f2 feat: organizations sidebar (#9395)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-06-15 16:12:47 +05:30
Jeroen ReumkensandPeer Richelsen 955de3133c feat: weekly view calendar improvements (#9491)
* Add correct start/end time for weekly calendar by looking at calendar's availability

* Always show 24h in weekly calendar, added scroll functionality, current time and events in correct timezone

* Improved current time style

* Show slots for every minute, so also odd timeslots due to offsets work

* Show correct timeformat for weekly view on left side time stamps

* Fix data attr for debug

* Position events differently so we can accomodate for offset start times.

* Removed schedule from public event api, because we don't use it anymore

* Fixed alignment of timeslots in weekview calendar.

* Added loading spinner to weekly calendar

* Force weekly view calendar hour labels to show 00 minutes, also for gmt x.5 timezones that are offset 30 mins.

* Change event duration blocks in weekly calendar when user changes duration in multi duration event

* Improved week view slot time label alignments for shorter durations

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-15 09:59:58 +00:00
Jeroen Reumkens faac3d0f57 fix: Fixed merge mistake in next.config breaking new booker redirects. (#9532) 2023-06-15 14:30:44 +05:30
Crowdin Bot 3fb1ede770 New Crowdin translations by Github Action 2023-06-15 08:59:05 +00:00
Hariom Balhara 53224886e3 feat: Routing Forms/Teams Support (#9417) 2023-06-15 08:58:07 +00:00
Udit Takkar e513180d7e fix: check user session in banner (#9530)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-06-15 09:36:09 +01:00
Udit Takkar 3116d6df1a fix: Email verification banner (#9519) 2023-06-15 11:41:32 +05:30
4284a4d16d feat: Organizations users calendar cache (#9498)
* Initial commit

* Adding feature flag

* feat: Orgs Schema Changing `scopedMembers` to `orgUsers` (#9209)

* Change scopedMembers to orgMembers

* Change to orgUsers

* Letting duplicate slugs for teams to support orgs

* Covering null on unique clauses

* Supporting having the orgId in the session cookie

* feat: organization event type filter (#9253)

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

* Missing changes to support orgs schema changes

* feat: Onboarding process to create an organization (#9184)

* Desktop first banner, mobile pending

* Removing dead code and img

* WIP

* Adds Email verification template+translations for organizations (#9202)

* First step done

* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding

* Step 2 done, avatar not working

* Covering null on unique clauses

* Onboarding admins step

* Last step to create teams

* Moving change password handler, improving verifying code flow

* Clearing error before submitting

* Reverting email testing api changes

* Reverting having the banner for now

* Consistent exported components

* Remove unneeded files from banner

* Removing uneeded code

* Fixing avatar selector

* Using meta component for head/descr

* Missing i18n strings

* Feedback

* Making an org avatar (temp)

* Check for subteams slug clashes with usernames

* Fixing create teams onsuccess

* feedback

* Making sure we check requestedSlug now

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* feat: [CAL-1816] Organization subdomain support (#9345)

* Desktop first banner, mobile pending

* Removing dead code and img

* WIP

* Adds Email verification template+translations for organizations (#9202)

* First step done

* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding

* Step 2 done, avatar not working

* Covering null on unique clauses

* Onboarding admins step

* Last step to create teams

* Moving change password handler, improving verifying code flow

* Clearing error before submitting

* Reverting email testing api changes

* Reverting having the banner for now

* Consistent exported components

* Remove unneeded files from banner

* Removing uneeded code

* Fixing avatar selector

* Using meta component for head/descr

* Missing i18n strings

* Feedback

* Making an org avatar (temp)

* Check for subteams slug clashes with usernames

* Fixing create teams onsuccess

* Covering users and subteams, excluding non-org users

* Unpublished teams shows correctly

* Create subdomain in Vercel

* feedback

* Renaming Vercel env vars

* Vercel domain check before creation

* Supporting cal-staging.com

* Change to have vercel detect it

* vercel domain check data message error

* Remove check domain

* Making sure we check requestedSlug now

* Feedback and unneeded code

* Reverting unneeded changes

* Unneeded changes

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Vercel subdomain creation in PROD only

* Making sure we let localhost still work

* Feedback

* Type check fixes

* feat: Organization branding in side menu (#9279)

* Desktop first banner, mobile pending

* Removing dead code and img

* WIP

* Adds Email verification template+translations for organizations (#9202)

* First step done

* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding

* Step 2 done, avatar not working

* Covering null on unique clauses

* Onboarding admins step

* Last step to create teams

* Moving change password handler, improving verifying code flow

* Clearing error before submitting

* Reverting email testing api changes

* Reverting having the banner for now

* Consistent exported components

* Remove unneeded files from banner

* Removing uneeded code

* Fixing avatar selector

* Org branding provider used in shell sidebar

* Using meta component for head/descr

* Missing i18n strings

* Feedback

* Making an org avatar (temp)

* Using org avatar (temp)

* Not showing org logo if not set

* User onboarding with org branding (slug)

* Check for subteams slug clashes with usernames

* Fixing create teams onsuccess

* feedback

* Feedback

* Org public profile

* Public profiles for team event types

* Added setup profile alert

* Using org avatar on subteams avatar

* Making sure we show the set up profile on org only

* Profile username availability rely on org hook

* Update apps/web/pages/team/[slug].tsx

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Update apps/web/pages/team/[slug].tsx

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* feat: Organization support for event types page (#9449)

* Desktop first banner, mobile pending

* Removing dead code and img

* WIP

* Adds Email verification template+translations for organizations (#9202)

* First step done

* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding

* Step 2 done, avatar not working

* Covering null on unique clauses

* Onboarding admins step

* Last step to create teams

* Moving change password handler, improving verifying code flow

* Clearing error before submitting

* Reverting email testing api changes

* Reverting having the banner for now

* Consistent exported components

* Remove unneeded files from banner

* Removing uneeded code

* Fixing avatar selector

* Org branding provider used in shell sidebar

* Using meta component for head/descr

* Missing i18n strings

* Feedback

* Making an org avatar (temp)

* Using org avatar (temp)

* Not showing org logo if not set

* User onboarding with org branding (slug)

* Check for subteams slug clashes with usernames

* Fixing create teams onsuccess

* feedback

* Feedback

* Org public profile

* Public profiles for team event types

* Added setup profile alert

* Using org avatar on subteams avatar

* Processing orgs and children as profile options

* Reverting change not belonging to this PR

* Making sure we show the set up profile on org only

* Removing console.log

* Comparing memberships to choose the highest one

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Type errors

* Refactor and type fixes

* Update orgDomains.ts

* Feedback

* Reverting

* NIT

* fix issue getting org slug from domain

* Improving orgDomains util

* Host comes with port

* Update useRouterQuery.ts

* add org users cache-calendar

* fix typo

* re-use the simple user calendar-cache page

* Apply suggestions from code review

* Update packages/core/CalendarManager.ts

* type fixes

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2023-06-15 00:40:47 +00:00
zomars eadca3b2c0 Update .env.example 2023-06-14 14:45:53 -07:00
Crowdin Bot 68125af29f New Crowdin translations by Github Action 2023-06-14 21:44:55 +00:00
Crowdin Bot d436e628fe New Crowdin translations by Github Action 2023-06-14 21:41:30 +00:00
c5483c81de feat: Organizations (#8993)
* Initial commit

* Adding feature flag

* feat: Orgs Schema Changing `scopedMembers` to `orgUsers` (#9209)

* Change scopedMembers to orgMembers

* Change to orgUsers

* Letting duplicate slugs for teams to support orgs

* Covering null on unique clauses

* Supporting having the orgId in the session cookie

* feat: organization event type filter (#9253)

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

* Missing changes to support orgs schema changes

* feat: Onboarding process to create an organization (#9184)

* Desktop first banner, mobile pending

* Removing dead code and img

* WIP

* Adds Email verification template+translations for organizations (#9202)

* First step done

* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding

* Step 2 done, avatar not working

* Covering null on unique clauses

* Onboarding admins step

* Last step to create teams

* Moving change password handler, improving verifying code flow

* Clearing error before submitting

* Reverting email testing api changes

* Reverting having the banner for now

* Consistent exported components

* Remove unneeded files from banner

* Removing uneeded code

* Fixing avatar selector

* Using meta component for head/descr

* Missing i18n strings

* Feedback

* Making an org avatar (temp)

* Check for subteams slug clashes with usernames

* Fixing create teams onsuccess

* feedback

* Making sure we check requestedSlug now

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* feat: [CAL-1816] Organization subdomain support (#9345)

* Desktop first banner, mobile pending

* Removing dead code and img

* WIP

* Adds Email verification template+translations for organizations (#9202)

* First step done

* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding

* Step 2 done, avatar not working

* Covering null on unique clauses

* Onboarding admins step

* Last step to create teams

* Moving change password handler, improving verifying code flow

* Clearing error before submitting

* Reverting email testing api changes

* Reverting having the banner for now

* Consistent exported components

* Remove unneeded files from banner

* Removing uneeded code

* Fixing avatar selector

* Using meta component for head/descr

* Missing i18n strings

* Feedback

* Making an org avatar (temp)

* Check for subteams slug clashes with usernames

* Fixing create teams onsuccess

* Covering users and subteams, excluding non-org users

* Unpublished teams shows correctly

* Create subdomain in Vercel

* feedback

* Renaming Vercel env vars

* Vercel domain check before creation

* Supporting cal-staging.com

* Change to have vercel detect it

* vercel domain check data message error

* Remove check domain

* Making sure we check requestedSlug now

* Feedback and unneeded code

* Reverting unneeded changes

* Unneeded changes

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Vercel subdomain creation in PROD only

* Making sure we let localhost still work

* Feedback

* Type check fixes

* feat: Organization branding in side menu (#9279)

* Desktop first banner, mobile pending

* Removing dead code and img

* WIP

* Adds Email verification template+translations for organizations (#9202)

* First step done

* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding

* Step 2 done, avatar not working

* Covering null on unique clauses

* Onboarding admins step

* Last step to create teams

* Moving change password handler, improving verifying code flow

* Clearing error before submitting

* Reverting email testing api changes

* Reverting having the banner for now

* Consistent exported components

* Remove unneeded files from banner

* Removing uneeded code

* Fixing avatar selector

* Org branding provider used in shell sidebar

* Using meta component for head/descr

* Missing i18n strings

* Feedback

* Making an org avatar (temp)

* Using org avatar (temp)

* Not showing org logo if not set

* User onboarding with org branding (slug)

* Check for subteams slug clashes with usernames

* Fixing create teams onsuccess

* feedback

* Feedback

* Org public profile

* Public profiles for team event types

* Added setup profile alert

* Using org avatar on subteams avatar

* Making sure we show the set up profile on org only

* Profile username availability rely on org hook

* Update apps/web/pages/team/[slug].tsx

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Update apps/web/pages/team/[slug].tsx

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* feat: Organization support for event types page (#9449)

* Desktop first banner, mobile pending

* Removing dead code and img

* WIP

* Adds Email verification template+translations for organizations (#9202)

* First step done

* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding

* Step 2 done, avatar not working

* Covering null on unique clauses

* Onboarding admins step

* Last step to create teams

* Moving change password handler, improving verifying code flow

* Clearing error before submitting

* Reverting email testing api changes

* Reverting having the banner for now

* Consistent exported components

* Remove unneeded files from banner

* Removing uneeded code

* Fixing avatar selector

* Org branding provider used in shell sidebar

* Using meta component for head/descr

* Missing i18n strings

* Feedback

* Making an org avatar (temp)

* Using org avatar (temp)

* Not showing org logo if not set

* User onboarding with org branding (slug)

* Check for subteams slug clashes with usernames

* Fixing create teams onsuccess

* feedback

* Feedback

* Org public profile

* Public profiles for team event types

* Added setup profile alert

* Using org avatar on subteams avatar

* Processing orgs and children as profile options

* Reverting change not belonging to this PR

* Making sure we show the set up profile on org only

* Removing console.log

* Comparing memberships to choose the highest one

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Type errors

* Refactor and type fixes

* Update orgDomains.ts

* Feedback

* Reverting

* NIT

* fix issue getting org slug from domain

* Improving orgDomains util

* Host comes with port

* Update useRouterQuery.ts

* Feedback

* Feedback

* Feedback

* Feedback: SSR for user event-types to have org context

* chore: Cache node_modules (#9492)

* Adding check for cache hit

* Adding a separate install step first

* Put the restore cache steps back

* Revert the uses type for restoring cache

* Added step to restore nm cache

* Removed the cache-hit check

* Comments and naming

* Removed extra install command

* Updated the name of the linting step to be more clear

* Removes the need for useEffect here

* Feedback

* Feedback

* Cookie domain needs a dot

* Type fix

* Update apps/web/public/static/locales/en/common.json

Co-authored-by: Omar López <zomars@me.com>

* Update packages/emails/src/templates/OrganizationAccountVerifyEmail.tsx

* Feedback

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-06-14 21:40:20 +00:00
Keith Williams 9db4a04cd9 chore: Cache node_modules (#9492)
* Adding check for cache hit

* Adding a separate install step first

* Put the restore cache steps back

* Revert the uses type for restoring cache

* Added step to restore nm cache

* Removed the cache-hit check

* Comments and naming

* Removed extra install command

* Updated the name of the linting step to be more clear
2023-06-14 15:27:47 -03:00
Alex van Andel 8943e8c2f6 Fix: Sets null to undefined during workflow select (#9513) 2023-06-14 14:43:42 +00:00
Crowdin Bot f17fa55426 New Crowdin translations by Github Action 2023-06-14 14:39:25 +00:00
Hariom BalharaandLeo Giovanetti 9ff83075d6 Improve IP Ban (#9512)
Co-authored-by: Leo Giovanetti <hello@leog.me>
2023-06-14 16:38:34 +02:00
Jeroen Reumkens 6b6043afdc Added custom error with more details to debug the ciaran event issue (#9482) 2023-06-14 10:47:58 +00:00
Crowdin Bot fd9162e70f New Crowdin translations by Github Action 2023-06-14 09:54:17 +00:00
Jeroen ReumkensandHariom Balhara 8331059b7c feat: new booker embed (#9023)
* Added embed files for new booker routes.

* Updated nextjs config to redirect to correct embed url.

* Notify embed of booker size after animation completes.

* Don't use animations and 100vh in embed.

* Added booker layout toggles to embed settings (wip)

* Booker header positioning tweaks

* Show correct layout in embed

* Don't show pricing event meta if stripe app is not installed

* Support custom border booker for embed

* wip on passing layout prop to iframe for previews

* Only show booker layout settings in embed-create-popup if booker layout feature is enabled.

* Made layout prop in preview state for embed optional

* Made layout prop in preview state for embed optional

* Get layout toggle working

* Fixed types

* Added themebasis prop

* Fix potential circular import that should then fix type errors

* Fixed type error by fixing casing.

* Type fixes

* Added translations

* More translations

* Remove comment

* Removed uneccessary addition to package.json

* Removed todo since it's solved in another way

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-06-14 09:22:44 +00:00
Syed Ali Shahbaz f0992b4914 fix with object undefined check (#9493) 2023-06-13 20:27:42 +00:00
TachyonicBytesandTachyonicBytes e081a4cfc2 fix: Add e2e test for routing forms webhook subscription (#9401)
* add e2e test for routing forms webhook subscription

* remove debugging checks

* fixed missing method for e2e

* remove forced timeout for e2e test

* fix e2e webhook test

* use routing forms page for install

---------

Co-authored-by: TachyonicBytes <support@tachyonicbytes.com>
2023-06-13 15:22:19 +00:00
895b3ca7c9 feat: add paid webhook (#8721)
* feat: add BOOKING_PAID on enums

* feat: trigger the BOOKING_PAID webhook

* refactor: create payment variable

* feat: add REJECTED and REQUESTED on enums

* feat: create migration to booking werbhooks

* Update packages/features/webhooks/lib/constants.ts

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>

* Update packages/prisma/schema.prisma

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>

* Update packages/prisma/migrations/20230513235419_add_booking_webhooks/migration.sql

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>

---------

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2023-06-13 14:57:59 +00:00
Jaideep GuntupalliandBailey Pumfleet 4d30264950 fix: Multiple In Person Locations not working as expected in Event Setup Tab (#9465)
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-06-13 14:54:53 +00:00
3a352a94f6 fix: phone input update state persistence (#9471)
* fix: phone input update state persistence

* refactor: use spreading

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: alannnc <alannnc@gmail.com>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-06-13 14:54:13 +00:00
Nikeshkumar TK 8623175612 fix: Routing form two "New" button issue. (#9479)
* event types public api fix

* Routing form new button issue

* Routing form new button issue

* Routing form new button issue

* Routing form new button issue

* Routing form new button issue
2023-06-13 14:53:50 +00:00
Udit TakkarandPeer Richelsen ea531777c4 fix: size of cell (#9486)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-13 11:08:00 +00:00
Soham Datta 2639ef7e13 fix: #9217 | redirected unready /admin to one of its tabs (#9222) 2023-06-13 10:46:41 +00:00
Jeroen Reumkens 0e76a19c96 Pass event duration to schedule api call (#9484) 2023-06-13 10:14:45 +00:00
3ec9eb377d docs: Added command in manual section (#9433)
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-06-12 21:46:29 +01:00
Udit TakkarandBailey Pumfleet 44b7000509 fix: dark mode in Alert component (#9467)
* fix: dark mode in Alert component

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>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-06-12 17:49:44 +00:00
depfu[bot]andHariom Balhara 251307f1e6 chore: Update vite 2.9.15 → 2.9.16 (patch) (#9346)
* Update vite to version 2.9.16

* Upgrade vite to 4.1.2 everywhere

---------

Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-06-12 16:51:02 +00:00
Balveer Singh RaoandUdit Takkar 75e638ec4d fix: copy icon for dark mode (#9459)
* fixed copy icon for dark mode

* fix: height

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-12 16:48:37 +00:00
Crowdin Bot 443ccdd6c0 New Crowdin translations by Github Action 2023-06-12 16:28:32 +00:00
Abhishek Sunil dbf11cc04b test: Add E2E test to verify theme change (#9455)
* Add E2E test to verify theme change

* PR fixes
2023-06-12 16:14:40 +00:00
Crowdin Bot a5c9729f5f New Crowdin translations by Github Action 2023-06-12 10:57:30 +00:00
sean-brydon a6e2983a22 Fix translations (#9466) 2023-06-12 10:43:50 +00:00
Meet Patel a21f0c46c7 enabled csv upload for team bulk invites (#9446) 2023-06-11 09:36:59 +00:00
Udit Takkar ed4a81e852 fix: theme in new booker (#9453)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-06-11 09:11:46 +00:00
9231218350 fix: carify what sender ID means (#9431)
* add tooltip on info icon

* fix text

* fix UI in addActionDialog

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-06-11 03:20:13 +00:00
7d1ea4ee2b chore: moved viewport meta from document to PageWrapper (#9406)
* moved viewport meta from document to _app

* moved viewport meta from _app.tsx to PageWrapper.tsx

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
2023-06-10 15:19:17 +00:00
Efraín Rochín 0c68e51930 fix: [CAL-1765] opt-in: clicking "confirm" or "reject" broken (#9443)
* Add support to a custom sessionGetter in trpc createContext

* type fixes
2023-06-10 11:02:50 +00:00
Alex van Andel 8e565dc610 v2.9.8
release.yml / release (push) Canceled after 0s
2023-06-10 12:46:24 +02:00
Rob JacksonandUdit Takkar 5b47844fa1 fix: Able to toggle off timeFormat on new booker UI (#9442)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-06-10 07:51:49 +00:00
Rob JacksonandEfraín Rochín 0d01d3246a test: Add E2E tests for seated attendee rescheduling or cancelling bookings (#9422)
* Add test for attendees rescheduling bookings

* Add tests for rescheduling seated bookings (first attendee)

* Fix tests not skipping disabled times on new booker UI

* Add tests for rescheduling seated bookings (all attendees)

* Remove duplicate tests (my mistake)

* Add explicit test ids for cancel inputs

* Add E2E tests for seated attendees cancelling their bookings

* Update E2E tests for a seated attendee rescheduling their booking

* Update tests to remove dependence on localised text

* Updated seated E2E tests to remove testing of unused URL

---------

Co-authored-by: Efraín Rochín <roae.85@gmail.com>
2023-06-10 03:47:50 +00:00
Alex van AndelandEfraín Rochín bb31c4ba14 fix: Prevent invalid host assignment (#9441)
* Avoids invalid hosts

* Fixed test selector, instead using react-select-2-listbox

* Different way of selecting the managed user

* Convert select-option- to value instead of label

* Replaced all instances of networkidle

* done rewriting test

* Fix prisma is not defined error

* Depend on inner text rather than testid

* Fill of team create is unstable

* Removed console.log

---------

Co-authored-by: Efraín Rochín <roae.85@gmail.com>
2023-06-10 02:39:49 +00:00
Crowdin Bot d9a7952467 New Crowdin translations by Github Action 2023-06-09 22:33:12 +00:00
Balveer Singh Rao 208dcbbda3 fixed Rotate spinner (#9429) 2023-06-09 22:21:54 +00:00
Crowdin Bot ff4494d395 New Crowdin translations by Github Action 2023-06-09 20:45:51 +00:00
Udit Takkar db4b32119e fix: UI bugs (#9435)
* fix: toggle group background

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

* fix: background

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

* chore

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

* fix: confirm modal

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

* fix: dark mode footer

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

* fix: change tootltip

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

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-06-09 20:32:35 +00:00
Carina WollendorferandCarinaWolli bfef5b1877 fix: team bio (#9438)
* fix bio rendering

* fix type error

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-06-09 12:50:35 +00:00
Carina WollendorferandCarinaWolli 91962cc90b check that input teamId equals webhook team (#9434)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-06-09 11:18:59 +00:00
Carina WollendorferandCarinaWolli 7a71a5ddd4 fix error when updating event type availability (#9428)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-06-09 10:19:49 +00:00
Jeroen Reumkens ca47a589ba feat: weekly view for new booker. (#9389)
* WIP on weekly view for new booker.

* Set responsive grid height for booker weekly view.

* Fixed types

* Ensure you can't click on disabled dates in weekly calendar

* CAL-1916: Improvements for blocked out parts background blocks, made the lines connect by using a shared background.

* Margin, padding and color tweaks for new booker CAL-1911

* Made week view have more days visible by default

* Fix to prevent layout switch from breaking when clicking same layout twice.
2023-06-09 09:38:18 +00:00
Crowdin Bot a33dbd8922 New Crowdin translations by Github Action 2023-06-09 08:31:50 +00:00
aaab4ca741 refactor: TwoFactor.tsx (#9392)
* Update TwoFactor.tsx

* fix: useeffect

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 <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-09 08:19:33 +00:00
Crowdin Bot 6aadf7429d New Crowdin translations by Github Action 2023-06-08 16:57:52 +00:00
GitStart-Cal.comandgitstart-calcom 748307bc91 fix: Wrong padding on modals - second version (CALCOM-8414) (#9419)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-06-08 16:45:06 +00:00
Crowdin Bot 559389a319 New Crowdin translations by Github Action 2023-06-08 14:37:40 +00:00
Ninad Trivedi a9140b6a76 Scrollbar bug fixed (#9357) 2023-06-08 14:25:23 +00:00
Crowdin Bot 8f50e873c9 New Crowdin translations by Github Action 2023-06-08 13:53:38 +00:00
9b28b7f78a fix: remove duplicate value i18n (#8547)
* fix: remove duplicate value i18n

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

* feat: add duplicate check in next config

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

* fix: remove more duplicates

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

* fix: description on two factor page

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

* Update apps/web/next.config.js

* Update apps/web/next.config.js

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-08 13:37:54 +00:00
Crowdin Bot 4417097f97 New Crowdin translations by Github Action 2023-06-08 12:45:11 +00:00
Jeroen Reumkens 93fc036d2c feat: Made booker layout settings more user friendly (#9397)
* Made booker layout settings more user friendly

* Fixed tyeps and default values in appearance form for booker layout

* Enable all layouts by default
2023-06-08 12:32:17 +00:00
Omar López 7f13fc58b9 v2.9.7
release.yml / release (push) Canceled after 0s
2023-06-07 16:02:32 -07:00
alannncandAlex van Andel 527d1f6c93 fix: remove bg-color (#9408)
* remove bg-color

* Update packages/app-store/_components/AppCard.tsx

Fix lint error trailing ,

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-06-07 16:02:32 -07:00
Omar López 0ec3c496d8 fix: missing company name in emails (#9409) 2023-06-07 16:02:32 -07:00
Jeroen Reumkens 62ec3de0b1 Recurring event doesn't neccessarily mean an event that requires confirmation. We need to show the recurring option nevertheless. (#9393) 2023-06-07 16:02:32 -07:00
Jeroen Reumkens 2c47cb6a3b Only take owner data we need in public event api, instead of everything. Saves bandwidth. (#9394) 2023-06-07 16:02:32 -07:00
Jeroen Reumkens dc2b145843 Show header layout in large views even if theres only one layout enable. Don't show toggle though. (#9398) 2023-06-07 16:02:32 -07:00
Nafees Nazik c958e1578a feat: use the correct locale for the title (#9286) 2023-06-07 16:02:32 -07:00
Crowdin Bot 1e857e5c0a New Crowdin translations by Github Action 2023-06-07 20:08:38 +00:00
Peer Richelsen 9bc8d48415 fix: leading none in shell (#9404) 2023-06-07 19:57:10 +00:00
f86c6734a7 fix: Wrong padding on "Invite Team Members" modal (CALCOM-8414-FIX) (#9273)
* Fix margin from invitation modal content

* Change margin to padding in Dialog component, so the divider absolute property works properly

* Update Dialog.tsx padding from the button top from 24px to 16px

* Change Dialog content padding to match figma design, having the padding bottom 16px

* Update FormBuilder.tsx to match figma

* Update FormBuilder.tsx Dialog content inner div paddings

* Update FormBuilder.tsx, remove relative to fix divider

* Fixing padding across dialogs

* nit

* Fixes e2e

* undo wrong padding

* fixed footer with divider

---------

Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: zomars <zomars@me.com>
2023-06-07 19:55:47 +00:00
HitenandPeer Richelsen 3d5ff82062 Fix width issue in general setting (#9388)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-07 11:08:29 +00:00
Crowdin Bot 8b63483e2e New Crowdin translations by Github Action 2023-06-07 10:56:44 +00:00
TheOnlyWayUpandPeer Richelsen c97d4288c0 typo(README): 'concent' -> 'consent' (#9356)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-07 11:55:45 +01:00
Hariom Balhara ff75364f22 Fix embed detection (#9385) 2023-06-07 08:43:12 +00:00
Crowdin Bot 7f1b028099 New Crowdin translations by Github Action 2023-06-07 07:40:33 +00:00
9e70a985e4 feat: email verification (#9081)
* Verify - inital email commit

* Add token type - api redirect - migration

* Redirect and valid api callback route

* Update email design

* Change signup URL to redirect to verify-email

* Add feature flag - add a11y text to email - add top banner

* Shell shouldnt redirect to onboarding if the user needs to verify account

* Move flag check to server

* Cleanup

* Rate limit

* Fix redirects

* Remove api signup mess

* Double negation for forced bool

* Fix props

* Update packages/emails/templates/account-verify-email.ts

* Enable migration by default

* Fix typos

* Fix google verify issue

* Update packages/features/auth/lib/verifyEmail.ts

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>

* NITS: @harioms addressed

* Remove schema changes

* Fix NITs+ improvments

* Update apps/web/pages/api/auth/verify-email.ts

Co-authored-by: Omar López <zomars@me.com>

* Update packages/features/ee/common/components/LicenseRequired.tsx

Co-authored-by: Omar López <zomars@me.com>

* Update apps/web/pages/api/auth/verify-email.ts

Co-authored-by: Omar López <zomars@me.com>

* Always preloads feature flags

* Update verifyEmail.ts

* Update schema.prisma

* Type fix

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2023-06-07 07:27:48 +00:00
Crowdin Bot 057cf273dd New Crowdin translations by Github Action 2023-06-07 02:15:50 +00:00
Omar López 9af621294d fix: Prevents undefined features flags on first render (#9374) 2023-06-07 02:03:52 +00:00
zomars bfb5a31032 Fix broken test introduced in #9085 2023-06-06 17:35:03 -07:00
Hariom BalharaandOmar López 6308955e89 fix: embed non-transparent dark background on success page (#9085)
* Fix colorscheme dark on success page due to embed not detected properly there

* Use calNamespace in getEmbediframe

* Fix missing variable

* Update basic.e2e.ts

---------

Co-authored-by: Omar López <zomars@me.com>
2023-06-06 23:52:13 +00:00
Crowdin Bot b0c5b629d7 New Crowdin translations by Github Action 2023-06-06 23:52:45 +00:00
10f965570b feat: Add copy invite link (#8355)
* feat: Add shared invite link

* refactor: Rename Invite to Team Invite model

* feat: add admin check for team invite link procedures

* Replace TeamInvite with VerificationToken

* Add team invite null checks

* Migrates tRPC procedures to new format

* Type fixes

* Update common.json

---------

Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-06-06 23:34:14 +00:00
Udit TakkarandPeer Richelsen c24f5200f5 fix: opt in booking from email (#9358)
* fix: opt in booking from email

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

* fix: undo this

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: type error

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

* fix: Avada Kedavra type errors

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-06 21:04:06 +00:00
zomars f922692a1a Sync yarn.lock with website 2023-06-06 12:07:37 -07:00
Crowdin Bot 65c137f67d New Crowdin translations by Github Action 2023-06-06 18:44:00 +00:00
Efraín Rochínandzomars 585679969a perf: Avoid useless dependencies in calendar cache page (#9290)
* increases the time range to cover all time zones

* it isolates the getCachedResults and rename to getCalendarsEvents

* remove unrelated changes

* remove unrelated changes

---------

Co-authored-by: zomars <zomars@me.com>
2023-06-06 18:30:29 +00:00
Crowdin Bot 893b67b6c4 New Crowdin translations by Github Action 2023-06-06 18:16:49 +00:00
Crowdin Bot 3c296b440e New Crowdin translations by Github Action 2023-06-06 18:15:56 +00:00
Crowdin Bot f8a59b52f7 New Crowdin translations by Github Action 2023-06-06 18:14:59 +00:00
Efraín RochínandKeith Williams 17d106cfda chore: Replace UI login with api login on e2e tests (#9344)
* Replace the ui login with api login

* test api login

* go to first page after login in some tests

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-06-06 18:02:32 +00:00
Crowdin Bot dcf9d30382 New Crowdin translations by Github Action 2023-06-06 17:48:34 +00:00
eafd7f6b77 fix: Disabled pinch to zoom on mobile devices#9155 (#9163)
* Disabled pinch to zoom on mobile devices#9155

* Modified the better fix to the feature

* Update apps/web/pages/_document.tsx

* Update apps/web/pages/_document.tsx

* Update apps/web/styles/globals.css

* Update apps/web/pages/_document.tsx

* Update apps/web/pages/_document.tsx

* Update apps/web/pages/_document.tsx

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-06-06 17:35:19 +00:00
f31165b442 feat: event settings booker layout toggle (#9082)
* WIP for adding booker layout toggle in event settings pages

* Prevent form error from getting form stuck in loading state

* Fixed types for bookerlayouts settings and preselect correct layout in booker

* Added defaultlayout settings to profile too, and use that in booker plus as default for events.

* Made layout settings responsive

* Added feature toggle for new layout settings

* Fixed user builder for tests by adding defaultlyotu

* Show toggles on booker for layout switch based on selected layouts. Also added a small fix for the settings toggles to preselect the correct toggle for defaultlayout when user profile settings are used.

* Used zod parse to fix type errors.

* Fix unit test

* Set selected date to today in datepicker when week or column view is default layout. It uses that date to show in the title bar.

* Moved booker layout settings to event and user meta data instead of new db column.

* Converted booker layout strings into an enum.

* Renamed booker layouts feature flag and deleted unused v2 booker feature flag.

* Update packages/trpc/server/routers/viewer/eventTypes/update.handler.ts

Co-authored-by: Omar López <zomars@me.com>

* Fix import

* Fix lint warnings in EventTypeSingleLayout

* Fixed bug where when selected date was passed via query param page booking form wouldn't automatically show up. It would still serve you the date selection. This should fix e2e tests.

* Fixed layout header.

* Enabled booking layout toggle feature flag.

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-06-06 15:31:43 +00:00
Crowdin Bot 40c5d5871e New Crowdin translations by Github Action 2023-06-06 14:09:06 +00:00
Jeroen Reumkensandsean-brydon 2362754356 feat: CAL-1674 Added new header layout for new booker. (#9227)
* CAL-1674: Added new header layout for new booker.

* Removed unneccesary bg style for toggle group, since it's already the default bg color.

* Fixed types

* Gave minwidth to booker header date title, to prevent arrows for switching weeks from jumping around.

* Push small calendar down

* Fixed overflow in small calendar view

* Added booker modal padding underneath badges

* Fixed grid area name causing broken animation. Also use setstyle directly instead of using animate for non animated properties. Performs better and removes some errors by framer motion for non animatable units like 'auto'.

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-06-06 13:55:27 +00:00
Crowdin Bot 8ccf3b589e New Crowdin translations by Github Action 2023-06-06 12:14:47 +00:00
2dbc73c75b chore: fix all Typescript warnings (#8618)
* [CAL-1517] fix all Typescript warnings

* solve conflicts

* Update stripeCheckoutSession.handler.ts

Parse is a guard, so even though the variable is unused the parse itself is needed.

* Update ToolbarPlugin.tsx

Don't change dependency tree

---------

Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-06-06 11:59:57 +00:00
Ben Hybert 87e1ad7115 chore: switch over to use variable for app/companyName (#9334)
* switch over to use variable for app/companyName
* enable_apps_description - appName
* recording_from_your_recent_call - appName
* the_calcom_team - companyName
* calcom_is_better_with_team - appName

* capitalise 'S' in teams page.
2023-06-06 11:58:05 +00:00
il3ven 2e9895aceb add telegramvideo to packages/app-tore/index.ts (#9324)
`telegramvideo` seems to be missing and causing issue #9308
2023-06-06 11:24:54 +00:00
fbbc573f33 chore: remove deprecated metamask and rainbow (#9299)
* Remove deprecated code related to rainbow

* Remove translations for rainbow specific features

* Remove english translations for metamask

* Remove translations for metamask

* Fix lint issues

* Fix prettier issues

* update yarn lock

* Generated yarn.lock with submodules

* Added auth submodule deps

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-06-06 11:18:44 +00:00
Piyush GargandPeer Richelsen 8bc8a045b6 feat: email undefined value (#9240)
* Fix: #9210

* Fix: #9215 Added Password Validation to App Signup

* FIX #9216 Fixed the Update Button Allignment

* Fix #9238: Don't render descripiton in email template if undefined

* Remove Prev Changes

* Remove unwanted space

* Revert yarn.lock

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-06 10:03:40 +00:00
Crowdin Bot 8c049b29b3 New Crowdin translations by Github Action 2023-06-06 09:47:49 +00:00
ravi kunwarandUdit Takkar f1e783f047 fix: Availability Name does Not Update (#9322)
* fix: Availability Name does Not Update

* fix: use i18n in navigation

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-06 09:33:34 +00:00
Crowdin Bot 3baa6c7145 New Crowdin translations by Github Action 2023-06-06 07:17:36 +00:00
dd28bb46db fix: title (#9055)
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-06 07:04:54 +00:00
c182091fa9 fix: /event-type /apps - doesn't match designs (#9317)
* updated AppCard comp for design changes

* updated rainfallForm for design changes

* Fix:: event-type/apps design issues

* Update apps/web/components/eventtype/EventAppsTab.tsx

* Update packages/app-store/_components/AppCard.tsx

* Update packages/app-store/_components/AppCard.tsx

* Fix lint

---------

Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
Co-authored-by: alannnc <alannnc@gmail.com>
2023-06-05 22:49:27 +00:00
Crowdin Bot d061572ed6 New Crowdin translations by Github Action 2023-06-05 22:34:31 +00:00
Ujwal Kumarandalannnc 3a943fbc81 Fix background colour for show on booking page switch on event types page (#9318)
Co-authored-by: alannnc <alannnc@gmail.com>
2023-06-05 22:24:10 +00:00
Crowdin Bot 8a58a51a2c New Crowdin translations by Github Action 2023-06-05 12:20:39 +00:00
Crowdin Bot e92265ceaa New Crowdin translations by Github Action 2023-06-05 12:19:41 +00:00
Udit Takkar b7b1fff1c0 fix: hide 2fa tab when using google as identity provider (#9333)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-06-05 12:04:48 +00:00
Crowdin Bot 886f7021f4 New Crowdin translations by Github Action 2023-06-05 10:13:18 +00:00
Alex van Andel 2b17188087 Fixup tsconfig.json in packaged embed-react (#9330) 2023-06-05 09:57:53 +00:00
Ayush Mainali d498988bd4 fix: fixes page rendering comments initially (#9270) 2023-06-05 09:55:10 +00:00
Hariom Balhara 785a08ba2c Fix duplicate query param missing issue (#9328) 2023-06-05 09:50:34 +00:00
Hariom BalharaandBailey Pumfleet 6aa83102e4 Add stricter self-review guideline (#9327)
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-06-05 11:00:04 +01:00
Ujwal Kumar 97fe41f3ba Fix hardcoded label of identifier label in form builder (#9249) 2023-06-05 09:47:08 +00:00
Piyush GargandPeer Richelsen b7cecfbb66 fix: Skeleton Padding and Width Issue (#9310)
* Fixed the Skeleton Padding and Width Issue

* Fix the Icon Bounce on Skeleton

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-05 09:24:16 +00:00
Anwar Sadath 7f7d80e653 fix: /getting-started/connected-calendar Toggle the first calendar on (#9028)
* [CAL-115] /getting-started/connected-calendar Toggle the first calendar on

* Fix lint error

* Fix error shown after completion of onboarding by connecting to calendars and playing around with toggle

* Fix type errors

* Fix first calendar selected even in all the screens wherever connectedCalendars is used
2023-06-05 09:21:50 +00:00
Ujwal Kumarandsean-brydon 64796a96fc Fix alignment of update and cancel button on sign up flow (#9218)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-06-05 09:16:28 +00:00
Crowdin Bot 83cc7dc86e New Crowdin translations by Github Action 2023-06-05 09:23:16 +00:00
Nafees Nazik 75c9145558 feat: add disable signup feature flag (#9274)
* feat: add feature flag

* feat: use the feature flag
2023-06-05 09:08:16 +00:00
Nafees Nazik 869f459d98 fix: title in booking submitted page (#9326) 2023-06-05 07:28:03 +00:00
Crowdin Bot e00008b663 New Crowdin translations by Github Action 2023-06-05 06:04:16 +00:00
175f2482e1 fix: Update forms UI (#8093)
* Routing Forms: Update forms UI

* fix failing test

* fix: Routing Forms forms UI Update

* fix: Routing Forms forms UI Update

* Apply suggestions from code review

---------

Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-06-05 05:51:19 +00:00
Crowdin Bot 9d91d72a72 New Crowdin translations by Github Action 2023-06-04 18:58:28 +00:00
Ujwal Kumar fd9cd1b98b fix: background colour for disabled select option (#9320)
* Fix disabled bg colour in CheckedTeamSelect

* Fix disabled bg colour in CheckedSelect, CheckedUserSelect, ChildrenEventTypeSelect
2023-06-04 18:46:19 +00:00
Crowdin Bot 2640040359 New Crowdin translations by Github Action 2023-06-03 11:13:34 +00:00
alannnc dec8f8e896 add team validations (#9300) 2023-06-03 11:02:23 +00:00
Efraín RochínandOmar López b4eff25636 fix: Issues with calendar sync at the beginning and end of month (#9282)
* increases the time range to cover all time zones

* remove console.log

---------

Co-authored-by: Omar López <zomars@me.com>
2023-06-02 20:17:53 +00:00
Alex van Andel 2a752e5358 v2.9.6
release.yml / release (push) Canceled after 0s
2023-06-02 22:18:28 +02:00
Syed Ali Shahbaz b2684837c1 Remove end as mandatory property (#9297) 2023-06-02 20:01:30 +00:00
Crowdin Bot 2fed2133a5 New Crowdin translations by Github Action 2023-06-02 19:44:04 +00:00
Alex van Andel 7fc0877c26 Set reqBody.end to start + eventType.length (#9296) 2023-06-02 19:29:52 +00:00
Crowdin Bot f7ce774ddc New Crowdin translations by Github Action 2023-06-02 18:42:40 +00:00
174c730f1f chore: add canonical to the document via PageWrapper (#8639)
* add canonical to the document via `PageWrapper`

* use WEBSITE_URL constant to create the canonical url value

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-02 18:28:03 +00:00
Syed Ali Shahbaz 8ad513e4ec fix typo in swagger (#9278) 2023-06-01 21:09:18 +00:00
a0bf5b4067 test: Smoke Tests for packaged embeds and build improvements (#9169)
* Fix 2 Factor Auth

* Add a sandbox to verify types of embed-react

* Add fault types location

* Fix type location again

* Break types

* Ensure that builds are done again when doing pbublish

* Debug failure in CI

* Make sure unit test files arent used by playwright

* Fix embed-react test description

* Update .github/workflows/e2e-embed-react.yml

Co-authored-by: Omar López <zomars@me.com>

* Remove unnecessary log

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Omar López <zomars@me.com>
2023-06-01 20:41:30 +00:00
Hariom BalharaandOmar López 484f603c9e fix: Routing Forms, Number operators were using string operands (#9182)
* Fix operators for number

* Add number operator unit tests

* Fixes and remove between operators

---------

Co-authored-by: Omar López <zomars@me.com>
2023-06-01 20:29:13 +00:00
zomars 94b16fb327 Update yarn.lock 2023-06-01 12:29:49 -07:00
Jeroen Reumkens 55db7bdf76 Make new booker work with private links (#9260) 2023-06-01 18:55:43 +00:00
Crowdin Bot 14af241d5f New Crowdin translations by Github Action 2023-06-01 18:57:05 +00:00
Keith Williams 4efa90e42f chore: Upgrade turbo (#9271)
* chore: Upgrade turbo

* Pinned turbo version

* Revert "Pinned turbo version"

This reverts commit ae263009ee4750943e25efe62052152b40655844.

* Upgraded to 1.10.1
2023-06-01 18:43:21 +00:00
Peer Richelsen 581e1759c4 chore: prevent merge conflicts in common.json (#9268) 2023-06-01 15:47:43 +00:00
Crowdin Bot 61cdac321e New Crowdin translations by Github Action 2023-06-01 15:09:08 +00:00
Ayush MainaliandPeer Richelsen 59a04137a1 fix: fixed width resizing of colorpicker (#9233)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-01 14:53:39 +00:00
Crowdin Bot eba1a1ffff New Crowdin translations by Github Action 2023-06-01 12:30:28 +00:00
Nafees Nazik df25b179d7 fix: use skeleton loader (#9264) 2023-06-01 12:17:11 +00:00
Ted Spare ff2d9ad67d Use github-script to safely label PRs from forks (#9256) 2023-06-01 11:48:36 +00:00
Crowdin Bot 485b72cd0d New Crowdin translations by Github Action 2023-06-01 11:56:48 +00:00
Jeroen ReumkensandPeer Richelsen 57eb5a4328 Fix for booker colors in dark mode, we can't use bg-default in both places because we use a custom background in dark mode. Otherwise it would be a similar color in dark. So we need to supply a custom dark mode color. (#9259)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-01 11:38:52 +00:00
Peer Richelsen 2c4954e31c Update package.json
release.yml / release (push) Canceled after 0s
2023-06-01 12:43:56 +01:00
Alex van Andel 748f2b42e8 Remove debugger; calls (#9262) 2023-06-01 11:28:59 +00:00
Nafees NazikandKeith Williams 6bc713bc2e feat: add additional parameters for QR code generator app (#9177)
* fix: add i18n key

* fix: add additional parameters

* fix: query params

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-06-01 10:13:54 +00:00
Hariom Balhara b1019d6bab fix: [Hotfix]No phone number is valid (#9258)
* Fix invalid phone number always

* Fix countrycode detection
2023-06-01 09:47:18 +00:00
Nafees Nazik 268f1872e4 fix: layout effect hook warning (#9226)
* feat: add isomorphic hook

* feat: use useIsomorphicLayoutEffect hook
2023-06-01 09:45:24 +00:00
Efraín RochínandKeith Williams 60d439cc32 prevents revalidation errors from reaching the when editing an event type (#9191)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-06-01 09:16:30 +00:00
Crowdin Bot 36d201ac20 New Crowdin translations by Github Action 2023-06-01 08:01:31 +00:00
d6644bc232 feat: Customise JITSI Url path (#8835)
* Closes #1847 - adds an environment variable `JITSI_HOST_URL` to configure the Host URL for self hosted instances

* Closes #1847 - adds an environment variable `JITSI_HOST_URL` to configure the Host URL for self hosted instances

* Closes #1847 - adds an environment variable `JITSI_HOST_URL` to configure the Host URL for self hosted instances

* Replace Tab with spaces

* Add JITSI_HOST_URL env

* Remote Tabs for Spaces

* Support jitsi_host key

* Remove ENV, add Interface jitsi host URL and fallback

* Fix linting issues

* Clean code

* add missing await

* Add configurable meeting slug

* Refactor to jitsiHost, remove slug generator

* zod default doesnt work right now. So remove it

* Add replacement values and url encoding

* Change Placeholder to unified values.

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-06-01 07:48:01 +00:00
alannnc 7a9b893931 Add startOf endOf day for date range (#9252) 2023-05-31 22:12:04 +00:00
Crowdin Bot 235c314e68 New Crowdin translations by Github Action 2023-05-31 21:02:11 +00:00
Omar López 831253e057 v2.9.5
release.yml / release (push) Canceled after 0s
2023-05-31 14:01:14 -07:00
Crowdin Bot d9527902e6 New Crowdin translations by Github Action 2023-05-31 21:00:30 +00:00
Omar López 290ff79c86 feat: Ports getSchedule to public API (#9254)
* Ports getSchedule to public API

* Typefix
2023-05-31 20:46:54 +00:00
Crowdin Bot 8618df09e5 New Crowdin translations by Github Action 2023-05-31 19:02:21 +00:00
Alex van Andel 6a56c12ef1 Only include accepted users, ignore all pending, unregistered or not (#9250) 2023-05-31 18:48:32 +00:00
Peer Richelsen 64ae135f90 Merge branch 'main' of https://github.com/calcom/cal.com 2023-05-31 17:54:27 +01:00
Crowdin Bot b18f77c957 New Crowdin translations by Github Action 2023-05-31 16:47:18 +00:00
Peer Richelsen 98da15e801 Merge branch 'main' of https://github.com/calcom/cal.com 2023-05-31 17:46:38 +01:00
Nafees Nazik 90b0d24c1e fix: remove duplicate i18n key (#9234) 2023-05-31 16:33:29 +00:00
Crowdin Bot 34d728cae0 New Crowdin translations by Github Action 2023-05-31 16:31:11 +00:00
Syed Ali Shahbaz 3eedeb603d fix: Allows team owners and team admins to query team availability (#9245)
* allows team owners and team admins to query team availability

* code imp

* add type for memberRoles

* further type adjustments

* \_(o.o)_/

* type

* type..
2023-05-31 16:17:26 +00:00
Syed Ali Shahbaz 885bfdade4 feat: adds paymentID and allow attendeeEmail filtering in Booking API (#9143)
* adds payment id to return in GET bookings

* Delete .gitkeep. Unintended

* Delete Licenses. Unintended commit

* initial working code for attendeeEmails filter

* improve code readability

* adds multiAttendee validation akin to multi userId

* code improvement

* adds swagger format email
2023-05-31 12:35:30 +00:00
Crowdin Bot 1382d3bc56 New Crowdin translations by Github Action 2023-05-31 12:50:34 +00:00
Crowdin Bot 9068b221c8 New Crowdin translations by Github Action 2023-05-31 12:49:38 +00:00
Crowdin Bot b8be61947a New Crowdin translations by Github Action 2023-05-31 12:48:45 +00:00
Crowdin Bot 41811db5aa New Crowdin translations by Github Action 2023-05-31 12:47:54 +00:00
Crowdin Bot 39ba1bcdaf New Crowdin translations by Github Action 2023-05-31 12:47:06 +00:00
Crowdin Bot d8943274d1 New Crowdin translations by Github Action 2023-05-31 12:46:07 +00:00
Crowdin Bot 67082198aa New Crowdin translations by Github Action 2023-05-31 12:45:09 +00:00
Crowdin Bot db5dbdea40 New Crowdin translations by Github Action 2023-05-31 12:44:17 +00:00
Crowdin Bot 08eba7077d New Crowdin translations by Github Action 2023-05-31 12:43:26 +00:00
Crowdin Bot ecf5e7c7b7 New Crowdin translations by Github Action 2023-05-31 12:42:34 +00:00
Jeroen Reumkens 220023f403 feat(UI): improvements booker (#9221)
* Changed background from muted to default

* Improved skelleton color for loading states. Also prevented scrollbar in available timeslots loading state by showing less skeletons.

* Moved description to top, above other event meta, since this block doesnt have an icon and looks better this way.
2023-05-31 12:30:09 +00:00
Crowdin Bot 0d4e625041 New Crowdin translations by Github Action 2023-05-31 08:57:40 +00:00
Crowdin Bot 3f736e8b80 New Crowdin translations by Github Action 2023-05-31 08:56:49 +00:00
Hariom Balhara 9394c09247 Remove cache-db from without database build (#9220) 2023-05-31 09:55:57 +01:00
Crowdin Bot e7ab400d29 New Crowdin translations by Github Action 2023-05-31 04:46:32 +00:00
92012dab09 fix: wrong description in Calendar Invite after rescheduling meeting (#8576)
* added instructions for setup on windows to avoid symbolic link issues

* fixed wrong description after rescheduling

* Update README.md

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-31 04:33:52 +00:00
Crowdin Bot 26f0a14ac2 New Crowdin translations by Github Action 2023-05-31 03:16:45 +00:00
7a9a2fc76f feat: Make database optional when building (#8561)
* Make database optional when building

Solves #3026 and #6780

We have a few app-store related pages that require a database connection at build time.

This change adds try-catch blocks in getStaticPaths or getStaticProps for those pages, and when no database is available we either:

- getStaticPaths: Respond with no paths, but fall back to rendering the pages on-demand
- getStaticProps: Respond with no data, but enable revalidation until a future request does respond with data

This makes Cal.com more compatible with typical Docker-based workflows, that do not expect external services to be available at build time. This includes Docker-based hosting providers, such as Cloudron (#3026).

* Change apps/categories/index to be server-rendered

* Update yarn.lock

* Update yarn.lock

* Update [category].tsx

---------

Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-05-31 03:04:06 +00:00
Keith Williams 991167fb8b Removed extra db-seed call since we use cache-db (#9212) 2023-05-31 00:13:28 +00:00
Omar López 451278b48e fix: routing-form flakyness (#9214) 2023-05-30 21:15:57 +00:00
Nafees NazikandKeith Williams e6f142dc9e fix: event editor not rendered (#9172)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-05-30 19:38:17 +00:00
Nafees Nazik 951709edef fix: use ip address in forgot password rate limitation (#7832)
* feat: use IP address for rate limiting

* fix: use mail as the last choice

* fix: fallback to ip

* fix: endpoint

* fix: endpoint
2023-05-30 19:26:29 +00:00
Crowdin Bot 6cf8083b02 New Crowdin translations by Github Action 2023-05-30 19:01:13 +00:00
Crowdin Bot 3fe32d2a9b New Crowdin translations by Github Action 2023-05-30 19:00:26 +00:00
Crowdin Bot dc02443a33 New Crowdin translations by Github Action 2023-05-30 18:59:41 +00:00
Crowdin Bot f785ab553a New Crowdin translations by Github Action 2023-05-30 18:58:49 +00:00
Crowdin Bot 92c015ec83 New Crowdin translations by Github Action 2023-05-30 18:58:03 +00:00
Crowdin Bot 60bfdc0adc New Crowdin translations by Github Action 2023-05-30 18:57:09 +00:00
Crowdin Bot ae52c83010 New Crowdin translations by Github Action 2023-05-30 18:56:22 +00:00
Crowdin Bot 1c9a549c10 New Crowdin translations by Github Action 2023-05-30 18:55:26 +00:00
Crowdin Bot 4e55b88ec9 New Crowdin translations by Github Action 2023-05-30 18:54:27 +00:00
Crowdin Bot c87ada5646 New Crowdin translations by Github Action 2023-05-30 18:53:41 +00:00
Crowdin Bot d2af42431c New Crowdin translations by Github Action 2023-05-30 18:52:45 +00:00
Crowdin Bot 8a96fc7905 New Crowdin translations by Github Action 2023-05-30 18:51:53 +00:00
787edd28c7 fix: Popular Apps Slider (#9189)
* Fixes Popular Apps Slider

* Update PopularAppsSlider.tsx

* Reverts env cache

* Revert

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-05-30 18:36:48 +00:00
Keith Williams 2e379653a4 chore: Shard E2E tests (#9206)
* Shard E2E tests

* Updated shards to 5

* Removed action triggers used for testing
2023-05-30 17:31:21 +00:00
4541845340 feat: Use team logos for various meta icons (#8955)
* Use team logos for various meta icons

* Automatically resize team icons for different uses

* Fix api/logo error when unable to find a team

* Avoid loading image-optimizer in api/logo when not needed

---------

Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-05-30 16:31:49 +00:00
Crowdin Bot 59f9cf8aa3 New Crowdin translations by Github Action 2023-05-30 16:45:20 +00:00
7c46d1b348 chore: Cleanup full raw avatar responses from tRPC endpoints (#8901)
* [CAL-1383] Cleanup full raw avatar responses from tRPC endpoints

* Cleanup additional handlers where base64 images were sent through the response

* Add 'image' correctly to the profiles array

---------

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-05-30 16:30:19 +00:00
a6330c0fc8 chore: Rounding, frames and radio buttons of the booking forms not consistent (#8705)
* Rounding, frames and radio buttons of the booking forms not consistent

* Updates

---------

Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-05-30 15:47:22 +00:00
f72296c323 fix: footer in modals (#8940)
* [CAL-1543] Fix the footer in modals

* change request

* update style

* fix modal height

* Lint fix

* Fix the footer in modals

---------

Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-05-30 15:44:24 +00:00
93c4211da4 change request (#9075)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-05-30 15:43:24 +00:00
ea063dd487 feat: Add new events to webhook BOOKING_CONFIRMED, BOOKING_REJECTED (#8884)
* test: booking rejection

* test: check if webhook is called

* feat: BOOKING_REJECTED enum ,constant in backend

* feat: send booking rejected webhook to subscribers

* feat: add Booking rejected migration

* calendar event

* Revert "calendar event"

This reverts commit 28d45dccfdd3788f0124f9be8783161b1156cd09.

* feat: BOOKING_REQUESTED enum, constant, migration

* feat: Send BOOKING REQUESTED Webhook call

* feat: Add booking rejected/requested event in form

* feat: data-testid to rejection confirm btn

* test: BOOKING_REJECTED, BOOKING_REQUESTED

* fix: booking status PENDING, Linting

* feat: add new labels to common.json

* remove: meeting ended hook from request hook

* refactor: abstract handleWebhookTrigger

* fix: create a single file for migration

* refactor: reduce code repetition and fix test

* feat: add team webhooks to subscriberOptions

* refactor: subscriberOptions

---------

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-05-30 15:35:05 +00:00
18815a9a70 feat: added toggle for dynamic booking in general settings #9060 (#9077)
* added toggle for dynamic booking in general settings

* trigger GitHub actions

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-05-30 15:34:35 +00:00
54271283af fix: Adjust height of sidebar to fit on screen when top banner is visible (#8795)
* Adjust height of sidebar to fit on screen when top banner is visible

* Remove empty space on the bottom of the sidebar

* Remove gap at bottom left of sidebar

* fix: use layout effect and resize observer

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 <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-05-30 15:05:46 +00:00
Crowdin Bot dab0176938 New Crowdin translations by Github Action 2023-05-30 15:33:40 +00:00
ab6a7b5377 fix: Skeleton Loader (#8925)
* temp fix

* removed isLoading declaration

* final cleanup

---------

Co-authored-by: firefox292 <saurabh.n@nestaway.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-05-30 15:00:32 +00:00
1fdd28b974 fix(readme): Update version requirements (#9158)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-05-30 15:59:26 +01:00
317f59ba63 refactor: Show 404 page for new booker when event is not found. (#8946)
* CAL-1653: Show 404 page for new booker when event is not found.

* Fixed slack icon color on 404 page

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-05-30 11:49:52 -03:00
Crowdin Bot 3426c17f16 New Crowdin translations by Github Action 2023-05-30 14:34:07 +00:00
Crowdin Bot 5bc6cbe7f5 New Crowdin translations by Github Action 2023-05-30 14:33:05 +00:00
1eb873bd35 fix: update button fix (#9106)
* update button fix

* Update appearance.tsx

* fix linter

---------

Co-authored-by: Vinoth Kumar V <vinoth_kumar_v@Vinoths-MacBook-Pro.local>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-05-30 16:32:15 +02:00
Peer RichelsenandHariom Balhara 5bca02bac2 fix: full name of app authors and added correct urls (#9099)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-05-30 14:25:56 +00:00
a062b5a355 chore: translation for unauthorized workflow error (#8573)
* fix: translation for unauthorized workflow error

* change to dynamic string

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-05-30 14:24:17 +00:00
Hariom Balhara fba2a2e2ca test: Two Factor Auth (#9142)
* Fix 2 Factor Auth

* Add 2fa tests
2023-05-30 14:23:49 +00:00
VasuDevraniandPeer Richelsen 6daa69cff9 fix: loader animation in profile update button (#9162)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-30 14:18:49 +00:00
VasuDevraniandPeer Richelsen 60a3bc5832 feat: sidebar nav item tooltips (#9160)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-30 14:18:21 +00:00
24ee3401af Avoid exec the getAvailability function when there are`t calendar selected (#9192)
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-05-30 16:17:11 +02:00
Keith Williams cae1ca8dfb Replace user email with ID in logs (#9115) 2023-05-30 16:15:09 +02:00
VasuDevrani 35144e6f47 fixed: x button overlap (#9198) 2023-05-30 14:13:17 +00:00
Crowdin Bot e37e07ab3b New Crowdin translations by Github Action 2023-05-30 13:53:11 +00:00
Alex van Andel 9b9defc0f1 ci: Fix fetch() error, improvements to booking-seats test (#9203) 2023-05-30 13:52:07 +00:00
zomars feb31fae6e Update action.yml 2023-05-29 20:40:03 -07:00
04ff5f78a9 fix: upgrade btn bug (#9104)
* fix: upgrade btn bug

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

* fix: use border-subtle

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>
Co-authored-by: Omar López <zomars@me.com>
2023-05-29 20:31:55 -07:00
Hariom BalharaandPeer Richelsen 8922981012 fix: location for @calcom/embed-react types (#9090)
* Use correct location for types

* Release packages

* Update versions

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-29 20:30:52 -07:00
d5746ed277 Make trackingId begin with GTM- always (#8937)
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
2023-05-29 20:30:02 -07:00
Udit TakkarandPeer Richelsen dd94653a48 fix: confirm button not displayed (#9181)
* fix: confirm button not displayed

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

* fix: use paymentAppData

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-05-29 20:29:28 -07:00
d3ed637387 remove cold starts log (#9137)
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2023-05-29 20:29:07 -07:00
Crowdin Bot 6faa9c0b58 New Crowdin translations by Github Action 2023-05-30 03:13:26 +00:00
zomars 4c854e00ca Fix tests flakyness 2023-05-29 20:12:17 -07:00
zomars 67bb4cf5ca Skip flaky tests for now 2023-05-29 20:12:07 -07:00
zomars 0ed5ce229b Prevent flooding logs when no logo is available 2023-05-29 19:44:20 -07:00
Crowdin Bot 1065108a42 New Crowdin translations by Github Action 2023-05-30 02:06:22 +00:00
zomars 8e7080fc96 Fix logout test URL discrepancy 2023-05-29 19:05:34 -07:00
Crowdin Bot 46d46f9733 New Crowdin translations by Github Action 2023-05-30 01:40:02 +00:00
zomars 79e914b0fb Prevents page.pause reaching main 2023-05-29 18:28:40 -07:00
Crowdin Bot 0075414c8b New Crowdin translations by Github Action 2023-05-29 20:26:22 +00:00
zomars 285c122bdc Uses buildjet cache action 2023-05-29 13:22:24 -07:00
zomars be304738e7 Re-enables buildjet on PR blocking tasks 2023-05-29 12:54:02 -07:00
Joe Au-YeungandPeer Richelsen 9f881e9911 ci: Move linting to build jet (#9188)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-29 20:43:32 +01:00
Crowdin Bot 21cb2e5609 New Crowdin translations by Github Action 2023-05-29 19:16:45 +00:00
Crowdin Bot bfe0fb91f3 New Crowdin translations by Github Action 2023-05-29 19:14:49 +00:00
Adithya KrishnaandPeer Richelsen 37e6f18378 feat: Auto check PR titles if they follow conventional commits spec (#9109)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-29 20:12:31 +01:00
jemiluv8andHariom Balhara 15e50fcd34 fix: Route Builder rules should be case insensitive (#9040)
* override jsonLogic operators on string operands to allow for case insensitive comparisons.

Affected Operators: "==", "===", "!=", "!==", "in"

* disable no-explicit-any on jsonLogicOverrides file since most of the code there will be from jsonLogic and may not meet our coding style.

Majority of overrides require us to copy over functions and their signatures from jsonLogic and then modify their implementation.

The signature of functions implementing most operators take the operands typed as "any", which is intended, but doesn't adhere to our coding style. Hence the need to override the eslint rule

* run linter to fix issues

* Fix bug in in operator when second arg is an array

* remove redundant indexOf check on overriden jsonLogic "in" operator.

Note: this deviates from the original implementation in the jsonLogic library because our current useage ensures that the second operand is always a string or string[] and will therefore always have .index function. Whenever our invariants change in the future, make sure to modify this implementation to prevent any unexpected

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-05-29 20:11:55 +01:00
5534153d43 Add the or operator to the emails package.json as well (#9013)
Co-authored-by: TachyonicBytes <support@tachyonicbytes.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-29 20:11:45 +01:00
Crowdin Bot f0a9897daf New Crowdin translations by Github Action 2023-05-29 19:11:19 +00:00
Crowdin Bot a334415ff9 New Crowdin translations by Github Action 2023-05-29 19:10:28 +00:00
Crowdin Bot 75347b7a79 New Crowdin translations by Github Action 2023-05-29 19:09:34 +00:00
Crowdin Bot b8a756f98d New Crowdin translations by Github Action 2023-05-29 19:08:22 +00:00
Crowdin Bot 3bf23850a3 New Crowdin translations by Github Action 2023-05-29 19:07:27 +00:00
Crowdin Bot d13eb45565 New Crowdin translations by Github Action 2023-05-29 19:06:26 +00:00
Crowdin Bot 414c616f03 New Crowdin translations by Github Action 2023-05-29 19:05:35 +00:00
zomars bca4df677c Debugging API linting staleness 2023-05-29 12:03:32 -07:00
Crowdin Bot 876584e132 New Crowdin translations by Github Action 2023-05-29 18:59:25 +00:00
Crowdin Bot d242145c0a New Crowdin translations by Github Action 2023-05-29 18:58:30 +00:00
5640e2eb25 fix: Change Detroit to New York as a default for EST time zone (#8218)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2023-05-29 19:57:34 +01:00
Peer Richelsen db7494b714 Merge branch 'main' of https://github.com/calcom/cal.com 2023-05-29 17:28:09 +01:00
mohammed hussamandPeer Richelsen ada8e19715 feat: filter bookings by event type (#9011)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-29 17:01:17 +01:00
GitStart-Cal.comandgitstart-calcom df7870bfce fix: dark mode fixes (#9141)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-05-29 16:53:33 +01:00
Crowdin Bot 07749a9623 New Crowdin translations by Github Action 2023-05-29 15:53:25 +00:00
Udit TakkarandPeer Richelsen 8a181f3204 refactor: radio area group (#9113)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-29 16:52:27 +01:00
zomars fcb0fce799 Migrates missing jest test to vitest 2023-05-26 14:15:01 -07:00
Crowdin Bot fcb06f82d9 New Crowdin translations by Github Action 2023-05-26 20:59:30 +00:00
zomars 75ba35c732 Possible fix to stuck CI 2023-05-26 13:58:33 -07:00
Peer Richelsen 9cb01ef8ae Merge branch 'main' of https://github.com/calcom/cal.com 2023-05-24 14:47:23 +01:00
Peer Richelsen 4cf33cf12a Merge branch 'main' of https://github.com/calcom/cal.com 2023-05-22 23:45:38 +01:00
Peer Richelsen 64418e0a85 Merge branch 'main' of https://github.com/calcom/cal.com 2023-05-17 12:58:41 +01:00
Peer Richelsen f513fdf3c1 Merge branch 'main' of https://github.com/calcom/cal.com 2023-05-16 17:22:19 +01:00
Peer Richelsen 8290ca0106 Merge branch 'main' of https://github.com/calcom/cal.com 2023-05-10 11:13:21 +01:00
Peer Richelsen 32fb9cb746 Merge branch 'main' of https://github.com/calcom/cal.com 2023-05-05 14:58:47 +01:00
Peer Richelsen 807f186f6b Merge branch 'main' of https://github.com/calcom/cal.com 2023-04-27 12:07:02 +01:00
Peer Richelsen 52202948a9 Merge branch 'main' of https://github.com/calcom/cal.com 2023-04-25 20:18:40 +02:00
Peer Richelsen 4e545595b7 Merge branch 'main' of https://github.com/calcom/cal.com 2023-04-24 18:06:49 +02:00
Peer Richelsen 1af1802dbe fixed timezone dropdown 2023-04-22 17:55:15 +02:00
676 changed files with 16420 additions and 9689 deletions
-6
View File
@@ -106,12 +106,6 @@ LARK_OPEN_APP_ID=""
LARK_OPEN_APP_SECRET=""
LARK_OPEN_VERIFICATION_TOKEN=""
# - WEB3
# Used for the Web3 plugin
# @see https://github.com/calcom/cal.com/blob/main/packages/app-store/rainbow/README.md
ALCHEMY_API_KEY=""
INFURA_API_KEY=""
# - SALESFORCE
# Used for the Salesforce (Sales Cloud) app
SALESFORCE_CONSUMER_KEY=""
+26
View File
@@ -5,6 +5,7 @@
# - SHARED
# - NEXTAUTH
# - E-MAIL SETTINGS
# - ORGANIZATIONS
# - LICENSE (DEPRECATED) ************************************************************************************
# https://github.com/calcom/cal.com/blob/main/LICENSE
@@ -32,6 +33,8 @@ PRISMA_GENERATE_DATAPROXY=
# ***********************************************************************************************************
# - SHARED **************************************************************************************************
# Set this to http://app.cal.local:3000 if you want to enable organizations, and
# check variable ORGANIZATIONS_ENABLED at the bottom of this file
NEXT_PUBLIC_WEBAPP_URL='http://localhost:3000'
# Change to 'http://localhost:3001' if running the website simultaneously
NEXT_PUBLIC_WEBSITE_URL='http://localhost:3000'
@@ -183,3 +186,26 @@ CSP_POLICY=
EDGE_CONFIG=
NEXT_PUBLIC_MINUTES_TO_BOOK=5 # Minutes
# - ORGANIZATIONS *******************************************************************************************
# Enable Organizations non-prod domain setup, works in combination with organizations feature flag
# This is mainly needed locally, because for orgs to work a full domain name needs to point
# to the app, i.e. app.cal.local instead of using localhost, which is very disruptive
#
# This variable should only be set to 1 or true if you are in a non-prod environment and you want to
# use organizations
ORGANIZATIONS_ENABLED=
# Vercel Config to create subdomains for organizations
# Get it from https://vercel.com/<TEAM_OR_USER_NAME>/<PROJECT_SLUG>/settings
PROJECT_ID_VERCEL=
# Get it from: https://vercel.com/teams/<TEAM_SLUG>/settings
TEAM_ID_VERCEL=
# Get it from: https://vercel.com/account/tokens
AUTH_BEARER_TOKEN_VERCEL=
#Enables New booker for Embed only
NEW_BOOKER_ENABLED_FOR_EMBED=0
#Enables New booker for All but Embed requests
NEW_BOOKER_ENABLED_FOR_NON_EMBED=0
+3 -4
View File
@@ -8,8 +8,6 @@ Fixes # (issue)
Loom Video: https://www.loom.com/
-->
**Environment**: Staging(main branch) / Production
## Type of change
<!-- Please delete bullets that are not relevant. -->
@@ -27,13 +25,14 @@ Fixes # (issue)
- [ ] Test A
- [ ] 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 performed a self-review of my own code and corrected any misspellings
- I haven't commented my code, particularly in hard-to-understand areas
- I haven't checked if my PR needs changes to the documentation
- I haven't checked if my changes generate no new warnings
+1 -1
View File
@@ -8,7 +8,7 @@ runs:
using: "composite"
steps:
- name: Cache production build
uses: actions/cache@v3
uses: buildjet/cache@v3
id: cache-build
env:
cache-name: prod-build
+1 -1
View File
@@ -12,7 +12,7 @@ runs:
steps:
- name: Cache database
id: cache-db
uses: actions/cache@v3
uses: buildjet/cache@v3
env:
cache-name: cache-db
key-1: ${{ hashFiles('packages/prisma/schema.prisma', 'packages/prisma/migrations/**/**.sql', 'packages/prisma/*.ts') }}
+2 -2
View File
@@ -4,7 +4,7 @@ runs:
using: "composite"
steps:
- name: Create env file
uses: actions/cache@v3
uses: buildjet/cache@v3
id: env-cache
with:
path: gh.env
@@ -13,4 +13,4 @@ runs:
- name: Set Environment Variables
uses: tw3lveparsecs/github-actions-setvars@latest
with:
envFilePath: gh.env
envFilePath: gh.env
+12 -3
View File
@@ -20,7 +20,7 @@ runs:
using: "composite"
steps:
- name: Use Node ${{ inputs.node_version }}
uses: actions/setup-node@v3
uses: buildjet/setup-node@v3
with:
node-version: ${{ inputs.node_version }}
- name: Expose yarn config as "$GITHUB_OUTPUT"
@@ -32,7 +32,7 @@ runs:
# Yarn rotates the downloaded cache archives, @see https://github.com/actions/setup-node/issues/325
# Yarn cache is also reusable between arch and os.
- name: Restore yarn cache
uses: actions/cache@v3
uses: buildjet/cache@v3
id: yarn-download-cache
with:
path: ${{ steps.yarn-config.outputs.CACHE_FOLDER }}
@@ -40,10 +40,18 @@ runs:
restore-keys: |
yarn-download-cache-
# Invalidated on yarn.lock changes
- name: Restore node_modules
id: yarn-nm-cache
uses: buildjet/cache@v3
with:
path: "**/node_modules/"
key: ${{ runner.os }}-yarn-nm-cache-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}
# Invalidated on yarn.lock changes
- name: Restore yarn install state
id: yarn-install-state-cache
uses: actions/cache@v3
uses: buildjet/cache@v3
with:
path: .yarn/ci-cache/
key: ${{ runner.os }}-yarn-install-state-cache-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}
@@ -58,5 +66,6 @@ runs:
YARN_ENABLE_IMMUTABLE_INSTALLS: "false" # So it doesn't try to remove our private submodule deps
YARN_ENABLE_GLOBAL_CACHE: "false" # Use local cache folder to keep downloaded archives
YARN_INSTALL_STATE_PATH: .yarn/ci-cache/install-state.gz # Very small speedup when lock does not change
YARN_NM_MODE: "hardlinks-local" # Reduce node_modules size
# Other environment variables
HUSKY: "0" # By default do not run HUSKY install
@@ -5,7 +5,7 @@ runs:
steps:
- name: Cache playwright binaries
id: playwright-cache
uses: actions/cache@v2
uses: buildjet/cache@v2
with:
path: |
~/Library/Caches/ms-playwright
@@ -0,0 +1,16 @@
name: Add PRs to project Reviewing PRs
on:
pull_request:
types:
- opened
jobs:
add-PR-to-project:
name: Add PRs to project Reviewing PRs
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@v0.1.0
with:
project-url: https://github.com/orgs/calcom/projects/11
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
+64 -16
View File
@@ -1,27 +1,75 @@
name: "Apply issue labels to PR"
on:
pull_request:
types: [opened]
pull_request_target:
types:
- opened
jobs:
label_on_pr:
runs-on: ubuntu-latest
permissions:
contents: none
issues: read
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 16
- name: Apply labels from linked issue to PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_DATA: ${{ toJson(github.event.pull_request) }}
run: node ./.github/workflows/scripts/apply-issue-labels-to-pr.ts
uses: actions/github-script@v5
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
async function getLinkedIssues(owner, repo, prNumber) {
const query = `query GetLinkedIssues($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
closingIssuesReferences(first: 10) {
nodes {
number
labels(first: 10) {
nodes {
name
}
}
}
}
}
}
}`;
const variables = {
owner: owner,
repo: repo,
prNumber: prNumber,
};
const result = await github.graphql(query, variables);
return result.repository.pullRequest.closingIssuesReferences.nodes;
}
const pr = context.payload.pull_request;
const linkedIssues = await getLinkedIssues(
context.repo.owner,
context.repo.repo,
pr.number
);
const labelsToAdd = new Set();
for (const issue of linkedIssues) {
if (issue.labels && issue.labels.nodes) {
for (const label of issue.labels.nodes) {
labelsToAdd.add(label.name);
}
}
}
if (labelsToAdd.size) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: Array.from(labelsToAdd),
});
}
+1 -1
View File
@@ -6,7 +6,7 @@ on:
jobs:
cleanup:
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
- name: Check out code
uses: actions/checkout@v3
+1 -1
View File
@@ -5,7 +5,7 @@ env:
NODE_OPTIONS: "--max-old-space-size=8192"
jobs:
check-types:
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
+1 -1
View File
@@ -6,7 +6,7 @@ jobs:
e2e-app-store:
timeout-minutes: 20
name: E2E App-Store Apps
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
services:
postgres:
image: postgres:12.1
+4 -2
View File
@@ -5,7 +5,7 @@ on:
jobs:
e2e-embed:
timeout-minutes: 20
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
services:
postgres:
image: postgres:12.1
@@ -25,7 +25,9 @@ jobs:
- uses: ./.github/actions/cache-db
- uses: ./.github/actions/cache-build
- name: Run Tests
run: yarn test-e2e:embed-react
run: |
yarn test-e2e:embed-react
yarn workspace @calcom/embed-react packaged:tests
env:
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
- name: Upload Test Results
+1 -1
View File
@@ -5,7 +5,7 @@ on:
jobs:
e2e-embed:
timeout-minutes: 20
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
services:
postgres:
image: postgres:12.1
+8 -4
View File
@@ -4,8 +4,8 @@ on:
jobs:
e2e:
timeout-minutes: 20
name: E2E tests
runs-on: ubuntu-latest
name: E2E tests (${{ matrix.shard }}/${{ strategy.job-total }})
runs-on: buildjet-4vcpu-ubuntu-2204
services:
postgres:
image: postgres:12.1
@@ -14,6 +14,10 @@ jobs:
POSTGRES_DB: calendso
ports:
- 5432:5432
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
@@ -24,12 +28,12 @@ jobs:
- uses: ./.github/actions/cache-db
- uses: ./.github/actions/cache-build
- name: Run Tests
run: yarn test-e2e
run: yarn e2e --shard=${{ matrix.shard }}/${{ strategy.job-total }}
env:
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
- name: Upload Test Results
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name: test-results
name: test-results-${{ matrix.shard }}_${{ strategy.job-total }}
path: test-results
+3 -3
View File
@@ -20,7 +20,7 @@ env:
INPUT_ENV_PAYMENT_FEE_FIXED: 10
INPUT_ENV_SAML_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
INPUT_ENV_SAML_ADMINS: pro@example.com
INPUT_ENV_NEXTAUTH_URL: http://127.0.0.1:3000/api/auth
INPUT_ENV_NEXTAUTH_URL: http://localhost:3000
INPUT_ENV_NEXT_PUBLIC_IS_E2E: 1
# INPUT_ENV_EMAIL_FROM: e2e@cal.com
# INPUT_ENV_EMAIL_SERVER_HOST: ${{ secrets.CI_EMAIL_SERVER_HOST }}
@@ -36,14 +36,14 @@ env:
jobs:
create_env_file:
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
- uses: ozaytsev86/create-env-file@v1
with:
file-name: ${{ github.workspace }}/gh.env
- uses: actions/cache@v3
- uses: buildjet/cache@v3
id: env-cache
with:
path: gh.env
+2 -2
View File
@@ -3,13 +3,13 @@ on:
workflow_call:
jobs:
lint:
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
- uses: ./.github/actions/yarn-install
- name: Save Code Linting Reports
- name: Run Linting with Reports
run: yarn lint:report
continue-on-error: true
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
analyze:
needs: build
if: always()
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
+9 -2
View File
@@ -16,7 +16,8 @@ concurrency:
jobs:
changes:
runs-on: ubuntu-latest
name: Detect changes
runs-on: buildjet-4vcpu-ubuntu-2204
permissions:
pull-requests: read
outputs:
@@ -66,6 +67,12 @@ jobs:
uses: ./.github/workflows/production-build.yml
secrets: inherit
build-without-database:
name: Production build (without database)
needs: env
uses: ./.github/workflows/production-build-without-database.yml
secrets: inherit
e2e:
name: E2E tests
needs: [changes, lint, build]
@@ -101,7 +108,7 @@ jobs:
required:
needs: [lint, type-check, test, build, e2e]
if: always()
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
- name: fail if conditional jobs failed
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'skipped') || contains(needs.*.result, 'cancelled')
@@ -0,0 +1,16 @@
name: Production Build (without database)
on:
workflow_call:
jobs:
build:
name: Build
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/env-read-file
- uses: ./.github/actions/cache-build
+1 -1
View File
@@ -6,7 +6,7 @@ on:
jobs:
build:
name: Build
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
timeout-minutes: 30
services:
postgres:
@@ -1,139 +0,0 @@
const https = require("https");
async function applyLabelFromLinkedIssueToPR(pr, token) {
// Get the labels from issues linked to the PR
const query = `
query GetLinkedIssues($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
closingIssuesReferences(first: 10) {
nodes {
number
labels(first: 10) {
nodes {
name
}
}
}
}
}
}
}
`;
const graphqlData = JSON.stringify({
query,
variables: {
owner: pr.base.repo.owner.login,
repo: pr.base.repo.name,
prNumber: pr.number,
},
});
const requestOptions = {
hostname: "api.github.com",
path: "/graphql",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": graphqlData.length,
Authorization: "Bearer " + token,
"User-Agent": "Node.js",
},
};
// Use the native Node.js request library to make the request
let linkedIssues;
linkedIssues = await new Promise((resolve, reject) => {
const request = https.request(requestOptions, (response) => {
let responseBody = "";
response.on("data", (chunk) => {
responseBody += chunk;
});
response.on("end", () => {
resolve(JSON.parse(responseBody)?.data?.repository?.pullRequest?.closingIssuesReferences?.nodes);
});
});
request.on("error", (error) => {
reject(error);
});
request.write(graphqlData);
request.end();
});
if (!linkedIssues || linkedIssues.length === 0) {
console.log("No issue linked.");
return;
}
// Iterate over linked issues and apply labels to PR
for (const issue of linkedIssues) {
const labels = issue?.labels?.nodes?.map((label) => label.name);
if (!labels || labels.length === 0) {
console.log(`No labels found on the linked issue #${issue.number}.`);
continue;
}
const labelsData = JSON.stringify({
labels: labels,
});
const requestOptions = {
hostname: "api.github.com",
path: `/repos/${pr.base.repo.owner.login}/${pr.base.repo.name}/issues/${pr.number}/labels`,
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": labelsData.length,
Authorization: "Bearer " + token,
"User-Agent": "Node.js",
},
};
let labelResult;
labelResult = await new Promise((resolve, reject) => {
const request = https.request(requestOptions, (response) => {
let responseBody = "";
response.on("data", (chunk) => {
responseBody += chunk;
});
response.on("data", () => {});
response.on("end", () => {
resolve(JSON.parse(responseBody));
});
});
request.on("error", (error) => {
reject(error);
});
request.write(labelsData);
request.end();
});
if (labelResult.message) {
console.log(`Error labelling PR: ${labelResult.message}`);
continue;
}
console.log(
`Applied labels: ${labels.join(", ")} to PR #${pr.number} from linked issue #${issue.number}`
);
}
}
// Pass the PR data and GitHub token to the script
(async () => {
if (!process.env.PR_DATA) {
console.log("No PR data found.");
return;
}
const prData = JSON.parse(process.env.PR_DATA);
const token = process.env.GITHUB_TOKEN;
await applyLabelFromLinkedIssueToPR(prData, token);
})();
@@ -0,0 +1,21 @@
name: "Validate PRs"
on:
pull_request_target:
types:
- opened
- reopened
- edited
- synchronize
permissions:
pull-requests: read
jobs:
validate-pr:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -7,7 +7,7 @@ on:
jobs:
test:
timeout-minutes: 20
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
+1 -1
View File
@@ -6,7 +6,7 @@ on:
jobs:
setup:
name: Yarn install & cache
runs-on: ubuntu-latest
runs-on: buildjet-4vcpu-ubuntu-2204
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
+10 -2
View File
@@ -106,7 +106,7 @@ To get a local copy up and running, please follow these simple steps.
Here is what you need to be able to run Cal.com.
- Node.js (Version: >=15.x <17)
- Node.js (Version: >=18.x)
- PostgreSQL
- Yarn _(recommended)_
@@ -201,6 +201,14 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env
1. Set a 32 character random string in your `.env` file for the `CALENDSO_ENCRYPTION_KEY` (You can use a command like `openssl rand -base64 24` to generate one).
1. Set up the database using the Prisma schema (found in `packages/prisma/schema.prisma`)
In a development environment, run:
```sh
yarn workspace @calcom/prisma db-migrate
```
In a production environment, run:
```sh
yarn workspace @calcom/prisma db-deploy
```
@@ -416,7 +424,7 @@ Make sure to complete section "Obtaining the Google API Credentials". After the
following
1. Add extra redirect URL `<Cal.com URL>/api/auth/callback/google`
1. Under 'OAuth concent screen', click "PUBLISH APP"
1. Under 'OAuth consent screen', click "PUBLISH APP"
### Obtaining Microsoft Graph Client ID and Secret
-1
View File
@@ -1 +0,0 @@
module.exports = require("@calcom/config/eslint-preset");
+11 -1
View File
@@ -1,6 +1,6 @@
import { z } from "zod";
import { _BookingModel as Booking, _AttendeeModel, _UserModel } from "@calcom/prisma/zod";
import { _BookingModel as Booking, _AttendeeModel, _UserModel, _PaymentModel } from "@calcom/prisma/zod";
import { extendedBookingCreateBody, iso8601 } from "@calcom/prisma/zod-utils";
import { schemaQueryUserId } from "./shared/queryUserId";
@@ -49,6 +49,15 @@ export const schemaBookingReadPublic = Booking.extend({
locale: true,
})
.optional(),
payment: z
.array(
_PaymentModel.pick({
id: true,
success: true,
paymentOption: true,
})
)
.optional(),
}).pick({
id: true,
userId: true,
@@ -61,6 +70,7 @@ export const schemaBookingReadPublic = Booking.extend({
timeZone: true,
attendees: true,
user: true,
payment: true,
metadata: true,
status: true,
responses: true,
@@ -0,0 +1,20 @@
import { withValidation } from "next-validations";
import { z } from "zod";
import { baseApiParams } from "./baseApiParams";
// Extracted out as utility function so can be reused
// at different endpoints that require this validation.
export const schemaQueryAttendeeEmail = baseApiParams.extend({
attendeeEmail: z.string().email(),
});
export const schemaQuerySingleOrMultipleAttendeeEmails = z.object({
attendeeEmail: z.union([z.string().email(), z.array(z.string().email())]).optional(),
});
export const withValidQueryAttendeeEmail = withValidation({
schema: schemaQueryAttendeeEmail,
type: "Zod",
mode: "query",
});
-2
View File
@@ -11,10 +11,8 @@
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
"dev": "PORT=3002 next dev",
"lint": "eslint . --ignore-path .gitignore",
"lint:report": "eslint . --format json --output-file ../../lint-results/api.json",
"lint:fix": "eslint . --ext .ts,.js,.tsx,.jsx --fix",
"start": "PORT=3002 next start",
"test": "echo 'No tests yet' && exit 1",
"type-check": "tsc --pretty --noEmit"
},
"devDependencies": {
+24 -3
View File
@@ -2,9 +2,11 @@ import type { NextApiRequest } from "next";
import { z } from "zod";
import { getUserAvailability } from "@calcom/core/getUserAvailability";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server";
import { availabilityUserSelect } from "@calcom/prisma";
import { MembershipRole } from "@calcom/prisma/enums";
import { stringOrNumber } from "@calcom/prisma/zod-utils";
/**
@@ -97,6 +99,9 @@ import { stringOrNumber } from "@calcom/prisma/zod-utils";
* 404:
* description: User not found | Team not found | Team has no members
*/
interface MemberRoles {
[userId: number | string]: MembershipRole;
}
const availabilitySchema = z
.object({
@@ -113,10 +118,12 @@ const availabilitySchema = z
);
async function handler(req: NextApiRequest) {
const { prisma, isAdmin } = req;
const { prisma, isAdmin, userId: reqUserId } = req;
const { username, userId, eventTypeId, dateTo, dateFrom, teamId } = availabilitySchema.parse(req.query);
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(req.headers.host ?? "");
if (!teamId)
return getUserAvailability({
orgSlug: isValidOrgDomain ? currentOrgDomain || undefined : undefined,
username,
dateFrom,
dateTo,
@@ -129,12 +136,26 @@ async function handler(req: NextApiRequest) {
});
if (!team) throw new HttpError({ statusCode: 404, message: "teamId not found" });
if (!team.members) throw new HttpError({ statusCode: 404, message: "team has no members" });
const allMemberIds = team.members.map((membership) => membership.userId);
const allMemberIds = team.members.reduce((allMemberIds: number[], member) => {
if (member.accepted) {
allMemberIds.push(member.userId);
}
return allMemberIds;
}, []);
const members = await prisma.user.findMany({
where: { id: { in: allMemberIds } },
select: availabilityUserSelect,
});
if (!isAdmin) throw new HttpError({ statusCode: 403, message: "Forbidden" });
const memberRoles: MemberRoles = team.members.reduce((acc: MemberRoles, membership) => {
acc[membership.userId] = membership.role;
return acc;
}, {} as MemberRoles);
// check if the user is a team Admin or Owner, if it is a team request, or a system Admin
const isUserAdminOrOwner =
memberRoles[reqUserId] == MembershipRole.ADMIN ||
memberRoles[reqUserId] == MembershipRole.OWNER ||
isAdmin;
if (!isUserAdminOrOwner) throw new HttpError({ statusCode: 403, message: "Forbidden" });
const availabilities = members.map(async (user) => {
return {
userId: user.id,
+44 -19
View File
@@ -32,40 +32,65 @@ import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransform
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/ArrayOfBookings"
* $ref: "#/components/schemas/Booking"
* examples:
* bookings:
* value: [
* booking:
* value:
* {
* "id": 1,
* "description": "Meeting with John",
* "eventTypeId": 2,
* "uid": "a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8",
* "title": "Business Meeting",
* "startTime": "2023-04-20T10:00:00.000Z",
* "endTime": "2023-04-20T11:00:00.000Z",
* "timeZone": "Europe/London",
* "attendees": [
* {
* "email": "example@cal.com",
* "name": "John Doe",
* "timeZone": "Europe/London",
* "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"
* }
* }
* ]
* }
* }
* ]
* 401:
* description: Authorization information is missing or invalid.
* 404:
* description: Booking was not found
*/
export async function getHandler(req: NextApiRequest) {
const { prisma, query } = req;
const { id } = schemaQueryIdParseInt.parse(query);
const booking = await prisma.booking.findUnique({
where: { id },
include: { attendees: true, user: true },
include: { attendees: true, user: true, payment: true },
});
return { booking: schemaBookingReadPublic.parse(booking) };
}
+128 -48
View File
@@ -5,6 +5,7 @@ import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server";
import { schemaBookingReadPublic } from "~/lib/validations/booking";
import { schemaQuerySingleOrMultipleAttendeeEmails } from "~/lib/validations/shared/queryAttendeeEmail";
import { schemaQuerySingleOrMultipleUserIds } from "~/lib/validations/shared/queryUserId";
/**
@@ -31,6 +32,19 @@ import { schemaQuerySingleOrMultipleUserIds } from "~/lib/validations/shared/que
* items:
* type: integer
* example: [2, 3, 4]
* - in: query
* name: attendeeEmail
* required: false
* schema:
* oneOf:
* - type: string
* format: email
* example: john.doe@example.com
* - type: array
* items:
* type: string
* format: email
* example: [john.doe@example.com, jane.doe@example.com]
* operationId: listBookings
* tags:
* - bookings
@@ -45,22 +59,47 @@ import { schemaQuerySingleOrMultipleUserIds } from "~/lib/validations/shared/que
* bookings:
* value: [
* {
* "id": 1,
* "description": "Meeting with John",
* "eventTypeId": 2,
* "uid": "a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8",
* "title": "Business Meeting",
* "startTime": "2023-04-20T10:00:00.000Z",
* "endTime": "2023-04-20T11:00:00.000Z",
* "timeZone": "Europe/London",
* "attendees": [
* {
* "email": "example@cal.com",
* "name": "John Doe",
* "timeZone": "Europe/London",
* "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"
* }
* }
* ]
* }
* }
* ]
* 401:
@@ -69,26 +108,42 @@ import { schemaQuerySingleOrMultipleUserIds } from "~/lib/validations/shared/que
* description: No bookings were found
*/
async function handler(req: NextApiRequest) {
const { userId, isAdmin, prisma } = req;
const args: Prisma.BookingFindManyArgs = {};
args.include = {
attendees: true,
user: true,
};
/** Only admins can query other users */
if (isAdmin && req.query.userId) {
const query = schemaQuerySingleOrMultipleUserIds.parse(req.query);
const userIds = Array.isArray(query.userId) ? query.userId : [query.userId || userId];
const users = await prisma.user.findMany({
where: { id: { in: userIds } },
select: { email: true },
});
const userEmails = users.map((u) => u.email);
args.where = {
/**
* Constructs the WHERE clause for Prisma booking findMany operation.
*
* @param userId - The ID of the user making the request. This is used to filter bookings where the user is either the host or an attendee.
* @param attendeeEmails - An array of emails provided in the request for filtering bookings by attendee emails, used in case of Admin calls.
* @param userIds - An array of user IDs to be included in the filter. Defaults to an empty array, and an array of user IDs in case of Admin call containing it.
* @param userEmails - An array of user emails to be included in the filter if it is an Admin call and contains userId in query parameter. Defaults to an empty array.
*
* @returns An object that represents the WHERE clause for the findMany/findUnique operation.
*/
function buildWhereClause(
userId: number,
attendeeEmails: string[],
userIds: number[] = [],
userEmails: string[] = []
) {
const filterByAttendeeEmails = attendeeEmails.length > 0;
const userFilter = userIds.length > 0 ? { userId: { in: userIds } } : { userId };
let whereClause = {};
if (filterByAttendeeEmails) {
whereClause = {
AND: [
userFilter,
{
attendees: {
some: {
email: { in: attendeeEmails },
},
},
},
],
};
} else {
whereClause = {
OR: [
{ userId: { in: userIds } },
userFilter,
{
attendees: {
some: {
@@ -98,7 +153,45 @@ async function handler(req: NextApiRequest) {
},
],
};
} else if (!isAdmin) {
}
return {
...whereClause,
};
}
async function handler(req: NextApiRequest) {
const { userId, isAdmin, prisma } = req;
const args: Prisma.BookingFindManyArgs = {};
args.include = {
attendees: true,
user: true,
payment: true,
};
const queryFilterForAttendeeEmails = schemaQuerySingleOrMultipleAttendeeEmails.parse(req.query);
const attendeeEmails = Array.isArray(queryFilterForAttendeeEmails.attendeeEmail)
? queryFilterForAttendeeEmails.attendeeEmail
: typeof queryFilterForAttendeeEmails.attendeeEmail === "string"
? [queryFilterForAttendeeEmails.attendeeEmail]
: [];
const filterByAttendeeEmails = attendeeEmails.length > 0;
/** Only admins can query other users */
if (isAdmin) {
if (req.query.userId) {
const query = schemaQuerySingleOrMultipleUserIds.parse(req.query);
const userIds = Array.isArray(query.userId) ? query.userId : [query.userId || userId];
const users = await prisma.user.findMany({
where: { id: { in: userIds } },
select: { email: true },
});
const userEmails = users.map((u) => u.email);
args.where = buildWhereClause(userId, attendeeEmails, userIds, userEmails);
} else if (filterByAttendeeEmails) {
args.where = buildWhereClause(userId, attendeeEmails, [], []);
}
} else {
const user = await prisma.user.findUnique({
where: { id: userId },
select: {
@@ -108,20 +201,7 @@ async function handler(req: NextApiRequest) {
if (!user) {
throw new HttpError({ message: "User not found", statusCode: 500 });
}
args.where = {
OR: [
{
userId,
},
{
attendees: {
some: {
email: user.email,
},
},
},
],
};
args.where = buildWhereClause(userId, attendeeEmails, [], []);
}
const data = await prisma.booking.findMany(args);
return { bookings: data.map((booking) => schemaBookingReadPublic.parse(booking)) };
-1
View File
@@ -26,7 +26,6 @@ import { defaultResponder } from "@calcom/lib/server";
* required:
* - eventTypeId
* - start
* - end
* - name
* - email
* - timeZone
+19
View File
@@ -98,6 +98,25 @@ const swaggerHandler = withSwagger({
},
},
},
payment: {
type: Array,
items: {
properties: {
id: {
type: "number",
example: 1,
},
success: {
type: "boolean",
example: true,
},
paymentOption: {
type: "string",
example: "ON_BOOKING",
},
},
},
},
},
},
},
+27
View File
@@ -0,0 +1,27 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server";
import { createContext } from "@calcom/trpc/server/createContext";
import { viewerRouter } from "@calcom/trpc/server/routers/viewer/_router";
import { TRPCError } from "@trpc/server";
import { getHTTPStatusCodeFromError } from "@trpc/server/http";
async function handler(req: NextApiRequest, res: NextApiResponse) {
/** @see https://trpc.io/docs/server-side-calls */
const ctx = await createContext({ req, res });
const caller = viewerRouter.createCaller(ctx);
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return await caller.slots.getSchedule(req.query as any /* Let tRPC handle this */);
} catch (cause) {
if (cause instanceof TRPCError) {
const statusCode = getHTTPStatusCodeFromError(cause);
throw new HttpError({ statusCode, message: cause.message });
}
throw cause;
}
}
export default defaultResponder(handler);
+9
View File
@@ -0,0 +1,9 @@
import { defaultHandler } from "@calcom/lib/server";
import { withMiddleware } from "~/lib/helpers/withMiddleware";
export default withMiddleware()(
defaultHandler({
GET: import("./_get"),
})
);
+5 -4
View File
@@ -1,19 +1,20 @@
import type { Request, Response } from "express";
import type { NextApiRequest, NextApiResponse } from "next";
import { createMocks } from "node-mocks-http";
import { describe, expect, test, vi } from "vitest";
import dayjs from "@calcom/dayjs";
import sendPayload from "@calcom/features/webhooks/lib/sendPayload";
import { buildBooking, buildEventType, buildWebhook } from "@calcom/lib/test/builder";
import prisma from "@calcom/prisma";
import { prismaMock } from "../../../../../tests/config/singleton";
import prismaMock from "../../../../../tests/libs/__mocks__/prisma";
import handler from "../../../pages/api/bookings/_post";
type CustomNextApiRequest = NextApiRequest & Request;
type CustomNextApiResponse = NextApiResponse & Response;
jest.mock("@calcom/features/webhooks/lib/sendPayload");
jest.mock("@calcom/lib/server/i18n", () => {
vi.mock("@calcom/features/webhooks/lib/sendPayload");
vi.mock("@calcom/lib/server/i18n", () => {
return {
getTranslation: (key: string) => key,
};
@@ -151,7 +152,7 @@ describe("POST /api/bookings", () => {
});
});
xdescribe("Success", () => {
describe("Success", () => {
describe("Regular event-type", () => {
test("Creates one single booking", async () => {
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
+1 -1
View File
@@ -50,6 +50,6 @@
"storybook-react-i18next": "^1.1.2",
"tailwindcss": "^3.2.1",
"typescript": "^4.9.4",
"vite": "^2.9.15"
"vite": "^4.1.2"
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ export const SnippedGenerator = {
// extend some internals to gain information about current path, method and spec in the generator function metioned later
spec: {
wrapSelectors: {
requestFor: (ori, system) => (state, path, method) => {
requestFor: (ori) => (state, path, method) => {
return ori(path, method)
?.set("spec", state.get("json", {}))
?.setIn(["oasPathMethod", "path"], path)
+18
View File
@@ -1,5 +1,23 @@
# @calcom/web
## 2.9.4
### Patch Changes
- Updated dependencies
- @calcom/embed-snippet@1.0.9
- @calcom/embed-react@1.1.1
- @calcom/embed-core@1.2.1
## 2.9.3
### Patch Changes
- Updated dependencies
- @calcom/embed-react@1.1.0
- @calcom/embed-core@1.2.0
- @calcom/embed-snippet@1.0.8
## 2.7.16
### Patch Changes
+1
View File
@@ -60,6 +60,7 @@ export default function AppListCard(props: AppListCardProps) {
timeoutRef.current = null;
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
+45 -8
View File
@@ -8,8 +8,11 @@ import { createRef, forwardRef, useRef, useState } from "react";
import type { ControlProps } from "react-select";
import { components } from "react-select";
import type { BookerLayout } from "@calcom/features/bookings/Booker/types";
import { useFlagMap } from "@calcom/features/flags/context/provider";
import { APP_NAME, EMBED_LIB_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { BookerLayouts } from "@calcom/prisma/zod-utils";
import {
Button,
Dialog,
@@ -41,12 +44,13 @@ type PreviewState = {
height: string;
};
theme: Theme;
floatingPopup: Record<string, string>;
floatingPopup: Record<string, string | boolean | undefined>;
elementClick: Record<string, string>;
palette: {
brandColor: string;
};
hideEventTypeDetails: boolean;
layout: BookerLayouts;
};
const queryParamsForDialog = ["embedType", "embedTabName", "embedUrl"];
@@ -129,11 +133,13 @@ const getEmbedUIInstructionString = ({
theme,
brandColor,
hideEventTypeDetails,
layout,
}: {
apiName: string;
theme?: string;
brandColor: string;
hideEventTypeDetails: boolean;
layout?: string;
}) => {
theme = theme !== "auto" ? theme : undefined;
return getInstructionString({
@@ -147,6 +153,7 @@ const getEmbedUIInstructionString = ({
},
},
hideEventTypeDetails: hideEventTypeDetails,
layout,
},
});
};
@@ -260,6 +267,7 @@ const getEmbedTypeSpecificString = ({
theme: PreviewState["theme"];
brandColor: string;
hideEventTypeDetails: boolean;
layout?: BookerLayout;
};
if (embedFramework === "react") {
uiInstructionStringArg = {
@@ -267,6 +275,7 @@ const getEmbedTypeSpecificString = ({
theme: previewState.theme,
brandColor: previewState.palette.brandColor,
hideEventTypeDetails: previewState.hideEventTypeDetails,
layout: previewState.layout,
};
} else {
uiInstructionStringArg = {
@@ -274,6 +283,7 @@ const getEmbedTypeSpecificString = ({
theme: previewState.theme,
brandColor: previewState.palette.brandColor,
hideEventTypeDetails: previewState.hideEventTypeDetails,
layout: previewState.layout,
};
}
if (!frameworkCodes[embedType]) {
@@ -626,6 +636,7 @@ const ThemeSelectControl = ({ children, ...props }: ControlProps<{ value: Theme;
const ChooseEmbedTypesDialogContent = () => {
const { t } = useLocale();
const router = useRouter();
return (
<DialogContent className="rounded-lg p-10" type="creation" size="lg">
<div className="mb-2">
@@ -671,6 +682,8 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
const router = useRouter();
const iframeRef = useRef<HTMLIFrameElement>(null);
const dialogContentRef = useRef<HTMLDivElement>(null);
const flags = useFlagMap();
const isBookerLayoutsEnabled = flags["booker-layouts"] === true;
const s = (href: string) => {
const searchParams = new URLSearchParams(router.asPath.split("?")[1] || "");
@@ -691,7 +704,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
const [isEmbedCustomizationOpen, setIsEmbedCustomizationOpen] = useState(true);
const [isBookingCustomizationOpen, setIsBookingCustomizationOpen] = useState(true);
const [previewState, setPreviewState] = useState({
const [previewState, setPreviewState] = useState<PreviewState>({
inline: {
width: "100%",
height: "100%",
@@ -703,6 +716,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
palette: {
brandColor: "#000000",
},
layout: BookerLayouts.MONTH_VIEW,
});
const close = () => {
@@ -765,6 +779,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
arg: {
theme: previewState.theme,
hideEventTypeDetails: previewState.hideEventTypeDetails,
layout: previewState.layout,
styles: {
branding: {
...previewState.palette,
@@ -798,6 +813,12 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
{ value: Theme.light, label: "Light Theme" },
];
const layoutOptions = [
{ value: BookerLayouts.MONTH_VIEW, label: t("bookerlayout_month_view") },
{ value: BookerLayouts.WEEK_VIEW, label: t("bookerlayout_week_view") },
{ value: BookerLayouts.COLUMN_VIEW, label: t("bookerlayout_column_view") },
];
const FloatingPopupPositionOptions = [
{
value: "bottom-right",
@@ -1070,6 +1091,27 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
</div>
</Label>
))}
{isBookerLayoutsEnabled && (
<Label className="mb-6">
<div className="mb-2">{t("layout")}</div>
<Select
className="w-full"
defaultValue={layoutOptions[0]}
onChange={(option) => {
if (!option) {
return;
}
setPreviewState((previewState) => {
return {
...previewState,
layout: option.value,
};
});
}}
options={layoutOptions}
/>
</Label>
)}
</div>
</CollapsibleContent>
</Collapsible>
@@ -1133,12 +1175,7 @@ export const EmbedDialog = () => {
const router = useRouter();
const embedUrl: string = router.query.embedUrl as string;
return (
<Dialog
name="embed"
clearQueryParamsOnClose={queryParamsForDialog}
onOpenChange={(open) => {
if (!open) window.resetEmbedStatus();
}}>
<Dialog name="embed" clearQueryParamsOnClose={queryParamsForDialog}>
{!router.query.embedType ? (
<ChooseEmbedTypesDialogContent />
) : (
-51
View File
@@ -1,51 +0,0 @@
import dynamic from "next/dynamic";
import type { Dispatch } from "react";
import { useState, useEffect } from "react";
import type { JSONObject } from "superjson/dist/types";
export type Gate = undefined | "rainbow"; // Add more like ` | "geolocation" | "payment"`
export type GateState = {
rainbowToken?: string;
};
type GateProps = {
children: React.ReactNode;
gates: Gate[];
appData: JSONObject;
dispatch: Dispatch<Partial<GateState>>;
};
const RainbowGate = dynamic(() => import("@calcom/app-store/rainbow/components/RainbowKit"));
// To add a new Gate just add the gate logic to the switch statement
const Gates: React.FC<GateProps> = ({ children, gates, appData, dispatch }) => {
const [rainbowToken, setRainbowToken] = useState<string>();
useEffect(() => {
dispatch({ rainbowToken });
}, [rainbowToken, dispatch]);
let gateWrappers = <>{children}</>;
// Recursively wraps the `gateWrappers` with new gates allowing for multiple gates
for (const gate of gates) {
switch (gate) {
case "rainbow":
if (appData.blockchainId && appData.smartContractAddress && !rainbowToken) {
gateWrappers = (
<RainbowGate
setToken={setRainbowToken}
chainId={appData.blockchainId as number}
tokenAddress={appData.smartContractAddress as string}>
{gateWrappers}
</RainbowGate>
);
}
}
}
return gateWrappers;
};
export default Gates;
+26 -2
View File
@@ -1,10 +1,13 @@
import { DefaultSeo } from "next-seo";
import { Inter } from "next/font/google";
import localFont from "next/font/local";
import Head from "next/head";
import Script from "next/script";
import "@calcom/embed-core/src/embed-iframe";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { buildCanonical } from "@calcom/lib/next-seo.config";
import type { AppProps } from "@lib/app-providers";
import AppProviders from "@lib/app-providers";
@@ -13,7 +16,7 @@ import { seoConfig } from "@lib/config/next-seo.config";
import I18nLanguageHandler from "@components/I18nLanguageHandler";
export interface CalPageWrapper {
(props?: any): JSX.Element;
(props?: AppProps): JSX.Element;
PageWrapper?: AppProps["Component"]["PageWrapper"];
}
@@ -49,9 +52,30 @@ function PageWrapper(props: AppProps) {
};
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => page);
// Canonical: Check if the URL is from cal.com so that we set the canonical conditionally
const isCalcom =
WEBAPP_URL &&
(new URL(WEBAPP_URL).hostname.endsWith("cal.com") || new URL(WEBAPP_URL).hostname.endsWith("cal.dev"));
const path = router.asPath;
return (
<AppProviders {...providerProps}>
<DefaultSeo {...seoConfig.defaultNextSeo} />
<Head>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
/>
</Head>
<DefaultSeo
// Set canonical to https://cal.com or self-hosted URL
canonical={
isCalcom
? buildCanonical({ path, origin: "https://cal.com" }) // cal.com & .dev
: buildCanonical({ path, origin: WEBAPP_URL }) // self-hosted
}
{...seoConfig.defaultNextSeo}
/>
<I18nLanguageHandler />
<Script
nonce={nonce}
@@ -1,3 +1,5 @@
import type { FunctionComponent, SVGProps } from "react";
import { InstallAppButton } from "@calcom/app-store/components";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
@@ -55,7 +57,7 @@ const AdditionalCalendarSelector = ({ isLoading }: AdditionalCalendarSelectorPro
<InstallAppButton
type={data.type}
render={(installProps) => {
const props = { ...installProps } as any;
const props = { ...installProps } as FunctionComponent<SVGProps<SVGSVGElement>>;
return (
<DropdownItem {...props} color="minimal" type="button">
<span className="flex items-center gap-x-2">
+5 -2
View File
@@ -129,7 +129,10 @@ const Component = ({
className="bg-subtle text-emphasis rounded-md p-1 text-xs capitalize">
{categories[0]}
</Link>{" "}
• {t("published_by", { author })}
•{" "}
<a target="_blank" rel="noreferrer" href={website}>
{t("published_by", { author })}
</a>
</h2>
{isTemplate && (
<Badge variant="red" className="mt-4">
@@ -257,7 +260,7 @@ const Component = ({
)}
</span>
<h4 className="text-emphasis mt-8 mb-2 font-semibold ">{t("learn_more")}</h4>
<h4 className="text-emphasis mt-8 mb-2 font-semibold ">{t("contact")}</h4>
<ul className="prose-sm -ml-1 -mr-1 leading-5">
{docs && (
<li>
@@ -2,60 +2,12 @@ import type { ComponentProps } from "react";
import React from "react";
import AppCategoryNavigation from "@calcom/app-store/_components/AppCategoryNavigation";
import type { InstalledAppVariants } from "@calcom/app-store/utils";
import Shell from "@calcom/features/shell/Shell";
import { trpc } from "@calcom/trpc/react";
import type { HorizontalTabItemProps, VerticalTabItemProps } from "@calcom/ui";
import { BarChart, Calendar, CreditCard, Grid, Share2, Video } from "@calcom/ui/components/icon";
const tabs: (VerticalTabItemProps | HorizontalTabItemProps)[] = [
{
name: "calendar",
href: "/apps/installed/calendar",
icon: Calendar,
},
{
name: "conferencing",
href: "/apps/installed/conferencing",
icon: Video,
},
{
name: "payment",
href: "/apps/installed/payment",
icon: CreditCard,
},
{
name: "automation",
href: "/apps/installed/automation",
icon: Share2,
},
{
name: "analytics",
href: "/apps/installed/analytics",
icon: BarChart,
},
{
name: "other",
href: "/apps/installed/other",
icon: Grid,
},
];
export default function InstalledAppsLayout({
children,
...rest
}: { children: React.ReactNode } & ComponentProps<typeof Shell>) {
const variant: (typeof InstalledAppVariants)[number] = "payment";
const query = trpc.viewer.integrations.useQuery({
variant,
onlyInstalled: true,
});
let actualTabs = tabs;
if (query.data?.items.length === 0) {
actualTabs = tabs.filter((tab) => tab.name !== variant);
}
return (
<Shell {...rest} hideHeadingOnMobile>
<AppCategoryNavigation baseURL="/apps/installed" containerClassname="min-w-0 w-full">
+15 -15
View File
@@ -26,24 +26,24 @@ export default function TwoFactor({ center = true }) {
return (
<div className={center ? "mx-auto !mt-0 max-w-sm" : "!mt-0 max-w-sm"}>
<Label className="mt-4"> {t("2fa_code")}</Label>
<Label className="mt-4">{t("2fa_code")}</Label>
<p className="text-subtle mb-4 text-sm">{t("2fa_enabled_instructions")}</p>
<input hidden type="hidden" value={value} {...methods.register("totpCode")} />
<input type="hidden" value={value} {...methods.register("totpCode")} />
<div className="flex flex-row justify-between">
<Input
className={className}
name="2fa1"
inputMode="decimal"
{...digits[0]}
autoFocus
autoComplete="one-time-code"
/>
<Input className={className} name="2fa2" inputMode="decimal" {...digits[1]} />
<Input className={className} name="2fa3" inputMode="decimal" {...digits[2]} />
<Input className={className} name="2fa4" inputMode="decimal" {...digits[3]} />
<Input className={className} name="2fa5" inputMode="decimal" {...digits[4]} />
<Input className={className} name="2fa6" inputMode="decimal" {...digits[5]} />
{digits.map((digit, index) => (
<Input
key={`2fa${index}`}
className={className}
name={`2fa${index + 1}`}
inputMode="decimal"
{...digit}
autoFocus={index === 0}
autoComplete="one-time-code"
/>
))}
</div>
</div>
);
@@ -24,7 +24,7 @@ export function AvailableEventLocations({ locations }: { locations: Props["event
}
const translateAbleKeys = [
"attendee_in_person",
"in_person_attendee_address",
"in_person",
"attendee_phone_number",
"link_meeting",
@@ -28,7 +28,6 @@ type AvailableTimesProps = {
bookingAttendees?: number | null;
slots?: Slot[];
isLoading: boolean;
ethSignature?: string;
duration: number;
};
@@ -43,7 +42,6 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
onTimeFormatChange,
seatsPerTimeSlot,
bookingAttendees,
ethSignature,
duration,
}) => {
const reserveSlotMutation = trpc.viewer.public.slots.reserveSlot.useMutation();
@@ -119,7 +117,6 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
timeFormat,
/** Treat as recurring only when a count exist and it's not a rescheduling workflow */
count: recurringCount && !rescheduleUid ? recurringCount : undefined,
...(ethSignature ? { ethSignature } : {}),
},
};
@@ -164,7 +161,8 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
brand === "#fff" || brand === "#ffffff" ? "" : ""
)}
onClick={() => reserveSlot(slot)}
data-testid="time">
data-testid="time"
data-disabled="false">
{dayjs(slot.time).tz(timeZone()).format(timeFormat)}
{!!seatsPerTimeSlot && (
<p
@@ -83,7 +83,9 @@ const BookingDescription: FC<Props> = (props) => {
size="sm"
truncateAfter={3}
/>
<h2 className="text-default mt-1 mb-2 break-words text-sm font-medium ">{profile.name}</h2>
<h2 className="text-default mt-1 mb-2 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">
{eventType.title}
</h1>
@@ -123,7 +123,8 @@ function BookingListItem(booking: BookingItemProps) {
disabled: mutation.isLoading,
},
// For bookings with payment, only confirm if the booking is paid for
...((isPending && !booking?.eventType?.price) || (!!booking?.eventType?.price && booking.paid)
...((isPending && !paymentAppData.enabled) ||
(paymentAppData.enabled && !!paymentAppData.price && booking.paid)
? [
{
id: "confirm",
@@ -318,6 +319,7 @@ function BookingListItem(booking: BookingItemProps) {
<DialogClose />
<Button
disabled={mutation.isLoading}
data-testid="rejection-confirm"
onClick={() => {
bookingConfirm(false);
}}>
@@ -59,6 +59,7 @@ export default function CancelBooking(props: Props) {
<div className="mt-5 sm:mt-6">
<label className="text-default font-medium">{t("cancellation_reason")}</label>
<TextArea
data-testid="cancel_reason"
ref={cancelBookingRef}
placeholder={t("cancellation_reason_placeholder")}
value={cancellationReason}
@@ -75,7 +76,7 @@ export default function CancelBooking(props: Props) {
{t("nevermind")}
</Button>
<Button
data-testid="cancel"
data-testid="confirm_cancel"
onClick={async () => {
setLoading(true);
@@ -80,7 +80,6 @@ export const SlotPicker = ({
seatsPerTimeSlot,
bookingAttendees,
weekStart = 0,
ethSignature,
}: {
eventType: Pick<
EventType & { metadata: z.infer<typeof EventTypeMetaDataSchema> },
@@ -94,7 +93,6 @@ export const SlotPicker = ({
recurringEventCount?: number;
users: string[];
weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
ethSignature?: string;
}) => {
const [selectedDate, setSelectedDate] = useState<Dayjs>();
const [browsingDate, setBrowsingDate] = useState<Dayjs>();
@@ -193,7 +191,6 @@ export const SlotPicker = ({
seatsPerTimeSlot={seatsPerTimeSlot}
bookingAttendees={bookingAttendees}
recurringCount={recurringEventCount}
ethSignature={ethSignature}
duration={parseInt(duration)}
/>
</>
@@ -1,10 +1,9 @@
import dynamic from "next/dynamic";
import { useRouter } from "next/router";
import { useEffect, useMemo, useReducer, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { z } from "zod";
import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager";
import { getEventTypeAppData } from "@calcom/app-store/utils";
import dayjs from "@calcom/dayjs";
import {
useEmbedNonStylesConfig,
@@ -27,8 +26,6 @@ import { CreditCard, User, RefreshCcw } from "@calcom/ui/components/icon";
import { timeZone as localStorageTimeZone } from "@lib/clock";
import type { Gate, GateState } from "@components/Gates";
import Gates from "@components/Gates";
import BookingDescription from "@components/booking/BookingDescription";
import { SlotPicker } from "@components/booking/SlotPicker";
@@ -89,14 +86,6 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
setIs24hClockInLocalStorage(is24Hours);
};
const [gateState, gateDispatcher] = useReducer(
(state: GateState, newState: Partial<GateState>) => ({
...state,
...newState,
}),
{}
);
useEffect(() => {
setTimeZone(localStorageTimeZone() || dayjs.tz.guess());
}, []);
@@ -124,20 +113,11 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
);
const paymentAppData = getPaymentAppData(eventType);
const rainbowAppData = getEventTypeAppData(eventType, "rainbow") || {};
const rawSlug = profile.slug ? profile.slug.split("/") : [];
if (rawSlug.length > 1) rawSlug.pop(); //team events have team name as slug, but user events have [user]/[type] as slug.
const showEventTypeDetails = (isEmbed && !embedUiConfig.hideEventTypeDetails) || !isEmbed;
// Define conditional gates here
const gates = [
// Rainbow gate is only added if the event has both a `blockchainId` and a `smartContractAddress`
rainbowAppData && rainbowAppData.blockchainId && rainbowAppData.smartContractAddress
? ("rainbow" as Gate)
: undefined,
];
const { data: bookingAttendees } = trpc.viewer.bookings.getBookingAttendees.useQuery(
{
seatReferenceUid: rescheduleUid,
@@ -148,7 +128,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
);
return (
<Gates gates={gates} appData={rainbowAppData} dispatch={gateDispatcher}>
<>
<HeadSeo
title={`${rescheduleUid ? t("reschedule") : ""} ${eventType.title} | ${profile.name}`}
description={`${rescheduleUid ? t("reschedule") : ""} ${eventType.title}`}
@@ -296,7 +276,6 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
seatsPerTimeSlot={eventType.seatsPerTimeSlot || undefined}
bookingAttendees={bookingAttendees || undefined}
recurringEventCount={recurringEventCount}
ethSignature={gateState.rainbowToken}
/>
</div>
</div>
@@ -306,7 +285,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
</main>
</div>
<Toaster position="bottom-right" />
</Gates>
</>
);
};
@@ -4,7 +4,7 @@ import { useSession } from "next-auth/react";
import dynamic from "next/dynamic";
import Head from "next/head";
import { useRouter } from "next/router";
import { useEffect, useMemo, useReducer, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { useForm, useFormContext } from "react-hook-form";
import { v4 as uuidv4 } from "uuid";
import { z } from "zod";
@@ -12,7 +12,6 @@ import { z } from "zod";
import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager";
import type { EventLocationType } from "@calcom/app-store/locations";
import { createPaymentLink } from "@calcom/app-store/stripepayment/lib/client";
import { getEventTypeAppData } from "@calcom/app-store/utils";
import type { LocationObject } from "@calcom/core/location";
import dayjs from "@calcom/dayjs";
import {
@@ -51,8 +50,6 @@ import { AlertTriangle, Calendar, RefreshCw, User } from "@calcom/ui/components/
import { timeZone } from "@lib/clock";
import useRouterQuery from "@lib/hooks/useRouterQuery";
import type { Gate, GateState } from "@components/Gates";
import Gates from "@components/Gates";
import BookingDescription from "@components/booking/BookingDescription";
import type { BookPageProps } from "../../../pages/[user]/book";
@@ -224,13 +221,6 @@ const BookingPage = ({
const { data: session } = useSession();
const isBackgroundTransparent = useIsBackgroundTransparent();
const telemetry = useTelemetry();
const [gateState, gateDispatcher] = useReducer(
(state: GateState, newState: Partial<GateState>) => ({
...state,
...newState,
}),
{}
);
const { timezone } = useTimePreferences();
@@ -488,7 +478,6 @@ const BookingPage = ({
metadata,
hasHashedBookingLink,
hashedLink,
ethSignature: gateState.rainbowToken,
}));
recurringMutation.mutate(recurringBookings);
} else {
@@ -506,25 +495,15 @@ const BookingPage = ({
metadata,
hasHashedBookingLink,
hashedLink,
ethSignature: gateState.rainbowToken,
seatReferenceUid: router.query.seatReferenceUid as string,
});
}
};
const showEventTypeDetails = (isEmbed && !embedUiConfig.hideEventTypeDetails) || !isEmbed;
const rainbowAppData = getEventTypeAppData(eventType, "rainbow") || {};
// Define conditional gates here
const gates = [
// Rainbow gate is only added if the event has both a `blockchainId` and a `smartContractAddress`
rainbowAppData && rainbowAppData.blockchainId && rainbowAppData.smartContractAddress
? ("rainbow" as Gate)
: undefined,
];
return (
<Gates gates={gates} appData={rainbowAppData} dispatch={gateDispatcher}>
<>
<Head>
<title>
{rescheduleUid
@@ -686,7 +665,7 @@ const BookingPage = ({
</div>
</main>
<Toaster position="bottom-right" />
</Gates>
</>
);
};
@@ -57,12 +57,15 @@ const LocationInput = (props: {
<Input {...locationFormMethods.register(eventLocationType.variable)} type="text" {...remainingProps} />
);
} else if (eventLocationType?.organizerInputType === "phone") {
const { defaultValue, ...rest } = remainingProps;
return (
<Controller
name={eventLocationType.variable}
control={control}
defaultValue={defaultValue}
render={({ field: { onChange, value } }) => {
return <PhoneInput onChange={onChange} value={value} {...remainingProps} />;
return <PhoneInput onChange={onChange} value={value} {...rest} />;
}}
/>
);
@@ -88,6 +91,9 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
useEffect(() => {
if (selection) {
locationFormMethods.setValue("locationType", selection?.value);
if (selection?.address) {
locationFormMethods.setValue("locationAddress", selection?.address);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selection]);
@@ -149,12 +155,25 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
name: "locationType",
});
const selectedAddrValue = useWatch({
control: locationFormMethods.control,
name: "locationAddress",
});
const eventLocationType = getEventLocationType(selectedLocation);
const defaultLocation = defaultValues?.find(
(location: { type: EventLocationType["type"] }) => location.type === eventLocationType?.type
(location: { type: EventLocationType["type"]; address?: string }) => {
if (location.type === LocationType.InPerson) {
return location.type === eventLocationType?.type && location.address === selectedAddrValue;
} else {
return location.type === eventLocationType?.type;
}
}
);
console.log(defaultLocation);
const LocationOptions = (() => {
if (eventLocationType && eventLocationType.organizerInputType && LocationInput) {
if (!eventLocationType.variable) {
@@ -55,7 +55,7 @@ export const RescheduleDialog = (props: IRescheduleDialog) => {
</p>
<TextArea
data-testid="reschedule_reason"
name={t("reschedule_reason")}
name={t("reason_for_reschedule")}
value={rescheduleReason}
onChange={(e) => setRescheduleReason(e.target.value)}
className="mb-5 sm:mb-6"
@@ -16,6 +16,7 @@ import {
allowDisablingHostConfirmationEmails,
} from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails";
import { FormBuilder } from "@calcom/features/form-builder/FormBuilder";
import { BookerLayoutSelector } from "@calcom/features/settings/BookerLayoutSelector";
import { classNames } from "@calcom/lib";
import { APP_NAME, CAL_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -65,7 +66,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
const [requiresConfirmation, setRequiresConfirmation] = useState(eventType.requiresConfirmation);
const placeholderHashedLink = `${CAL_URL}/d/${hashedUrl}/${eventType.slug}`;
const seatsEnabled = formMethods.watch("seatsPerTimeSlotEnabled");
const noShowFeeEnabled = eventType.metadata.apps?.stripe?.paymentOption === "HOLD";
const noShowFeeEnabled = eventType.metadata?.apps?.stripe?.paymentOption === "HOLD";
useEffect(() => {
!hashedUrl && setHashedUrl(generateHashedLink(eventType.users[0]?.id ?? team?.id));
@@ -159,6 +160,10 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
}
/>
</div>
<hr className="border-subtle [&:has(+div:empty)]:hidden" />
<div>
<BookerLayoutSelector fallbackToUserSettings />
</div>
<hr className="border-subtle" />
<FormBuilder
title={t("booking_questions_title")}
@@ -104,9 +104,9 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
</div>
</div>
{!shouldLockDisableProps("apps").disabled && (
<div>
<div className="bg-muted rounded-md p-8">
{!isLoading && notInstalledApps?.length ? (
<h2 className="text-emphasis my-2 text-lg font-semibold">{t("available_apps")}</h2>
<h2 className="text-emphasis text-lg font-semibold">{t("available_apps")}</h2>
) : null}
<div className="before:border-0">
{notInstalledApps?.map((app) => (
@@ -115,7 +115,7 @@ export const EventSetupTab = (
const [showLocationModal, setShowLocationModal] = useState(false);
const [editingLocationType, setEditingLocationType] = useState<string>("");
const [selectedLocation, setSelectedLocation] = useState<LocationOption | undefined>(undefined);
const [multipleDuration, setMultipleDuration] = useState(eventType.metadata.multipleDuration);
const [multipleDuration, setMultipleDuration] = useState(eventType.metadata?.multipleDuration);
const locationOptions = props.locationOptions.filter((option) => {
return !team ? option.label !== "Conferencing" : true;
@@ -136,9 +136,17 @@ export const EventSetupTab = (
selectedMultipleDuration.find((opt) => opt.value === eventType.length) ?? null
);
const openLocationModal = (type: EventLocationType["type"]) => {
const openLocationModal = (type: EventLocationType["type"], address = "") => {
const option = getLocationFromType(type, locationOptions);
setSelectedLocation(option);
if (option && option.value === LocationType.InPerson) {
const inPersonOption = {
...option,
address,
};
setSelectedLocation(inPersonOption);
} else {
setSelectedLocation(option);
}
setShowLocationModal(true);
};
@@ -298,10 +306,14 @@ export const EventSetupTab = (
onClick={() => {
locationFormMethods.setValue("locationType", location.type);
locationFormMethods.unregister("locationLink");
locationFormMethods.unregister("locationAddress");
if (location.type === LocationType.InPerson) {
locationFormMethods.setValue("locationAddress", location.address);
} else {
locationFormMethods.unregister("locationAddress");
}
locationFormMethods.unregister("locationPhoneNumber");
setEditingLocationType(location.type);
openLocationModal(location.type);
openLocationModal(location.type, location.address);
}}
aria-label={t("edit")}
className="hover:text-emphasis text-subtle mr-1 p-1">
@@ -517,7 +529,18 @@ export const EventSetupTab = (
defaultValues={formMethods.getValues("locations")}
selection={
selectedLocation
? { value: selectedLocation.value, label: t(selectedLocation.label), icon: selectedLocation.icon }
? selectedLocation.address
? {
value: selectedLocation.value,
label: t(selectedLocation.label),
icon: selectedLocation.icon,
address: selectedLocation.address,
}
: {
value: selectedLocation.value,
label: t(selectedLocation.label),
icon: selectedLocation.icon,
}
: undefined
}
setSelectedLocation={setSelectedLocation}
@@ -75,8 +75,7 @@ function getNavigation(props: {
installedAppsNumber: number;
availability: AvailabilityOption | undefined;
}) {
const { eventType, t, enabledAppsNumber, installedAppsNumber, enabledWorkflowsNumber, availability } =
props;
const { eventType, t, enabledAppsNumber, installedAppsNumber, enabledWorkflowsNumber } = props;
const duration =
eventType.metadata?.multipleDuration?.map((duration) => ` ${duration}`) || eventType.length;
@@ -178,6 +177,7 @@ function EventTypeSingleLayout({
enabledWorkflowsNumber,
availability,
});
navigation.splice(1, 0, {
name: "availability",
href: `/event-types/${eventType.id}?tabName=availability`,
@@ -185,7 +185,7 @@ function EventTypeSingleLayout({
info:
isManagedEventType || isChildrenManagedEventType
? eventType.schedule === null
? "Member's default schedule"
? "members_default_schedule"
: isChildrenManagedEventType
? `${
eventType.scheduleName
@@ -193,7 +193,7 @@ function EventTypeSingleLayout({
: `default_schedule_name`
}`
: eventType.scheduleName ?? `default_schedule_name`
: `default_schedule_name`,
: eventType.scheduleName ?? `default_schedule_name`,
});
// If there is a team put this navigation item within the tabs
if (team) {
@@ -220,7 +220,18 @@ function EventTypeSingleLayout({
});
}
return navigation;
}, [t, eventType, installedAppsNumber, enabledAppsNumber, enabledWorkflowsNumber, team, availability]);
}, [
t,
eventType,
enabledAppsNumber,
installedAppsNumber,
enabledWorkflowsNumber,
availability,
isManagedEventType,
isChildrenManagedEventType,
team,
formMethods,
]);
const permalink = `${CAL_URL}/${team ? `team/${team.slug}` : eventType.users[0].username}/${
eventType.slug
@@ -236,22 +247,36 @@ function EventTypeSingleLayout({
heading={eventType.title}
CTA={
<div className="flex items-center justify-end">
{!eventType.metadata.managedEventConfig && (
{!eventType.metadata?.managedEventConfig && (
<>
<div className="sm:hover:bg-muted hidden items-center rounded-md px-2 lg:flex">
<Skeleton
as={Label}
htmlFor="hiddenSwitch"
className="mt-2 hidden cursor-pointer self-center whitespace-nowrap pr-2 sm:inline">
{t("hide_from_profile")}
</Skeleton>
<Switch
id="hiddenSwitch"
checked={formMethods.watch("hidden")}
onCheckedChange={(e) => {
formMethods.setValue("hidden", e);
}}
/>
<div
className={classNames(
"sm:hover:bg-muted hidden cursor-pointer items-center rounded-md",
formMethods.watch("hidden") ? "px-2" : "",
"lg:flex"
)}>
{formMethods.watch("hidden") && (
<Skeleton
as={Label}
htmlFor="hiddenSwitch"
className="mt-2 hidden cursor-pointer self-center whitespace-nowrap pr-2 sm:inline">
{t("hidden")}
</Skeleton>
)}
<Tooltip
content={
formMethods.watch("hidden") ? t("show_eventtype_on_profile") : t("hide_from_profile")
}>
<div className="self-center rounded-md p-2">
<Switch
id="hiddenSwitch"
checked={!formMethods.watch("hidden")}
onCheckedChange={(e) => {
formMethods.setValue("hidden", !e);
}}
/>
</div>
</Tooltip>
</div>
<VerticalDivider className="hidden lg:block" />
</>
@@ -344,18 +369,18 @@ function EventTypeSingleLayout({
</DropdownItem>
</DropdownMenuItem>
<DropdownMenuSeparator />
<div className="sm:hover:bg-subtle flex h-9 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 py-2 px-4">
<Skeleton
as={Label}
htmlFor="hiddenSwitch"
className="mt-2 inline cursor-pointer self-center pr-2 ">
{t("hide_from_profile")}
{formMethods.watch("hidden") ? t("show_eventtype_on_profile") : t("hide_from_profile")}
</Skeleton>
<Switch
id="hiddenSwitch"
checked={formMethods.watch("hidden")}
checked={!formMethods.watch("hidden")}
onCheckedChange={(e) => {
formMethods.setValue("hidden", e);
formMethods.setValue("hidden", !e);
}}
/>
</div>
@@ -365,7 +390,7 @@ function EventTypeSingleLayout({
<Button
className="ml-4 lg:ml-0"
type="submit"
loading={formMethods.formState.isSubmitting || isUpdateMutationLoading}
loading={isUpdateMutationLoading}
data-testid="update-eventtype"
form="event-type-form">
{t("save")}
@@ -39,6 +39,7 @@ export default function RequiresConfirmationController({
if (!requiresConfirmation) {
formMethods.setValue("metadata.requiresConfirmationThreshold", undefined);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [requiresConfirmation]);
const { shouldLockDisableProps } = useLockedFieldsManager(
@@ -15,7 +15,7 @@ interface IConnectCalendarsProps {
const ConnectedCalendars = (props: IConnectCalendarsProps) => {
const { nextStep } = props;
const queryConnectedCalendars = trpc.viewer.connectedCalendars.useQuery();
const queryConnectedCalendars = trpc.viewer.connectedCalendars.useQuery({ onboarding: true });
const { t } = useLocale();
const queryIntegrations = trpc.viewer.integrations.useQuery({ variant: "calendar", onlyInstalled: false });
@@ -8,23 +8,17 @@ import { md } from "@calcom/lib/markdownIt";
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import turndown from "@calcom/lib/turndownService";
import { trpc } from "@calcom/trpc/react";
import { Button, Editor, ImageUploader, Label, showToast } from "@calcom/ui";
import { Avatar } from "@calcom/ui";
import { Avatar, Button, Editor, ImageUploader, Label, showToast } from "@calcom/ui";
import { ArrowRight } from "@calcom/ui/components/icon";
import type { IOnboardingPageProps } from "../../../pages/getting-started/[[...step]]";
type FormData = {
bio: string;
};
interface IUserProfileProps {
user: IOnboardingPageProps["user"];
}
const UserProfile = (props: IUserProfileProps) => {
const { user } = props;
const UserProfile = () => {
const [user] = trpc.viewer.me.useSuspenseQuery();
const { t } = useLocale();
const avatarRef = useRef<HTMLInputElement>(null!);
const avatarRef = useRef<HTMLInputElement>(null);
const { setValue, handleSubmit, getValues } = useForm<FormData>({
defaultValues: { bio: user?.bio || "" },
});
@@ -76,7 +70,7 @@ const UserProfile = (props: IUserProfileProps) => {
async function updateProfileHandler(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const enteredAvatar = avatarRef.current.value;
const enteredAvatar = avatarRef.current?.value;
mutation.mutate({
avatar: enteredAvatar,
});
@@ -127,14 +121,16 @@ const UserProfile = (props: IUserProfileProps) => {
id="avatar-upload"
buttonMsg={t("add_profile_photo")}
handleAvatarChange={(newAvatar) => {
avatarRef.current.value = newAvatar;
if (avatarRef.current) {
avatarRef.current.value = newAvatar;
}
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"value"
)?.set;
nativeInputValueSetter?.call(avatarRef.current, newAvatar);
const ev2 = new Event("input", { bubbles: true });
avatarRef.current.dispatchEvent(ev2);
avatarRef.current?.dispatchEvent(ev2);
updateProfileHandler(ev2 as unknown as FormEvent<HTMLFormElement>);
setImageSrc(newAvatar);
}}
@@ -13,15 +13,13 @@ import { ArrowRight } from "@calcom/ui/components/icon";
import { UsernameAvailabilityField } from "@components/ui/UsernameAvailability";
import type { IOnboardingPageProps } from "../../../pages/getting-started/[[...step]]";
interface IUserSettingsProps {
user: IOnboardingPageProps["user"];
nextStep: () => void;
}
const UserSettings = (props: IUserSettingsProps) => {
const { user, nextStep } = props;
const { nextStep } = props;
const [user] = trpc.viewer.me.useSuspenseQuery();
const { t } = useLocale();
const [selectedTimeZone, setSelectedTimeZone] = useState(dayjs.tz.guess());
const telemetry = useTelemetry();
@@ -69,7 +67,7 @@ const UserSettings = (props: IUserSettingsProps) => {
<form onSubmit={onSubmit}>
<div className="space-y-6">
{/* Username textfield */}
<UsernameAvailabilityField user={user} />
<UsernameAvailabilityField />
{/* Full name textfield */}
<div className="w-full">
@@ -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" disabled={isDisabling}>
<Button type="submit" className="ms-2 me-2" data-testid="disable-2fa" disabled={isDisabling}>
{t("disable")}
</Button>
<Button color="secondary" onClick={onCancel}>
@@ -156,7 +156,9 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
<img src={dataUri} alt="" />
}
</div>
<p className="text-center font-mono text-xs">{secret}</p>
<p data-testid="two-factor-secret" className="text-center font-mono text-xs">
{secret}
</p>
</>
</WithStep>
<Form handleSubmit={handleEnable} form={form}>
@@ -164,7 +166,11 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
<div className="mb-4">
<TwoFactor center />
{errorMessage && <p className="mt-1 text-sm text-red-700">{errorMessage}</p>}
{errorMessage && (
<p data-testid="error-submitting-code" className="mt-1 text-sm text-red-700">
{errorMessage}
</p>
)}
</div>
</WithStep>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
@@ -178,12 +184,16 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
</Button>
</WithStep>
<WithStep step={SetupStep.DisplayQrCode} current={step}>
<Button type="submit" className="ms-2 me-2" onClick={() => setStep(SetupStep.EnterTotpCode)}>
<Button
type="submit"
data-testid="goto-otp-screen"
className="ms-2 me-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="ms-2 me-2" data-testid="enable-2fa" disabled={isSubmitting}>
{t("enable")}
</Button>
</WithStep>
+3 -2
View File
@@ -4,6 +4,7 @@ import { useRouter } from "next/router";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { md } from "@calcom/lib/markdownIt";
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import type { TeamWithMembers } from "@calcom/lib/server/queries/teams";
import { Avatar } from "@calcom/ui";
@@ -23,7 +24,7 @@ const Member = ({ member, teamName }: { member: MemberType; teamName: string | n
return (
<Link key={member.id} href={{ pathname: `/${member.username}`, query: queryParamsToForward }}>
<div className="sm:min-w-80 sm:max-w-80 bg-default hover:bg-muted border-subtle group flex min-h-full flex-col space-y-2 rounded-md border p-4 hover:cursor-pointer">
<div className="sm:min-w-80 sm:max-w-80 bg-default hover:bg-muted border-subtle group flex min-h-full flex-col space-y-2 rounded-md border p-4 hover:cursor-pointer">
<Avatar
size="md"
alt={member.name || ""}
@@ -36,7 +37,7 @@ const Member = ({ member, teamName }: { member: MemberType; teamName: string | n
<>
<div
className=" text-subtle break-words text-sm [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600"
dangerouslySetInnerHTML={{ __html: md.render(member.bio || "") }}
dangerouslySetInnerHTML={{ __html: md.render(markdownToSafeHTML(member.bio)) }}
/>
</>
) : (
@@ -8,14 +8,12 @@ import { getPremiumPlanPriceValue } from "@calcom/app-store/stripepayment/lib/ut
import { fetchUsername } from "@calcom/lib/fetchUsername";
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { User } from "@calcom/prisma/client";
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import { Button, Dialog, DialogClose, DialogContent, Input, Label } from "@calcom/ui";
import { Star as StarSolid } from "@calcom/ui/components/icon";
import { Check, Edit2, ExternalLink } from "@calcom/ui/components/icon";
import { Check, Edit2, ExternalLink, Star as StarSolid } from "@calcom/ui/components/icon";
export enum UsernameChangeStatusEnum {
UPGRADE = "UPGRADE",
@@ -29,7 +27,6 @@ interface ICustomUsernameProps {
setInputUsernameValue: (value: string) => void;
onSuccessMutation?: () => void;
onErrorMutation?: (error: TRPCClientErrorLike<AppRouter>) => void;
user: Pick<User, "username" | "metadata">;
readonly?: boolean;
}
@@ -57,8 +54,8 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
onSuccessMutation,
onErrorMutation,
readonly: disabled,
user,
} = props;
const [user] = trpc.viewer.me.useSuspenseQuery();
const [usernameIsAvailable, setUsernameIsAvailable] = useState(false);
const [markAsError, setMarkAsError] = useState(false);
const router = useRouter();
@@ -21,7 +21,7 @@ interface ICustomUsernameProps {
onErrorMutation?: (error: TRPCClientErrorLike<AppRouter>) => void;
}
const UsernameTextfield = (props: ICustomUsernameProps) => {
const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.ComponentProps<typeof TextField>>) => {
const { t } = useLocale();
const {
currentUsername,
@@ -31,6 +31,7 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
usernameRef,
onSuccessMutation,
onErrorMutation,
...rest
} = props;
const [usernameIsAvailable, setUsernameIsAvailable] = useState(false);
const [markAsError, setMarkAsError] = useState(false);
@@ -79,7 +80,7 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
const ActionButtons = () => {
return usernameIsAvailable && currentUsername !== inputUsernameValue ? (
<div className="ms-2 me-2 mt-px flex flex-row space-x-2">
<div className="ms-2 me-2 flex flex-row space-x-2">
<Button
type="button"
onClick={() => setOpenDialogSaveUsername(true)}
@@ -116,9 +117,6 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
ref={usernameRef}
name="username"
value={inputUsernameValue}
addOnLeading={
<>{process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")}/</>
}
autoComplete="none"
autoCapitalize="none"
autoCorrect="none"
@@ -133,6 +131,7 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
setInputUsernameValue(event.target.value);
}}
data-testid="username-input"
{...rest}
/>
{currentUsername !== inputUsernameValue && (
<div className="absolute right-[2px] top-6 flex flex-row">
@@ -142,7 +141,7 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
</div>
)}
</div>
<div className="mt-5 hidden md:inline">
<div className="mt-7 hidden md:inline">
<ActionButtons />
</div>
</div>
@@ -2,9 +2,11 @@ import { useRouter } from "next/router";
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains";
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
import type { User } from "@calcom/prisma/client";
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import useRouterQuery from "@lib/hooks/useRouterQuery";
@@ -17,14 +19,24 @@ export const UsernameAvailability = IS_SELF_HOSTED ? UsernameTextfield : Premium
interface UsernameAvailabilityFieldProps {
onSuccessMutation?: () => void;
onErrorMutation?: (error: TRPCClientErrorLike<AppRouter>) => void;
user: Pick<User, "username" | "metadata">;
}
function useUserNamePrefix(organization: RouterOutputs["viewer"]["me"]["organization"]): string {
return organization
? organization.slug
? `${organization.slug}.${subdomainSuffix()}`
: organization.metadata && organization.metadata.requestedSlug
? `${organization.metadata.requestedSlug}.${subdomainSuffix()}`
: process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")
: process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "");
}
export const UsernameAvailabilityField = ({
onSuccessMutation,
onErrorMutation,
user,
}: UsernameAvailabilityFieldProps) => {
const router = useRouter();
const [user] = trpc.viewer.me.useSuspenseQuery();
const [currentUsernameState, setCurrentUsernameState] = useState(user.username || "");
const { username: usernameFromQuery, setQuery: setUsernameFromQuery } = useRouterQuery("username");
const { username: currentUsername, setQuery: setCurrentUsername } =
@@ -37,6 +49,8 @@ export const UsernameAvailabilityField = ({
},
});
const usernamePrefix = useUserNamePrefix(user.organization);
return (
<Controller
control={formMethods.control}
@@ -51,7 +65,7 @@ export const UsernameAvailabilityField = ({
setInputUsernameValue={onChange}
onSuccessMutation={onSuccessMutation}
onErrorMutation={onErrorMutation}
user={user}
addOnLeading={usernamePrefix}
/>
);
}}
@@ -26,12 +26,6 @@ export const CheckedSelect = ({
return (
<>
<Select
styles={{
option: (styles, { isDisabled }) => ({
...styles,
backgroundColor: isDisabled ? "#F5F5F5" : "inherit",
}),
}}
name={props.name}
placeholder={props.placeholder || t("select")}
isSearchable={false}
@@ -11,6 +11,7 @@ export type LocationOption = {
value: EventLocationType["type"];
icon?: string;
disabled?: boolean;
address?: string;
};
export type SingleValueLocationOption = SingleValue<LocationOption>;
+4 -2
View File
@@ -66,6 +66,7 @@ export function QueryCell<TData, TError extends ErrorLike>(
if ("empty" in opts && (query.data == null || (Array.isArray(query.data) && query.data.length === 0))) {
return opts.empty(query);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return opts.success(query as any);
}
@@ -88,9 +89,10 @@ const withQuery = <
TInput = inferProcedureInput<TQuery>,
TOutput = inferProcedureOutput<TQuery>
>(
queryProcedure: DecorateProcedure<TQuery, any, any>,
queryProcedure: DecorateProcedure<TQuery, inferProcedureInput<TQuery>, inferProcedureOutput<TQuery>>,
input?: TInput,
params?: UseTRPCQueryOptions<any, TInput, TOutput, TOutput, TError>
params?: UseTRPCQueryOptions<TQuery, TInput, TOutput, TOutput, TError>
) => {
return function WithQuery(
opts: Omit<
+10 -1
View File
@@ -9,6 +9,8 @@ import type { NextRouter } from "next/router";
import { useRouter } from "next/router";
import type { ComponentProps, PropsWithChildren, ReactNode } from "react";
import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider";
import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks";
import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic";
import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic";
import { FeatureProvider } from "@calcom/features/flags/context/provider";
@@ -205,6 +207,11 @@ function FeatureFlagsProvider({ children }: { children: React.ReactNode }) {
return <FeatureProvider value={flags}>{children}</FeatureProvider>;
}
function OrgBrandProvider({ children }: { children: React.ReactNode }) {
const orgBrand = useOrgBrandingValues();
return <OrgBrandingProvider value={orgBrand}>{children}</OrgBrandingProvider>;
}
const AppProviders = (props: AppPropsWithChildren) => {
const session = trpc.viewer.public.session.useQuery().data;
// No need to have intercom on public pages - Good for Page Performance
@@ -222,7 +229,9 @@ const AppProviders = (props: AppPropsWithChildren) => {
isThemeSupported={props.Component.isThemeSupported}
isBookingPage={props.Component.isBookingPage}>
<FeatureFlagsProvider>
<MetaProvider>{props.children}</MetaProvider>
<OrgBrandProvider>
<MetaProvider>{props.children}</MetaProvider>
</OrgBrandProvider>
</FeatureFlagsProvider>
</CalcomThemeProvider>
</TooltipProvider>
+19 -1
View File
@@ -5,7 +5,25 @@ export default function useRouterQuery<T extends string>(name: T) {
const existingQueryParams = router.asPath.split("?")[1];
const urlParams = new URLSearchParams(existingQueryParams);
const query = Object.fromEntries(urlParams);
const query: Record<string, string | string[]> = {};
// Following error is thrown by Typescript:
// 'Type 'URLSearchParams' can only be iterated through when using the '--downlevelIteration' flag or with a '--target' of 'es2015' or higher'
// We should change the target to higher ES2019 atleast maybe
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
for (const [key, value] of urlParams) {
if (!query[key]) {
query[key] = value;
} else {
let queryValue = query[key];
if (queryValue instanceof Array) {
queryValue.push(value);
} else {
queryValue = query[key] = [queryValue];
queryValue.push(value);
}
}
}
const setQuery = (newValue: string | number | null | undefined) => {
router.replace({ query: { ...router.query, [name]: newValue } }, undefined, { shallow: true });
+2 -1
View File
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
type GetSSRResult<TProps> =
//
{ props: TProps } | { redirect: any } | { notFound: boolean };
{ props: TProps } | { redirect: { destination: string; permanent: boolean } } | { notFound: boolean };
type GetSSRFn<TProps> = (...args: any[]) => Promise<GetSSRResult<TProps>>;
+3
View File
@@ -8,6 +8,7 @@ export default function withEmbedSsr(getServerSideProps: GetServerSideProps) {
return async (context: GetServerSidePropsContext): Promise<GetServerSidePropsResult<EmbedProps>> => {
const ssrResponse = await getServerSideProps(context);
const embed = context.query.embed;
const layout = context.query.layout;
if ("redirect" in ssrResponse) {
// Use a dummy URL https://base as the fallback base URL so that URL parsing works for relative URLs as well.
@@ -18,6 +19,8 @@ export default function withEmbedSsr(getServerSideProps: GetServerSideProps) {
destinationUrlObj.pathname +
"/embed?" +
destinationUrlObj.searchParams.toString() +
"&layout=" +
layout +
"&embed=" +
embed;
+41 -13
View File
@@ -3,20 +3,34 @@ 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";
let cold = true;
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";
return NextResponse.redirect(req.nextUrl);
}
// This console.log is required to create a report in axios for hot and cold requests
console.log(cold ? "Cold Start" : "Hot Start");
requestHeaders.set("x-cal-cold-start", cold ? "true" : "false");
cold = false;
if (!url.pathname.startsWith("/api")) {
//
// NOTE: When tRPC hits an error a 500 is returned, when this is received
@@ -54,13 +68,6 @@ const middleware: NextMiddleware = async (req) => {
}
}
// Ensure that embed query param is there in when /embed is added.
// query param is the way in which client side code knows that it is in embed mode.
if (url.pathname.endsWith("/embed") && typeof url.searchParams.get("embed") !== "string") {
url.searchParams.set("embed", "");
return NextResponse.redirect(url);
}
// Don't 404 old routing_forms links
if (url.pathname.startsWith("/apps/routing_forms")) {
url.pathname = url.pathname.replace("/apps/routing_forms", "/apps/routing-forms");
@@ -76,6 +83,26 @@ 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,
@@ -85,6 +112,7 @@ const middleware: NextMiddleware = async (req) => {
export const config = {
matcher: [
"/:path*",
"/api/collect-events/:path*",
"/api/auth/:path*",
"/apps/routing_forms/:path*",
+104 -12
View File
@@ -2,7 +2,7 @@ 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");
@@ -57,6 +57,20 @@ if (process.env.GOOGLE_API_CREDENTIALS && !validJson(process.env.GOOGLE_API_CRED
);
}
const informAboutDuplicateTranslations = () => {
const valueSet = new Set();
for (const key in englishTranslation) {
if (valueSet.has(englishTranslation[key])) {
console.warn("\x1b[33mDuplicate value found in:", "\x1b[0m", key);
} else {
valueSet.add(englishTranslation[key]);
}
}
};
informAboutDuplicateTranslations();
const plugins = [];
if (process.env.ANALYZE === "true") {
// only load dependency if env `ANALYZE` was set
@@ -169,7 +183,16 @@ const nextConfig = {
return config;
},
async rewrites() {
return [
// .* 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`;
let rewrites = [
{
source: "/org/:slug",
destination: "/team/:slug",
},
{
source: "/:user/avatar.png",
destination: "/api/user/avatar?username=:user",
@@ -201,21 +224,85 @@ const nextConfig = {
source: "/cancel/:path*",
destination: "/booking/:path*",
},
// Keep cookie based booker enabled just in case we disable new-booker globally
...[
{
source: userTypeRouteRegExp,
destination: "/new-booker/:user/:type",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type",
destination: "/new-booker/team/:slug/:type",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/d/:link/:slug",
destination: "/new-booker/d/:link/:slug",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
],
// Keep cookie based booker enabled to test new-booker embed in production
...[
{
source: `/:user((?!${pages.join("|")}).*)/:type/embed`,
destination: "/new-booker/:user/:type/embed",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type/embed",
destination: "/new-booker/team/:slug/:type/embed",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
],
/* TODO: have these files being served from another deployment or CDN {
source: "/embed/embed.js",
destination: process.env.NEXT_PUBLIC_EMBED_LIB_URL?,
}, */
{
source: `/:user((?!${pages.join("|")}).*)/:type`,
destination: "/new-booker/:user/:type",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type",
destination: "/new-booker/team/:slug/:type",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
/**
* Enables new booker using cookie. It works even if NEW_BOOKER_ENABLED_FOR_NON_EMBED, NEW_BOOKER_ENABLED_FOR_EMBED are disabled
*/
];
// 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(
...[
{
source: `/:user((?!${pages.join("|")}).*)/:type/embed`,
destination: "/new-booker/:user/:type/embed",
},
{
source: "/team/:slug/:type/embed",
destination: "/new-booker/team/:slug/:type/embed",
},
]
);
}
// 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(
...[
{
source: userTypeRouteRegExp,
destination: "/new-booker/:user/:type",
},
{
source: "/team/:slug/:type",
destination: "/new-booker/team/:slug/:type",
},
{
source: "/d/:link/:slug",
destination: "/new-booker/d/:link/:slug",
},
]
);
}
return rewrites;
},
async headers() {
return [
@@ -274,6 +361,11 @@ const nextConfig = {
destination: "/teams",
permanent: true,
},
{
source: "/settings/admin",
destination: "/settings/admin/flags",
permanent: true,
},
/* V2 testers get redirected to the new settings */
{
source: "/settings/profile",
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "2.9.4",
"version": "3.0.1",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
@@ -109,6 +109,7 @@
"react-intl": "^5.25.1",
"react-live-chat-loader": "^2.7.3",
"react-multi-email": "^0.5.3",
"react-phone-input-2": "^2.15.1",
"react-phone-number-input": "^3.2.7",
"react-schemaorg": "^2.0.0",
"react-select": "^5.7.0",
+3 -47
View File
@@ -6,7 +6,7 @@ import { useEffect, useState } from "react";
import { DOCS_URL, JOIN_SLACK, WEBSITE_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { HeadSeo } from "@calcom/ui";
import { BookOpen, Check, ChevronRight, FileText } from "@calcom/ui/components/icon";
import { BookOpen, Check, ChevronRight, FileText, Slack } from "@calcom/ui/components/icon";
import PageWrapper from "@components/PageWrapper";
@@ -165,29 +165,7 @@ export default function Custom404() {
className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0">
<span className="bg-muted flex h-12 w-12 items-center justify-center rounded-lg">
<svg
viewBox="0 0 2447.6 2452.5"
className="h-6 w-6"
xmlns="http://www.w3.org/2000/svg">
<g clipRule="evenodd" fillRule="evenodd">
<path
d="m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z"
fill="rgba(55, 65, 81)"
/>
<path
d="m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z"
fill="rgba(55, 65, 81)"
/>
<path
d="m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z"
fill="rgba(55, 65, 81)"
/>
<path
d="m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0"
fill="rgba(55, 65, 81)"
/>
</g>
</svg>
<Slack strokeWidth="1.5" fill="currentColor" className="h-6 w-6" />
</span>
</div>
<div className="min-w-0 flex-1">
@@ -313,29 +291,7 @@ export default function Custom404() {
className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0">
<span className="bg-muted flex h-12 w-12 items-center justify-center rounded-lg">
<svg
viewBox="0 0 2447.6 2452.5"
className="h-6 w-6"
xmlns="http://www.w3.org/2000/svg">
<g clipRule="evenodd" fillRule="evenodd">
<path
d="m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z"
fill="rgba(55, 65, 81)"
/>
<path
d="m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z"
fill="rgba(55, 65, 81)"
/>
<path
d="m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z"
fill="rgba(55, 65, 81)"
/>
<path
d="m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0"
fill="rgba(55, 65, 81)"
/>
</g>
</svg>
<Slack strokeWidth="1.5" fill="currentColor" className="h-6 w-6" />
</span>
</div>
<div className="min-w-0 flex-1">
+9 -1
View File
@@ -10,6 +10,7 @@ import {
useEmbedStyles,
useIsEmbed,
} from "@calcom/embed-core/embed-iframe";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { EventTypeDescriptionLazy as EventTypeDescription } from "@calcom/features/eventtypes/components";
import EmptyPage from "@calcom/features/eventtypes/components/EmptyPage";
import { WEBAPP_URL } from "@calcom/lib/constants";
@@ -255,6 +256,7 @@ const getEventTypesWithHiddenFromDB = async (userId: number) => {
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
const ssr = await ssrInit(context);
const crypto = await import("crypto");
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req.headers.host ?? "");
const usernameList = getUsernameList(context.query.user as string);
const dataFetchStart = Date.now();
@@ -263,6 +265,11 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
username: {
in: usernameList,
},
organization: isValidOrgDomain
? {
slug: currentOrgDomain,
}
: null,
},
select: {
id: true,
@@ -272,6 +279,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
bio: true,
brandColor: true,
darkBrandColor: true,
organizationId: true,
theme: true,
away: true,
verified: true,
@@ -284,7 +292,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
avatar: `${WEBAPP_URL}/${user.username}/avatar.png`,
}));
if (!users.length) {
if (!users.length || (!isValidOrgDomain && !users.some((user) => user.organizationId === null))) {
return {
notFound: true,
} as {
+56 -22
View File
@@ -1,8 +1,10 @@
import type { GetStaticPaths, GetStaticPropsContext } from "next";
import type { GetServerSidePropsContext } from "next";
import { z } from "zod";
import type { LocationObject } from "@calcom/app-store/locations";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
import { getUsernameList } from "@calcom/lib/defaultEvents";
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
@@ -15,7 +17,9 @@ import type { EmbedProps } from "@lib/withEmbedSsr";
import PageWrapper from "@components/PageWrapper";
import AvailabilityPage from "@components/booking/pages/AvailabilityPage";
export type AvailabilityPageProps = inferSSRProps<typeof getStaticProps> & EmbedProps;
import { ssrInit } from "@server/lib/ssr";
export type AvailabilityPageProps = inferSSRProps<typeof getServerSideProps> & EmbedProps;
export default function Type(props: AvailabilityPageProps) {
const { t } = useLocale();
@@ -50,6 +54,21 @@ export default function Type(props: AvailabilityPageProps) {
</div>
</main>
</div>
) : !props.isValidOrgDomain && props.organizationContext ? (
<div className="dark:bg-darkgray-50 h-screen">
<main className="mx-auto max-w-3xl px-4 py-24">
<div className="space-y-6" data-testid="event-types">
<div className="overflow-hidden rounded-sm border dark:border-gray-900">
<div className="text-muted dark:text-inverted p-8 text-center">
<h2 className="font-cal dark:text-inverted text-emphasis600 mb-2 text-3xl">
{" " + t("unavailable")}
</h2>
<p className="mx-auto max-w-md">{t("user_belongs_organization")}</p>
</div>
</div>
</div>
</main>
</div>
) : (
<AvailabilityPage {...props} />
);
@@ -59,21 +78,25 @@ Type.isBookingPage = true;
Type.PageWrapper = PageWrapper;
const paramsSchema = z.object({ type: z.string(), user: z.string() });
async function getUserPageProps(context: GetStaticPropsContext) {
async function getUserPageProps(context: GetServerSidePropsContext) {
// load server side dependencies
const prisma = await import("@calcom/prisma").then((mod) => mod.default);
const { privacyFilteredLocations } = await import("@calcom/app-store/locations");
const { parseRecurringEvent } = await import("@calcom/lib/isRecurringEvent");
const { EventTypeMetaDataSchema, teamMetadataSchema } = await import("@calcom/prisma/zod-utils");
const { ssgInit } = await import("@server/lib/ssg");
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req.headers.host ?? "");
const ssr = await ssrInit(context);
const { type: slug, user: username } = paramsSchema.parse(context.query);
const { type: slug, user: username } = paramsSchema.parse(context.params);
const ssg = await ssgInit(context);
const user = await prisma.user.findUnique({
const user = await prisma.user.findFirst({
where: {
/** TODO: We should standarize this */
username: username.toLowerCase().replace(/( |%20)/g, "+"),
organization: isValidOrgDomain
? {
slug: currentOrgDomain,
}
: null,
},
select: {
id: true,
@@ -87,6 +110,7 @@ async function getUserPageProps(context: GetStaticPropsContext) {
brandColor: true,
darkBrandColor: true,
metadata: true,
organizationId: true,
eventTypes: {
where: {
// Many-to-many relationship causes inclusion of the team events - cool -
@@ -108,6 +132,17 @@ async function getUserPageProps(context: GetStaticPropsContext) {
schedulingType: true,
metadata: true,
seatsPerTimeSlot: true,
team: {
select: {
logo: true,
parent: {
select: {
logo: true,
name: true,
},
},
},
},
},
orderBy: [
{
@@ -179,16 +214,17 @@ async function getUserPageProps(context: GetStaticPropsContext) {
},
// Dynamic group has no theme preference right now. It uses system theme.
themeBasis: user.username,
organizationContext: user?.organizationId !== null,
away: user?.away,
isDynamic: false,
trpcState: ssg.dehydrate(),
trpcState: ssr.dehydrate(),
isValidOrgDomain: orgDomainConfig(context.req.headers.host ?? ""),
isBrandingHidden: isBrandingHidden(user.hideBranding, hasActiveTeam || hasPremiumUserName),
},
revalidate: 10, // seconds
};
}
async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
async function getDynamicGroupPageProps(context: GetServerSidePropsContext) {
// load server side dependencies
const { getDefaultEvent, getGroupName, getUsernameList } = await import("@calcom/lib/defaultEvents");
const { privacyFilteredLocations } = await import("@calcom/app-store/locations");
@@ -197,10 +233,10 @@ async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
const { EventTypeMetaDataSchema, userMetadata: userMetadataSchema } = await import(
"@calcom/prisma/zod-utils"
);
const { ssgInit } = await import("@server/lib/ssg");
const ssr = await ssrInit(context);
const { getAppFromSlug } = await import("@calcom/app-store/utils");
const ssg = await ssgInit(context);
const { type: typeParam, user: userParam } = paramsSchema.parse(context.params);
const usernameList = getUsernameList(userParam);
const length = parseInt(typeParam);
@@ -230,6 +266,7 @@ async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
defaultScheduleId: true,
allowDynamicBooking: true,
metadata: true,
organizationId: true,
away: true,
schedules: {
select: {
@@ -313,24 +350,21 @@ async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
themeBasis: null,
isDynamic: true,
away: false,
trpcState: ssg.dehydrate(),
organizationContext: !users.some((user) => user.organizationId === null),
trpcState: ssr.dehydrate(),
isValidOrgDomain: orgDomainConfig(context.req.headers.host ?? ""),
isBrandingHidden: false, // I think we should always show branding for dynamic groups - saves us checking every single user
},
revalidate: 10, // seconds
};
}
export const getStaticProps = async (context: GetStaticPropsContext) => {
export async function getServerSideProps(context: GetServerSidePropsContext) {
const { user: userParam } = paramsSchema.parse(context.params);
// dynamic groups are not generated at build time, but otherwise are probably cached until infinity.
const isDynamicGroup = userParam.includes("+");
const isDynamicGroup = getUsernameList(userParam).length > 1;
if (isDynamicGroup) {
return await getDynamicGroupPageProps(context);
} else {
return await getUserPageProps(context);
}
};
export const getStaticPaths: GetStaticPaths = async () => {
return { paths: [], fallback: "blocking" };
};
}
+8 -10
View File
@@ -1,20 +1,18 @@
import type { GetStaticPropsContext } from "next";
import type { GetServerSidePropsContext } from "next";
import { getStaticProps as _getStaticProps } from "../[type]";
export { getStaticPaths } from "../[type]";
import { getServerSideProps as _getServerSideProps } from "../[type]";
export { default } from "../[type]";
export const getStaticProps = async (context: GetStaticPropsContext) => {
const staticResponse = await _getStaticProps(context);
if (staticResponse.notFound) {
return staticResponse;
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
const ssrResponse = await _getServerSideProps(context);
if (ssrResponse.notFound) {
return ssrResponse;
}
return {
...staticResponse,
...ssrResponse,
props: {
...staticResponse.props,
...ssrResponse.props,
isEmbed: true,
},
};
@@ -0,0 +1 @@
export { getStaticProps, getStaticPaths, default } from "./index";
@@ -3,22 +3,20 @@
* caching system that NextJS uses SSG pages.
* TODO: Redirect to user profile on browser
*/
import type { GetStaticPaths, GetStaticProps } from "next";
import type { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from "next";
import { z } from "zod";
import { getCachedResults } from "@calcom/core";
import getCalendarsEvents from "@calcom/core/getCalendarsEvents";
import dayjs from "@calcom/dayjs";
import prisma from "@calcom/prisma";
const CalendarCache = () => <div />;
const paramsSchema = z.object({ user: z.string(), month: z.string() });
export const getStaticProps: GetStaticProps<
{ results: Awaited<ReturnType<typeof getCachedResults>> },
{ results: Awaited<ReturnType<typeof getCalendarsEvents>> },
{ user: string }
> = async (context) => {
const { user: username, month } = paramsSchema.parse(context.params);
const userWithCredentials = await prisma.user.findUnique({
const userWithCredentials = await prisma.user.findFirst({
where: {
username,
},
@@ -29,16 +27,16 @@ export const getStaticProps: GetStaticProps<
selectedCalendars: true,
},
});
const startDate = (
dayjs(month, "YYYY-MM").isSame(dayjs(), "month") ? dayjs.utc() : dayjs.utc(month, "YYYY-MM")
).startOf("day");
const endDate = startDate.endOf("month");
// Subtract 11 hours from the start date to avoid problems in UTC- time zones.
const startDate = dayjs.utc(month, "YYYY-MM").startOf("day").subtract(11, "hours").format();
// Add 14 hours from the start date to avoid problems in UTC+ time zones.
const endDate = dayjs.utc(month, "YYYY-MM").endOf("month").add(14, "hours").format();
try {
const results = userWithCredentials?.credentials
? await getCachedResults(
? await getCalendarsEvents(
userWithCredentials?.credentials,
startDate.format(),
endDate.format(),
startDate,
endDate,
userWithCredentials?.selectedCalendars
)
: [];
@@ -64,5 +62,8 @@ export const getStaticPaths: GetStaticPaths = () => {
fallback: "blocking",
};
};
type Props = InferGetStaticPropsType<typeof getStaticProps>;
const CalendarCache = (props: Props) =>
process.env.NODE_ENV === "development" ? <pre>{JSON.stringify(props, null, " ")}</pre> : <div />;
export default CalendarCache;
+3 -3
View File
@@ -40,9 +40,9 @@ class MyDocument extends Document<Props> {
return (
<Html lang={locale}>
<Head nonce={nonce}>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/api/logo?type=apple-touch-icon" />
<link rel="icon" type="image/png" sizes="32x32" href="/api/logo?type=favicon-32" />
<link rel="icon" type="image/png" sizes="16x16" href="/api/logo?type=favicon-16" />
<link rel="manifest" href="/site.webmanifest" />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#000000" />
<meta name="msapplication-TileColor" content="#ff0000" />
+25 -9
View File
@@ -6,6 +6,7 @@ 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 { defaultHandler } from "@calcom/lib/server";
import { getTranslation } from "@calcom/lib/server/i18n";
import prisma from "@calcom/prisma";
@@ -13,28 +14,39 @@ const limiter = rateLimit({
intervalInMs: 60 * 1000, // 1 minute
});
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
async function handler(req: NextApiRequest, res: NextApiResponse) {
const t = await getTranslation(req.body.language ?? "en", "common");
const email = z
.string()
.email()
.transform((val) => val.toLowerCase())
.parse(req.body?.email);
.safeParse(req.body?.email);
const { isRateLimited } = limiter.check(10, email); // 10 requests per minute
if (isRateLimited) {
return res.status(429).json({ message: "Too Many Requests." });
if (!email.success) {
return res.status(400).json({ message: "email is required" });
}
if (req.method !== "POST") {
return res.status(405).end();
// fallback to email if ip is not present
let ip = (req.headers["x-real-ip"] as string) ?? email.data;
const forwardedFor = req.headers["x-forwarded-for"] as string;
if (!ip && forwardedFor) {
ip = forwardedFor?.split(",").at(0) ?? email.data;
}
// 10 requests per minute
try {
limiter.check(10, ip);
} catch (e) {
return res.status(429).json({ message: "Too Many Requests." });
}
try {
const maybeUser = await prisma.user.findUnique({
where: {
email,
email: email.data,
},
select: {
name: true,
@@ -97,3 +109,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return res.status(500).json({ message: "Unable to create password reset request" });
}
}
export default defaultHandler({
POST: Promise.resolve({ default: handler }),
});
+17 -11
View File
@@ -1,11 +1,20 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import { hashPassword } from "@calcom/features/auth/lib/hashPassword";
import { sendEmailVerification } from "@calcom/features/auth/lib/verifyEmail";
import slugify from "@calcom/lib/slugify";
import { closeComUpsertTeamUser } from "@calcom/lib/sync/SyncServiceManager";
import prisma from "@calcom/prisma";
import { IdentityProvider } from "@calcom/prisma/enums";
const signupSchema = z.object({
username: z.string(),
email: z.string().email(),
password: z.string().min(7),
language: z.string().optional(),
});
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "POST") {
return;
@@ -17,7 +26,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
const data = req.body;
const { email, password } = data;
const { email, password, language } = signupSchema.parse(data);
const username = slugify(data.username);
const userEmail = email.toLowerCase();
@@ -26,16 +36,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return;
}
if (!userEmail || !userEmail.includes("@")) {
res.status(422).json({ message: "Invalid email" });
return;
}
if (!password || password.trim().length < 7) {
res.status(422).json({ message: "Invalid input - password should be at least 7 characters long." });
return;
}
// There is an existingUser if the username matches
// OR if the email matches AND either the email is verified
// or both username and password are set
@@ -106,5 +106,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
}
await sendEmailVerification({
email: userEmail,
username,
language,
});
res.status(201).json({ message: "Created user" });
}
+48
View File
@@ -0,0 +1,48 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import dayjs from "@calcom/dayjs";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { prisma } from "@calcom/prisma";
const verifySchema = z.object({
token: z.string(),
});
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { token } = verifySchema.parse(req.query);
const foundToken = await prisma.verificationToken.findFirst({
where: {
token,
},
});
if (!foundToken) {
return res.status(401).json({ message: "No token found" });
}
if (dayjs(foundToken?.expires).isBefore(dayjs())) {
return res.status(401).json({ message: "Token expired" });
}
const user = await prisma.user.update({
where: {
email: foundToken?.identifier,
},
data: {
emailVerified: new Date(),
},
});
// Delete token from DB after it has been used
await prisma.verificationToken.delete({
where: {
id: foundToken?.id,
},
});
const hasCompletedOnboarding = user.completedOnboarding;
res.redirect(`${WEBAPP_URL}/${hasCompletedOnboarding ? "/event-types" : "/getting-started"}`);
}
+8 -3
View File
@@ -60,9 +60,14 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
res.setHeader("Content-Type", "text/html");
res.setHeader("Cache-Control", "no-cache, no-store, private, must-revalidate");
res.write(
renderEmail("OrganizerRequestEmail", {
calEvent: evt,
attendee: evt.organizer,
renderEmail("VerifyAccountEmail", {
language: t,
user: {
name: "Pro Example",
email: "pro@example.com",
},
verificationEmailLink:
"http://localhost:3000/api/auth/verify-email?token=b91af0eee5a9a24a8d83a3d3d6a58c1606496e94ced589441649273c66100f5b",
})
);
res.end();
+27 -4
View File
@@ -4,6 +4,7 @@ import { z } from "zod";
import { symmetricDecrypt } from "@calcom/lib/crypto";
import { defaultResponder } from "@calcom/lib/server";
import prisma from "@calcom/prisma";
import { UserPermissionRole } from "@calcom/prisma/enums";
import { TRPCError } from "@calcom/trpc/server";
import { createContext } from "@calcom/trpc/server/createContext";
import { viewerRouter } from "@calcom/trpc/server/routers/viewer/_router";
@@ -26,7 +27,7 @@ const decryptedSchema = z.object({
async function handler(req: NextApiRequest, res: NextApiResponse<Response>) {
const { action, token, reason } = querySchema.parse(req.query);
const { bookingUid } = decryptedSchema.parse(
const { bookingUid, userId } = decryptedSchema.parse(
JSON.parse(symmetricDecrypt(decodeURIComponent(token), process.env.CALENDSO_ENCRYPTION_KEY || ""))
);
@@ -34,10 +35,32 @@ async function handler(req: NextApiRequest, res: NextApiResponse<Response>) {
where: { uid: bookingUid },
});
const user = await prisma.user.findUniqueOrThrow({
where: { id: userId },
});
/** We shape the session as required by tRPC router */
async function sessionGetter() {
return {
user: {
id: userId,
username: "" /* Not used in this context */,
role: UserPermissionRole.USER,
},
hasValidLicense: true,
expires: "" /* Not used in this context */,
};
}
try {
/** @see https://trpc.io/docs/server-side-calls */
const ctx = await createContext({ req, res });
const caller = viewerRouter.createCaller({ ...ctx, req, res });
const ctx = await createContext({ req, res }, sessionGetter);
const caller = viewerRouter.createCaller({
...ctx,
req,
res,
user: { ...user, locale: user?.locale ?? "en" },
});
await caller.bookings.confirm({
bookingId: booking.id,
recurringEventId: booking.recurringEventId || undefined,
@@ -46,7 +69,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<Response>) {
});
} catch (e) {
let message = "Error confirming booking";
if (e instanceof TRPCError) message = e.message;
if (e instanceof TRPCError) message = (e as TRPCError).message;
res.redirect(`/booking/${bookingUid}?error=${encodeURIComponent(message)}`);
return;
}
+125 -17
View File
@@ -1,7 +1,19 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import { IS_SELF_HOSTED, LOGO, LOGO_ICON, WEBAPP_URL } from "@calcom/lib/constants";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import {
ANDROID_CHROME_ICON_192,
ANDROID_CHROME_ICON_256,
APPLE_TOUCH_ICON,
FAVICON_16,
FAVICON_32,
IS_SELF_HOSTED,
LOGO,
LOGO_ICON,
MSTILE_ICON,
WEBAPP_URL,
} from "@calcom/lib/constants";
import logger from "@calcom/lib/logger";
const log = logger.getChildLogger({ prefix: ["[api/logo]"] });
@@ -20,12 +32,80 @@ function extractSubdomainAndDomain(hostname: string) {
}
const logoApiSchema = z.object({
icon: z.coerce.boolean().optional(),
type: z.coerce.string().optional(),
});
const SYSTEM_SUBDOMAINS = ["console", "app", "www"];
async function getTeamLogos(subdomain: string) {
type LogoType =
| "logo"
| "icon"
| "favicon-16"
| "favicon-32"
| "apple-touch-icon"
| "mstile"
| "android-chrome-192"
| "android-chrome-256";
type LogoTypeDefinition = {
fallback: string;
w?: number;
h?: number;
source: "appLogo" | "appIconLogo";
};
const logoDefinitions: Record<LogoType, LogoTypeDefinition> = {
logo: {
fallback: `${WEBAPP_URL}${LOGO}`,
source: "appLogo",
},
icon: {
fallback: `${WEBAPP_URL}${LOGO_ICON}`,
source: "appIconLogo",
},
"favicon-16": {
fallback: `${WEBAPP_URL}${FAVICON_16}`,
w: 16,
h: 16,
source: "appIconLogo",
},
"favicon-32": {
fallback: `${WEBAPP_URL}${FAVICON_32}`,
w: 32,
h: 32,
source: "appIconLogo",
},
"apple-touch-icon": {
fallback: `${WEBAPP_URL}${APPLE_TOUCH_ICON}`,
w: 180,
h: 180,
source: "appLogo",
},
mstile: {
fallback: `${WEBAPP_URL}${MSTILE_ICON}`,
w: 150,
h: 150,
source: "appLogo",
},
"android-chrome-192": {
fallback: `${WEBAPP_URL}${ANDROID_CHROME_ICON_192}`,
w: 192,
h: 192,
source: "appLogo",
},
"android-chrome-256": {
fallback: `${WEBAPP_URL}${ANDROID_CHROME_ICON_256}`,
w: 256,
h: 256,
source: "appLogo",
},
};
function isValidLogoType(type: string): type is LogoType {
return type in logoDefinitions;
}
async function getTeamLogos(subdomain: string, isValidOrgDomain: boolean) {
try {
if (
// if not cal.com
@@ -39,25 +119,31 @@ async function getTeamLogos(subdomain: string) {
}
// load from DB
const { default: prisma } = await import("@calcom/prisma");
const team = await prisma.team.findUniqueOrThrow({
const team = await prisma.team.findFirst({
where: {
slug: subdomain,
...(isValidOrgDomain && {
metadata: {
path: ["isOrganization"],
equals: true,
},
}),
},
select: {
appLogo: true,
appIconLogo: true,
},
});
// try to use team logos, otherwise default to LOGO/LOGO_ICON regardless
return {
appLogo: team.appLogo || `${WEBAPP_URL}${LOGO}`,
appIconLogo: team.appIconLogo || `${WEBAPP_URL}${LOGO_ICON}`,
appLogo: team?.appLogo,
appIconLogo: team?.appIconLogo,
};
} catch (error) {
if (error instanceof Error) log.debug(error.message);
return {
appLogo: `${WEBAPP_URL}${LOGO}`,
appIconLogo: `${WEBAPP_URL}${LOGO_ICON}`,
appLogo: undefined,
appIconLogo: undefined,
};
}
}
@@ -68,6 +154,7 @@ async function getTeamLogos(subdomain: string) {
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { query } = req;
const parsedQuery = logoApiSchema.parse(query);
const { isValidOrgDomain } = orgDomainConfig(req.headers.host ?? "");
const hostname = req?.headers["host"];
if (!hostname) throw new Error("No hostname");
@@ -75,14 +162,35 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (!domains) throw new Error("No domains");
const [subdomain] = domains;
const { appLogo, appIconLogo } = await getTeamLogos(subdomain);
const teamLogos = await getTeamLogos(subdomain, isValidOrgDomain);
const filteredLogo = parsedQuery?.icon ? appIconLogo : appLogo;
// Resolve all icon types to team logos, falling back to Cal.com defaults.
const type: LogoType = parsedQuery?.type && isValidLogoType(parsedQuery.type) ? parsedQuery.type : "logo";
const logoDefinition = logoDefinitions[type];
const filteredLogo = teamLogos[logoDefinition.source] ?? logoDefinition.fallback;
const response = await fetch(filteredLogo);
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
res.setHeader("Content-Type", response.headers.get("content-type") as string);
res.setHeader("Cache-Control", "s-maxage=86400");
res.send(buffer);
try {
const response = await fetch(filteredLogo);
const arrayBuffer = await response.arrayBuffer();
let buffer = Buffer.from(arrayBuffer);
// If we need to resize the team logos (via Next.js' built-in image processing)
if (teamLogos[logoDefinition.source] && logoDefinition.w) {
const { detectContentType, optimizeImage } = await import("next/dist/server/image-optimizer");
buffer = await optimizeImage({
buffer,
contentType: detectContentType(buffer) ?? "image/jpeg",
quality: 100,
width: logoDefinition.w,
height: logoDefinition.h, // optional
});
}
res.setHeader("Content-Type", response.headers.get("content-type") as string);
res.setHeader("Cache-Control", "s-maxage=86400");
res.send(buffer);
} catch (error) {
res.statusCode = 404;
res.json({ error: "Failed fetching logo" });
}
}
@@ -0,0 +1 @@
export { default } from "@calcom/features/ee/organizations/api/subteams";
-4
View File
@@ -1,4 +0,0 @@
import ethRouter from "@calcom/app-store/rainbow/trpc/router";
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
export default createNextApiHandler(ethRouter);
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { viewerOrganizationsRouter } from "@calcom/trpc/server/routers/viewer/organizations/_router";
export default createNextApiHandler(viewerOrganizationsRouter);

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