Compare commits

...
240 Commits
Author SHA1 Message Date
Keith Williams f179c23cc5 v2.8.13
release.yml / release (push) Canceled after 0s
2023-05-10 11:26:52 -03:00
Jeroen Reumkens 9f83885e8c CAL-1577: Improved input addon styles. (#8803) 2023-05-10 08:59:32 +00:00
Syed Ali Shahbaz 8ffb5529e6 Use user profile timezone from DB as fallback for CalDAV all-day events (#8755)
* adds timezone pull from user profile in DB

* minor fixes

* fixes promise return type

* comments and minor fixes

* minor improvement

* fix return type

* changes for help in debugging
2023-05-10 08:44:15 +00:00
Jon@1599 2e686d03fa feat: allow only team users to record daily meetings (#8799) 2023-05-10 08:28:49 +00:00
Jeroen Reumkens cc3be0a6ac CAL-1623 CAL-1620: Improve animation for booker, and push mini calendar down in fullscreen views (#8773)
* CAL-1623 CAL-1620: Improve animation for booker, and push mini calendar down in fullscreen views

* Updated yarn.lock
2023-05-10 08:26:59 +00:00
ec3a3268c9 Resolved[#8732] Adding Trim to remove extra space (#8736)
* Resolved[#8732] Adding Trim to remove extra space

* fix: improve code

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

* chore: add lowercase

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

* chore: use lowercase

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>
2023-05-10 07:45:15 +00:00
Udit Takkar 77eed5611e fix: use useTeamsPlan hook (#8789)
* fix: use useTeamsPlan hook

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

* fix: z index bug on dialog

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

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-05-10 07:11:01 +00:00
Crowdin Bot a827d1defb New Crowdin translations by Github Action 2023-05-10 04:27:42 +00:00
Hariom Balhara b08a62d30b Allow booking responses to have the data from variantsConfig (#8718) 2023-05-10 03:59:51 +00:00
d6fb0df64f perf: tRPC procedures and middleware refactor (#8419)
* trpc procedures an middleware refactor

* allow use sessionMiddleware without a req object

* sync with the new tRPC structure

* tRPC refactor on routing form app

* import Prisma from @prisma/client

* Lazy load apps from appstore

* remove unrelated changes

* Add types for PaymentService

* type fixes

* Merge branch 'main' into roae85/cal-1514-set-the-user-session-only-on-the

* fix typo

* remove console.log

* remove explicit types from apstore object

* linter fixes

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-05-09 19:27:05 +00:00
Crowdin Bot 03548d8381 New Crowdin translations by Github Action 2023-05-09 17:41:16 +00:00
Carina WollendorferandCarinaWolli 36d8554fd5 disable standard confirmation emails if workflow exists (#8747)
* add switches to advanced even type settings

* only show switches when workflow is enabled

* check in event type update handler if user is allowed to disable standard emails

* don't send emails if disabled

* make sure emails can't be disabled if no workflow exist

* send workflow emails to all attendees

* code clean up

* add translations

* always send to all attendees when scheduling reminders

* change text

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-05-09 17:08:14 +00:00
sean-brydonandUdit Takkar 6fc9e9315d Bug/checkbox calendar sync state (#8677)
* switch wip

* Implement calendar switch component

* Checked default

* Add spinner to improve UX

* Add connected calendar reivaluate

* Remove local state

* Move back to middleware

* Fix spacing issue

* fix: spacing

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-05-09 17:53:23 +01:00
Ashray ShettyandBailey Pumfleet 6eb8119a82 Added the 'Add' button (#8777)
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-05-09 17:45:53 +01:00
Keith Williams f4c613e6d1 Enable workflows on merge_group (#8781)
* Enable workflows on merge_group

* Reverting back to pull_request_target
2023-05-09 17:43:07 +01:00
Nafees NazikandHariom Balhara 082507aea5 fix: location select logo in dark mode (#8633)
* fix: logo

* fix: icon on booker

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-05-09 15:27:09 +00:00
Leo GiovanettiandJoe Au-Yeung 90c1270e3d Fixing availability for MET (#8662)
* Fixing availability for MET

* Removing unneeded code

* Fixing unit tests

* Lint fixes

---------

Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
2023-05-09 11:15:13 -03:00
Joe Au-Yeungandalannnc 94917dbb1e Disable reserve slot on seated events (#8761)
* Disable reserve slot on seated events

* Move seats and reserve logic to backend

* Pass booking attendees

* Type fix

* Create booking when two users choose a slot before booking is created

* Update packages/trpc/server/routers/viewer/slots/reserveSlot.handler.ts

---------

Co-authored-by: alannnc <alannnc@gmail.com>
2023-05-09 11:02:19 +01:00
Carina WollendorferandCarinaWolli fbbcc401cb fix START_TIME variable + allow sms for self hosted (#8762)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-05-09 07:50:17 +00:00
swaraj bachu af7d7ec678 fixed ui bug (#8741) 2023-05-09 08:45:45 +01:00
Reckson Zirsangzela Khiangte 3b7d85852a Bug: Change color variable for dark mode (#8758) 2023-05-09 08:44:17 +01:00
Peer RichelsenandUdit Takkar 5362cd66b4 added gradients to dark dashboard and dark booking page (#8459)
* added gradients to dark dashboard and dark booking page

* removed bg of timezone dropdown for dak

* added gradient to confirmation screen

* only gradient in dashboard

* fix: prettier

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-05-08 16:03:24 -03:00
Zoheb AhmedandAlex van Andel 2907405d4e Fixes Bold/Italics Issue (#8613)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-05-08 14:11:25 +02:00
3c46c39114 feat: catch emails sent locally using Mailhog (#8470)
* feat: added mailhog to catch emails sent locally

* fix: added new line at the end of packages/emails/docker-compose.yml

* chore: removed EMAIL_SERVER_USER and EMAIL_SERVER_PASSWORD for local usage with Mailhog

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-06 19:08:04 -03:00
Manish Roy c6ab889b98 handles scenario where defaultschedule is nil (#8714) 2023-05-06 08:52:54 +01:00
Hariom Balhara a2e2b0ed34 Fix broken appsRouter (#8719) 2023-05-06 08:50:52 +01:00
bc38b3bc55 enhance Timezone Component with Box Shadow (#8582)
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-05 21:38:19 +00:00
Jeroen Reumkens 7611d5c24e CAL-1555: Show correct team avatars (#8665) 2023-05-05 21:44:21 +01:00
5d2fd7aafb [Symbolic Link Issues] Added instructions for setup on windows (#8574)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
2023-05-05 21:43:40 +01:00
b9ade55ac1 Update README.md (#8679)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-05 21:41:47 +01:00
Jeroen Reumkens d7051c4a3f Centered small_calendar view vertically (#8712) 2023-05-05 21:39:00 +01:00
Jeroen Reumkens ab50067194 Set selecteddate to today when switching layout. Before we were getting the current layout from the store, which would actually be the OLD layout you were switching from 😅 (#8711) 2023-05-05 16:42:47 +00:00
cdba1920fc refactor: split up routers to separate lambdas (#8041)
* split up routers to separate lambdas

* fix responsemeta

* move

* add typeguards to make sure all endpoints are covered in the approuter

* prettier

* move slotsrouter

* split ssg/ssr

* make sure correct headers are sent on viewer.public

* make sure correct headers are sent + use ctx.prisma

* Fixed new prefetch broken by merge

* Fixes after merge

* Created separate API route for all tRPC routers

* More fixes from refactor

* Fixed tRPC query for slots

* Put back extra line

* Fixed type checks

* Removed Endpoint type check since it loads from client

* Reverted change in getSchedule test

* Fix trpc routes in expectations

* Fix one more route test

---------

Co-authored-by: Efraín Rochín <roae.85@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-05-05 13:19:10 -03:00
Jeroen Reumkens 98a3508b8f CAL-1552: Add newline in front of time for selected timeslot in booker meta. (#8669) 2023-05-05 16:55:54 +01:00
Alex van Andel 446f384405 Add publishHandler user.id (#8703) 2023-05-05 15:36:33 +00:00
Jeroen Reumkensandsean-brydon be79b8ecd6 CAL-1554: remove gap between form and event meta on now booker due to css grid alignment (#8667)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-05-05 12:38:21 +00:00
371e7b024a add "reset to default" color option for ColorPicker (#8643)
* add reset to default color option

* Do not show reset button in DOM if resetDefaultValue is undefined

* handle edge case: if invalid hexcode is set for resetDefaultValue

* Do not show the reset button if the color is already set to the default value

* Remove data-testid from Button

---------

Co-authored-by: Praneeth Bhogaraju <praneeth.bhogaraju@ni.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-05-05 13:25:18 +01:00
sean-brydon 76807a7e70 Fix switch label hover (#8696) 2023-05-05 12:47:00 +01:00
Hariom BalharaandPeer Richelsen d3f77bf292 Fix: Embed Theme switching when appearance is using System Theme (#8655)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-05-05 16:54:06 +05:30
Nafees Nazik 2f175eacf5 fix: load date picker on client side (#8690) 2023-05-05 13:04:58 +02:00
Nafees Nazik b45aea3f26 feat: add search for members (#8659)
* feat: add search team members

* fix: add default value

* fix: add placeholder
2023-05-05 10:35:27 +01:00
Jeroen Reumkens c401a58bb1 CAL-1589: Added utm link to powered by in booker. Also added new colors to powered by component, so theyre also used in current booker form now on. (#8670) 2023-05-05 08:36:23 +01:00
05b42c001f feat: Add Zoho Bigin app to appstore (#8158)
* feat: add zoho bigin app to appstore

* Handle appKeys save and read correctly

* Fixes

* Remove env variables and seeding

* Create README for zoho-bigin and link to that in main README

* Remove imageSrc, thats deprecated

* Improved logs

* Remove stray beecepter fetch

* Add Zoho Bigin package.json description

* Load constants from config.json

* Refactor bigin auth tokens management

* Remove try catch

* fix refresh token not getting stored properly

* load api domain dynamically

* Fix reschedule not updating zoho bigin event

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-05-04 20:19:48 +00:00
Mahesh Bansodeandsean-brydon 20f7a5841e Added validation for duration field in the event types (#8649)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-05-04 12:05:52 +00:00
zomars dbf6bb991f Revert "perf: fix multiple versions of emotion/react loaded warning (#8549)"
This reverts commit 1cea7f9760.
2023-05-04 13:01:16 +01:00
Zeeshan BhatiandEfraín Rochín 75cd5a05b8 Fix Hover Effect in booking page (#8650)
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
2023-05-04 11:58:27 +00:00
Joeandsean-brydon ca6afed27c fix: changed assign to field as typeable input (#8637)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-05-04 11:39:43 +00:00
Jeroen Reumkens 45250f6f3e CAL-1558: Fixed responsiveness of booker for small calendar layout (#8660)
* CAL-1558: Fixed responsiveness of booker for small calendar layout

* Remove commented code
2023-05-04 12:32:22 +01:00
Jeroen Reumkens bd5af9e821 Fix/prevent duplicate avatars (#8664)
* Prevent duplicate avatars in new booker by also checking on profile url.

* Don't filter out profile without profile url

* Use webapp_url for avatars since that's what is used in the rest of the platform, that way the unique filter on avatar works
2023-05-04 11:07:37 +00:00
Jeroen Reumkens 4267c9063b CAL-1556: Alignment of current day dot in datepicker. (#8663) 2023-05-04 11:53:50 +01:00
Crowdin Bot 2b49d96e4a New Crowdin translations by Github Action 2023-05-03 18:37:57 +00:00
127c96b46c Update README.md (#8632)
* Update README.md

* Update README.md

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-05-03 15:36:55 -03:00
Hariom Balhara 3595ba9824 Fix tests (#8640) 2023-05-03 12:30:26 -03:00
Keith Williams 56acfa38f1 v2.8.12
release.yml / release (push) Canceled after 0s
2023-05-03 10:12:34 -03:00
Syed Ali Shahbaz e78fb22451 [URGENT] Hotfix: re-adds the password and username set check in signup (#8638)
* Check for existing password and username to ensure it isn't an invite

* added check with verified to allow SAML linking

* wrong verified check lol

* updated comment

* unintended
2023-05-03 10:10:28 -03:00
Babis DigosandKeith Williams 99f736b317 fix: Use correct values on append DayRange (#8597)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-05-02 19:43:32 -03:00
ee9f80ae3b Fix form inputs height to 36px (#8541)
* fix form inputs height to 36px

* fix: remove h-9 from preset

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

* fix input addOns container height

* replace minHeight class with height in input addOn

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-05-02 21:49:17 +00:00
a3c283da5e Updated the CSS classes for the user name and profile (#8571)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-05-02 18:20:01 -03:00
Zoheb Ahmed 0ef678b2cf Booking page bug fixed (#8614) 2023-05-02 18:10:47 -03:00
4f466fd95d [CAL-1095] Embed modal (floating pop up button) - UI/layout/spacing issues (#8217)
* [CAL-1095] Embed modal (floating pop up button) - UI/layout/spacing issues

* requested changes

---------

Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-05-02 15:57:01 -03:00
Keith Williams 5672a721d9 v2.8.11
release.yml / release (push) Canceled after 0s
2023-05-02 15:21:58 -03:00
Mohd Afzal KhanandKeith Williams 5ad45900ea fix the auto-focus issue in teams setting page (#8577)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-05-02 15:20:03 -03:00
Hariom Balhara ada88a2f7e Fix: Show smsReminderNumber on booking success page (#8596) 2023-05-02 16:58:39 +00:00
Srijan MukherjeeandAlex van Andel 9ba90e18de resolves #3565 (#8581)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-05-02 16:56:29 +00:00
Carina WollendorferandCarinaWolli a88eb0fdc5 Allow booking date overrides outside of working hours (#8538)
* allow booking on date overrides outside of working hours

* remove comment

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-05-02 11:38:56 -03:00
4b4c1200cd Add HistoryPlugin to the Editor UI component (#8603)
* Add HistoryPlugin to the Editor UI component

* Fix ESLint errors

---------

Co-authored-by: Praneeth Bhogaraju <praneeth.bhogaraju@ni.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-05-02 11:29:52 -03:00
Nafees NazikandUdit Takkar 1cea7f9760 perf: fix multiple versions of emotion/react loaded warning (#8549)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-05-02 18:31:54 +05:30
28dcf69d18 use named imports instead to avoid polluting the global namespace. in… (#8616)
* use named imports instead to avoid polluting the global namespace. in nodemailer

* Update packages/emails/templates/_base-email.ts

Lint fix.

* Revert yarn.lock changes

---------

Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-05-02 12:58:47 +00:00
Zeeshan Bhatiandnafees nazik 83d5176ddb Remove redirection after app uninstalltion (#8556)
Co-authored-by: nafees nazik <nafeesnazik21@gmail.com>
2023-05-02 18:14:09 +05:30
Alex van Andel 3815a4c4bf Fixed yarn.lock 2023-05-02 14:14:42 +02:00
Julius MarmingeandAlex van Andel 53748eb380 add an enum generator, stop importing from @prisma/client (#8548)
* add an enum generator and start importing from it

* keep moving imports

* fix remaining

* Header simplified

* Removed generated file from repo

* Updated .gitignore to exclude enums directory

* Add eslint rule to check for @prisma/client Prisma enum import

* Added another enum import + exclude PrismaClient

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-05-02 13:44:05 +02:00
hiteshbedre 28af11f90e fix: corrected discord link pattern (#8612) 2023-05-02 15:44:26 +05:30
Peer Richelsen ea676907b2 fixed border of status badge (#8568) 2023-04-30 13:26:09 +05:30
hallidayo 221c8b75bc added alignment on items (#8544) 2023-04-28 16:56:31 +05:30
GitStart-Cal.comandgitstart-calcom 5b9b7ab22d [CAL-1530] Assigned members to managed event types missing preview URL (#8552)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-04-27 14:51:20 -03:00
668ea088ef fix: password not updatable after an api error (#8514)
* fix: error clearance

* changes

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-04-27 11:43:23 +00:00
Nafees NazikandKeith Williams 8a6c420268 perf: prevent refetching i18n on route change (#8535)
* fix: prevent refetching i18n on route change

* remove additional properties

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-04-27 10:55:26 +00:00
Crowdin Bot d2f8a5c076 New Crowdin translations by Github Action 2023-04-27 08:08:34 +00:00
Abdurrahman RajabandPeer Richelsen 9c0f7bbfc7 Chore: fix RTL style in Color picker, Return arrow, Two factor page (#8317)
* chore: UI arrow left add RTL.

* chore: fix appearence page RTL

* chore: add margins two-factor RTL

* chore: update due to review

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-27 08:07:32 +00:00
Joe 8297824051 fix: edit mode set to true by default (#8468) 2023-04-26 22:06:04 +05:30
Joe Au-YeungandHariom Balhara 7ff5b66f46 Bookings with no-show fee to set proper location & prevent broken payments from blocking payment page (#8508)
* Better error logging on Stripe payment service

* Update booking

* If missing payment, load rest of bookings

* Address feedback

* Move requires confirmation threshold to webhook

* Use requiresConfirmation

* Revert unnecessary prettier changes

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-04-26 10:12:14 -04:00
Kirat 55570ed4bf Fixed discord validation (#8536) 2023-04-26 13:20:18 +00:00
Hariom Balhara 51695ee79d chore/AppStore: Remove deprecated imageSrc and make the paths relative in logo (#8479)
* Remove depreated imageSrc and make the paths relative in logo

* Do logo replacement at a single place

* Make logo relative at other places

* Fix campfire page not working in dev environment
2023-04-26 12:03:34 +01:00
Crowdin Bot 543466f1b9 New Crowdin translations by Github Action 2023-04-26 08:30:45 +00:00
Peer Richelsen 4abf2d67d9 removed devcontainer (#8521) 2023-04-26 10:29:54 +02:00
1eeb91a793 perf: lazy load tRPC routes (#8167)
* experiment: cold start perf

* fix: update failing test

* chore: add database indexes

* chore: use json protocol and add query batching back

* Update [status].tsx

* Update [trpc].ts

* Delete getSlimSession.ts

* Update createContext.ts

* remove trpc caller

* correctly import Prisma

* lazy ethRouter

* replace crypto with md5

* import fixes

* public event endpoint refactor

* Update yarn.lock

* Update yarn.lock

* Using yarn.lock from main

---------

Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-04-25 19:39:47 -03:00
Nafees Nazik a4725920ff fix: font not loaded in cal video (#8517) 2023-04-25 20:59:39 +02:00
Richard Poelderl 4cc7766f88 add noindex tags for apps setup pages (#8518) 2023-04-25 20:59:15 +02:00
Crowdin Bot 0c78715e9a New Crowdin translations by Github Action 2023-04-25 14:30:39 +00:00
Crowdin Bot 671d9b4903 New Crowdin translations by Github Action 2023-04-25 14:29:36 +00:00
Jeroen ReumkensandPeer Richelsen 524cefcbad UI Improvements for new booker (#8483)
CAL-1578: No layout shift when switching months (because of timeslots disappearing again)
CAL-1576: Font weight tweaks
Bigger sidebar on fullscreen booker layouts
CAL-1573: Slightly smaller padding around booker sections
CAL-1563: Prevent timezone select from resizing when typing in it
CAL-1561: Spacing improvements timeslots + button height
CAL-1560: Improve resize animation when changing from time slot picker to form
CAL-1549: Added overflow scroll to event description, removed icon from event description so we have more space, and added sexy scrollbars

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-25 14:28:32 +00:00
Crowdin Bot 72f72d86b2 New Crowdin translations by Github Action 2023-04-25 14:08:18 +00:00
Crowdin Bot c5c29322d3 New Crowdin translations by Github Action 2023-04-25 14:07:30 +00:00
Crowdin Bot 863b04e9c1 New Crowdin translations by Github Action 2023-04-25 14:06:34 +00:00
Crowdin Bot c2833f1b44 New Crowdin translations by Github Action 2023-04-25 14:05:43 +00:00
Hariom Balhara 25df264823 Remove reviews/rating/trending/verified props (#8482) 2023-04-25 16:04:40 +02:00
Crowdin Bot e870312ebe New Crowdin translations by Github Action 2023-04-25 14:04:23 +00:00
Crowdin Bot 5371bff6ff New Crowdin translations by Github Action 2023-04-25 14:03:32 +00:00
Jeroen Reumkens c791344c8b Switch back to small calendar layout when switching from mobile to desktop view. Also fixed rerender bug triggerd by ToggleGroup constantly updating because it got a new instance for the onValueChange function. (#8515) 2023-04-25 15:02:34 +01:00
Crowdin Bot 5e149d9f5f New Crowdin translations by Github Action 2023-04-25 12:22:07 +00:00
Alex van Andel 3e53aa13eb v2.8.10
release.yml / release (push) Canceled after 0s
2023-04-25 14:16:42 +02:00
317f961bc8 Feat/weekly booking view (#8484)
* Wip on booker atom

* Wip on booker atom

* Added correct icon imports

* Fixed build

* Responsive improvements

* Removed package lock

* Responsive tweaks

* Animation improvements and cleanup

* Animation improvements and event meta layout improvements.

* Tweaked margins.

* Added more event meta blocks

* Layout tweaks

* Converted booker layout to css grid and implemented multiple layout options

* cleanup

* Fixed build

* Fixed build

* Added temporary api route to enable/disable new booker

* Added sticky behavior

* Reverted yarn.lock and reinstalled new packages to see if this fixes build on vercel.

* Ensure divider lines always have 100% height.

* Improved animation config + initial load

* Ensure to pass eventid to getschedule, otherwise custom availability schedule wont work and wont return any availability

* Fixed divider line heights in booker

* Fixed timezone select positioning

* Added ability to view multiple days of timeslots

* Added icons to booker toggle

* Always show timeslots in timeslots view, also if no date is selected yet. In that case we show upcoming 5 days.

* Fixed timeslots in small calendar view

* Show selected day in calendar

* Fixed booker timeslots view

* Wip in making booking form work

* Moved most of the booker atom stuff to features, since it belongs there. Atom should be a rather small wrapper.

* Added create event functionality to booker form.

* Added guests toggle to booker form and styled input addons in dark mode.

* Added dynamic weekstart to booker

* Added seats limit feature to timeslots.

* Removed todo

* Added correct event avatars

* Added correct event name and icons

* Added correct translation for minutes text in multi duration

* Add rescheduling functionality to new booker.

* Added selected booking time to booking meta in sidebar.

* Abstracted away timeformat to custom hook

* Added correct key props to all components in booker.

* Fix build

* Create some new custom hooks to have a lot less repitition in code.

* Moved bookerform component inside booker directory since it is tied to it.

* Added error messages to booker form, plus fixed bug in recurring events.

* Added some comments <3

* Fixed todos in booker form.

* Added loading state for timeslot selector, and added prefetching of next month, in case of multi day view showing 2 months at the same time.

* Fixed import paths

* Added away view

* Validate uniqueness of event attendees.

* Tweaked comment

* #5798 added correct date format and style for selected date in booker.

* UI improvements

* Enable possibility to add booking values via query params.

* Added functionality to update query params when user selects date/duration etc in booker

* First steps in adding e2e test.

* Fixes after merge with main, and added new form builder.

* Implemented new form types and validation to booker, confirming new form builder. Validation still throwing wrong error keys though.

* Added search to timezone dropdown

* Added e2e test for booker (copy of current booker tests, only enabling cookie), plus fixed reschedule view.

* Updated yarn.lock

* Added new booker for team pages.

* Fixed input addon (hover) styles.

* Added dynamic booking.

* Hide timeformat  select for multi day view for now.

* Cleanup and ui tweaks

* removed log

* Mobile improvements

* Cleanup

* Small design tweaks after talking to ciaran.

* Text color and weight tweaks in booker

* Added rainbow gates to new booker.

* Added in default values which fixes form vallidation (???).

* Added empty defaults for name and email

* Added metadata

* Reset yarn.lock

* Fixed booker zod validation after change in main.

* Icon tweak

* Fixed timezone select styles after new classnames have been merged.

* Updated seat availability styles.

* Update yarn.lock

* Added explanation for alchemy key to .env.example

* Added tooltip to booker month/week/multiday toggle

* Fixed timezoneselect styles in booker after select updates.

* Updates bookingfields component by taking changes from current booker component

* Removed remaining booker todos

* Fix bookeventform

* Fix for recurring event meta

* Type fixes

* Typefixes

* Team event fixes

* Avoid hydration errors by only rendering date picker client side. Remove web3 gates since we dont offer them anymore. Prevent timeslot select from staying open when switching to a different month.

* Don't show calendar on mobile booker during booking.

* Always align booker buttons to bottom

* Don't show backend messages in error, rather show a helpful text like the current booker does as well.

* Do invisible next rewrite based on cookie from next.config.js (#7949)

* Do invisible next rewrite based on cookie from next.config.js

* Name embed link instead of bookerPath

* Rewrites only dynamic user pages

---------

Co-authored-by: zomars <zomars@me.com>

* Don't allow change of timezone when bookerform is visible

* Don't add duration to query param if the event is not a multi duration event.

* Update next.config.js

* Added correct timezone formatting to event meta when timeslot is selected.

* removed .env variable that isn't needed anymore.

* Update Gates.tsx

* Type fixes

* Allows to run all tests with the new booker

* Fixed timezone select styles after merge.

* Don't throw error when event doesn't have hosts, rather return no users, which will result in no availability in UI.

* Make booker errors of severity info instead of warning.

* Ensure team avatars are shown, as well as filter on uniqueness of avatars.

* Added all booked today message to timeslots.

* Added cal.com logo to booker.

* Fixed fragment classname error, minor mobile animation tweaks plus make all booked today text smaller for multi day layout.

* Improved timezone select styles, and updated arguments of getbooking function after updates in main.

* Prevent infinite loop in rewriting new booker.

* Prevent infinite loop in rewriting new booker.

* Moved new-booker pages to their own directory to prevent regexes confusing next and thus nut running getserversideprops after rewrite. Also adding clearing of old date in booker store, that could stick around when user immediately navigates back to the same page after booking.

* Fixed cal logo color in darkmode for new booker.

* Implemented new color tokens and theme variables. Also small design tweaks after merge with main.

* Minor style tweaks

* Show multiple locations in tooltip on booker #8222

* Radio button style tweaks

* Fixed build

* Updated calendar imports to new lucide names

* Removed resetting of selected times logic, because otherwise url params wouldnt be taken into account which is actually what we want. So old values sticking around when navigating back is actually the desired behavior.

* Updated tests to instead of always run the new booker in tests, have a utility to run both the new and old booker for specified tests.

* Added comment and eslint disable for if statement in booker test.

* Update packages/features/bookings/components/event-meta/Details.tsx

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

* Fix badge types

* Lazy loaded timezone select to save 85kb in bundle size.

* Upgraded framer to latest. Als moved framer and react sticky deps to features instead of atoms.

* Added new pagewrapper logic

* Simplified rescheduling ssr fetches, this now also supports multi seat rescheduling.

* Unset selected time when user is rescheduling directly after a new booking, otherwise it would show the form instead of new time selection.

* Updated form builder logic as per form builder in current booker.

* Updated form builder prefill logic as per logic in current booker.

* Updated getbooking function to fetch correct details when a reschedule uid is used

* Fixed booking questions test by NOT waiting for /book page because the new booker doesnt have this.

* Added former meeting time to reschedule view.

* Fixed types

* Undo playwright config update by mistake.

* Fixed event types test by only waiting for /book page in old booker

* Set new booker cookie to one year in the future instead of 2050

* added reset mockdate to test

* Temporary disabled test to see if this solves the out of memory error.

* Deleted test to see if that fixes the memory error

* Large View Header

* BookFormModal

* Modal

* Fix modal

* Select first day when switching months in booker

* SetSelected Date on large layouts

* Fix re-renders

* Fix yarn.lock

* Set selected date when switching layout

* Add empty state

* Lint

* sticky button on modal

* paramater layout & empty state

* fix: 7040 fix - team profile description (#8469)

* New Crowdin translations by Github Action

* Not center

* Fix positionion

* Removed unused dependnecy in effect

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

Co-authored-by: Alex van Andel <me@alexvanandel.com>

* Fix padding overflow

---------

Co-authored-by: Jeroen Reumkens <hello@jeroenreumkens.nl>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: hallidayo <22655069+Hallidayo@users.noreply.github.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-04-25 13:01:45 +01:00
b0c57a5c38 add australia country code to noAlphanumericSenderIdSupport (#8510)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-04-25 10:18:44 +00:00
Crowdin Bot 39c16bebd5 New Crowdin translations by Github Action 2023-04-25 09:55:06 +00:00
Alex van Andel b3a5f4a6c0 Fix yarn.lock file 2023-04-25 11:54:03 +02:00
Crowdin Bot 379785196d New Crowdin translations by Github Action 2023-04-25 06:41:22 +00:00
hallidayo 04f8de238f fix: 7040 fix - team profile description (#8469) 2023-04-25 12:10:35 +05:30
Joe 0af2be9d8f Fix: settings moved to userdropdown (#8409) 2023-04-24 23:51:17 +02:00
Crowdin Bot 98d31157cb New Crowdin translations by Github Action 2023-04-24 21:38:14 +00:00
Joe f99d518a06 fix: checkbox and text alignment (#8503) 2023-04-24 23:37:16 +02:00
Crowdin Bot 62770ecc49 New Crowdin translations by Github Action 2023-04-24 18:54:34 +00:00
Crowdin Bot be05ec66ce New Crowdin translations by Github Action 2023-04-24 18:53:52 +00:00
Crowdin Bot 8defc351be New Crowdin translations by Github Action 2023-04-24 18:52:54 +00:00
Crowdin Bot ab112d92ea New Crowdin translations by Github Action 2023-04-24 18:51:55 +00:00
Crowdin Bot cf1b124dc1 New Crowdin translations by Github Action 2023-04-24 18:51:07 +00:00
Crowdin Bot 38158ee043 New Crowdin translations by Github Action 2023-04-24 18:50:10 +00:00
Crowdin Bot 8d214cd8ac New Crowdin translations by Github Action 2023-04-24 18:49:15 +00:00
Crowdin Bot 01ed11ae33 New Crowdin translations by Github Action 2023-04-24 18:48:27 +00:00
Crowdin Bot 5c7756ce6b New Crowdin translations by Github Action 2023-04-24 18:47:38 +00:00
Crowdin Bot a8c5227536 New Crowdin translations by Github Action 2023-04-24 18:46:51 +00:00
Crowdin Bot f3730daf05 New Crowdin translations by Github Action 2023-04-24 18:46:08 +00:00
Crowdin Bot 47d5d800b0 New Crowdin translations by Github Action 2023-04-24 18:45:18 +00:00
Peer Richelsen f6d00a41fb removing more leading-none (#8464) 2023-04-24 18:44:30 +00:00
Crowdin Bot 692f2778e4 New Crowdin translations by Github Action 2023-04-24 17:22:12 +00:00
81d8628a87 remove application_fee_amount (#8496)
* Clean up linting

* Type fix

---------

Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: GitStart-Cal.com <121884634+gitstart-calcom@users.noreply.github.com>
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2023-04-24 17:21:24 +00:00
Crowdin Bot 07fbc4cbd3 New Crowdin translations by Github Action 2023-04-24 16:38:52 +00:00
Joe 8cb9a96c3a fix: text color when hover (#8497) 2023-04-24 16:37:53 +00:00
Crowdin Bot 73b1c1dfb7 New Crowdin translations by Github Action 2023-04-24 14:55:33 +00:00
Regina LiuandHariom Balhara 224a8864a6 Add Meta Pixel app (#8476)
* Add Meta Pixel app

* Remove app-store seedinfg for app

* Simplify code

* Apply suggestions from code review

* Fix yarn.lock

* Apply suggestions from code review

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-04-24 16:54:42 +02:00
6d02ac6729 New Booker Component (preparations for booker atom) (#6792)
* Wip on booker atom

* Wip on booker atom

* Added correct icon imports

* Fixed build

* Responsive improvements

* Removed package lock

* Responsive tweaks

* Animation improvements and cleanup

* Animation improvements and event meta layout improvements.

* Tweaked margins.

* Added more event meta blocks

* Layout tweaks

* Converted booker layout to css grid and implemented multiple layout options

* cleanup

* Fixed build

* Fixed build

* Added temporary api route to enable/disable new booker

* Added sticky behavior

* Reverted yarn.lock and reinstalled new packages to see if this fixes build on vercel.

* Ensure divider lines always have 100% height.

* Improved animation config + initial load

* Ensure to pass eventid to getschedule, otherwise custom availability schedule wont work and wont return any availability

* Fixed divider line heights in booker

* Fixed timezone select positioning

* Added ability to view multiple days of timeslots

* Added icons to booker toggle

* Always show timeslots in timeslots view, also if no date is selected yet. In that case we show upcoming 5 days.

* Fixed timeslots in small calendar view

* Show selected day in calendar

* Fixed booker timeslots view

* Wip in making booking form work

* Moved most of the booker atom stuff to features, since it belongs there. Atom should be a rather small wrapper.

* Added create event functionality to booker form.

* Added guests toggle to booker form and styled input addons in dark mode.

* Added dynamic weekstart to booker

* Added seats limit feature to timeslots.

* Removed todo

* Added correct event avatars

* Added correct event name and icons

* Added correct translation for minutes text in multi duration

* Add rescheduling functionality to new booker.

* Added selected booking time to booking meta in sidebar.

* Abstracted away timeformat to custom hook

* Added correct key props to all components in booker.

* Fix build

* Create some new custom hooks to have a lot less repitition in code.

* Moved bookerform component inside booker directory since it is tied to it.

* Added error messages to booker form, plus fixed bug in recurring events.

* Added some comments <3

* Fixed todos in booker form.

* Added loading state for timeslot selector, and added prefetching of next month, in case of multi day view showing 2 months at the same time.

* Fixed import paths

* Added away view

* Validate uniqueness of event attendees.

* Tweaked comment

* #5798 added correct date format and style for selected date in booker.

* UI improvements

* Enable possibility to add booking values via query params.

* Added functionality to update query params when user selects date/duration etc in booker

* First steps in adding e2e test.

* Fixes after merge with main, and added new form builder.

* Implemented new form types and validation to booker, confirming new form builder. Validation still throwing wrong error keys though.

* Added search to timezone dropdown

* Added e2e test for booker (copy of current booker tests, only enabling cookie), plus fixed reschedule view.

* Updated yarn.lock

* Added new booker for team pages.

* Fixed input addon (hover) styles.

* Added dynamic booking.

* Hide timeformat  select for multi day view for now.

* Cleanup and ui tweaks

* removed log

* Mobile improvements

* Cleanup

* Small design tweaks after talking to ciaran.

* Text color and weight tweaks in booker

* Added rainbow gates to new booker.

* Added in default values which fixes form vallidation (???).

* Added empty defaults for name and email

* Added metadata

* Reset yarn.lock

* Fixed booker zod validation after change in main.

* Icon tweak

* Fixed timezone select styles after new classnames have been merged.

* Updated seat availability styles.

* Update yarn.lock

* Added explanation for alchemy key to .env.example

* Added tooltip to booker month/week/multiday toggle

* Fixed timezoneselect styles in booker after select updates.

* Updates bookingfields component by taking changes from current booker component

* Removed remaining booker todos

* Fix bookeventform

* Fix for recurring event meta

* Type fixes

* Typefixes

* Team event fixes

* Avoid hydration errors by only rendering date picker client side. Remove web3 gates since we dont offer them anymore. Prevent timeslot select from staying open when switching to a different month.

* Don't show calendar on mobile booker during booking.

* Always align booker buttons to bottom

* Don't show backend messages in error, rather show a helpful text like the current booker does as well.

* Do invisible next rewrite based on cookie from next.config.js (#7949)

* Do invisible next rewrite based on cookie from next.config.js

* Name embed link instead of bookerPath

* Rewrites only dynamic user pages

---------

Co-authored-by: zomars <zomars@me.com>

* Don't allow change of timezone when bookerform is visible

* Don't add duration to query param if the event is not a multi duration event.

* Update next.config.js

* Added correct timezone formatting to event meta when timeslot is selected.

* removed .env variable that isn't needed anymore.

* Update Gates.tsx

* Type fixes

* Allows to run all tests with the new booker

* Fixed timezone select styles after merge.

* Don't throw error when event doesn't have hosts, rather return no users, which will result in no availability in UI.

* Make booker errors of severity info instead of warning.

* Ensure team avatars are shown, as well as filter on uniqueness of avatars.

* Added all booked today message to timeslots.

* Added cal.com logo to booker.

* Fixed fragment classname error, minor mobile animation tweaks plus make all booked today text smaller for multi day layout.

* Improved timezone select styles, and updated arguments of getbooking function after updates in main.

* Prevent infinite loop in rewriting new booker.

* Prevent infinite loop in rewriting new booker.

* Moved new-booker pages to their own directory to prevent regexes confusing next and thus nut running getserversideprops after rewrite. Also adding clearing of old date in booker store, that could stick around when user immediately navigates back to the same page after booking.

* Fixed cal logo color in darkmode for new booker.

* Implemented new color tokens and theme variables. Also small design tweaks after merge with main.

* Minor style tweaks

* Show multiple locations in tooltip on booker #8222

* Radio button style tweaks

* Fixed build

* Updated calendar imports to new lucide names

* Removed resetting of selected times logic, because otherwise url params wouldnt be taken into account which is actually what we want. So old values sticking around when navigating back is actually the desired behavior.

* Updated tests to instead of always run the new booker in tests, have a utility to run both the new and old booker for specified tests.

* Added comment and eslint disable for if statement in booker test.

* Update packages/features/bookings/components/event-meta/Details.tsx

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

* Fix badge types

* Lazy loaded timezone select to save 85kb in bundle size.

* Upgraded framer to latest. Als moved framer and react sticky deps to features instead of atoms.

* Added new pagewrapper logic

* Simplified rescheduling ssr fetches, this now also supports multi seat rescheduling.

* Unset selected time when user is rescheduling directly after a new booking, otherwise it would show the form instead of new time selection.

* Updated form builder logic as per form builder in current booker.

* Updated form builder prefill logic as per logic in current booker.

* Updated getbooking function to fetch correct details when a reschedule uid is used

* Fixed booking questions test by NOT waiting for /book page because the new booker doesnt have this.

* Added former meeting time to reschedule view.

* Fixed types

* Undo playwright config update by mistake.

* Fixed event types test by only waiting for /book page in old booker

* Set new booker cookie to one year in the future instead of 2050

* added reset mockdate to test

* Temporary disabled test to see if this solves the out of memory error.

* Deleted test to see if that fixes the memory error

* Select first day when switching months in booker

---------

Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Sean Brydon <sean@cal.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-24 16:32:30 +02:00
Crowdin Bot f838e8bd53 New Crowdin translations by Github Action 2023-04-24 14:13:48 +00:00
Hariom Balhara 5dac6896d2 Routing-Forms/External Redirect URL improvement (#8487) 2023-04-24 19:42:50 +05:30
Crowdin Bot 496cb265d1 New Crowdin translations by Github Action 2023-04-24 14:08:30 +00:00
sean-brydon c037a62a9d Sad to see it go (#8230) 2023-04-24 19:37:29 +05:30
Crowdin Bot 88d17165ec New Crowdin translations by Github Action 2023-04-22 19:57:56 +00:00
Alex van Andel 0e7e1c2cc0 v2.8.9
release.yml / release (push) Canceled after 0s
2023-04-22 21:56:46 +02:00
Crowdin Bot eb97feb173 New Crowdin translations by Github Action 2023-04-22 19:53:44 +00:00
Peer Richelsen 7d702dd546 only show vibrancy on MacOS (#8462) 2023-04-22 20:52:50 +01:00
Hariom Balhara 82b6b83885 Possible fix for iOS redirection (#8455) 2023-04-22 19:45:22 +02:00
Peer Richelsen 39b6def444 fixed timezone dropdown (#8461) 2023-04-22 10:32:06 -07:00
Crowdin Bot 7f5cdbab9b New Crowdin translations by Github Action 2023-04-22 15:48:07 +00:00
Peer Richelsen 1770b832f4 fixed bg for profile on light mode (#8457) 2023-04-22 17:47:15 +02:00
Crowdin Bot d30c0a626d New Crowdin translations by Github Action 2023-04-22 10:58:56 +00:00
Syed Ali ShahbazandAlex van Andel 2eed750ef6 Hotfix - CalDAV/Wraps timezone addition in try catch (#8453)
* Wraps timezone addition in try catch

* Suggestion accepted

Co-authored-by: Alex van Andel <me@alexvanandel.com>

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-04-22 10:58:03 +00:00
dd8968d0e7 Powered by for ee (#8434)
* moved poweredBy into ee/ to make it possible to change it when a whitelabel is applied

* added text-emphasis for custom powered by text in PoweredBy

---------

Co-authored-by: René Müller <rene.mueller@clicksports.de>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-22 01:57:53 +02:00
Crowdin Bot 827bebbc31 New Crowdin translations by Github Action 2023-04-21 22:44:24 +00:00
Joe Au-Yeung d97ac0e0f8 Fix booking page when toggling payment app settings (#8451)
* Use getPaymentAppData on booking list item

* Use paymentAppdata for event type description

* Remove console.log

* Type fix

* Type fixes

* Remove updating event type price
2023-04-21 15:43:31 -07:00
Crowdin Bot 600d2725aa New Crowdin translations by Github Action 2023-04-21 22:19:49 +00:00
zomars 2a09dc6a46 Comments out e2e page pause 2023-04-21 15:18:45 -07:00
Crowdin Bot 1f746b67f4 New Crowdin translations by Github Action 2023-04-21 22:00:44 +00:00
Omar López bb95f2ba39 Enable prisma json protocol (#7993)
* Enable prisma json protocol

* Upgrades prisma

* Upgrades prisma
2023-04-21 18:59:42 -03:00
Alex van Andel 3d93b4127c Use avatar.png instead of base64. (#8450)
* Use avatar.png instead of base64.

* Removed another case of base64 avatar use
2023-04-21 20:00:31 +02:00
Efraín RochínandKeith Williams 094fae38fc Add log messages on cold/hot requests (#8420)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-04-21 12:08:59 -03:00
Alex van Andel 3af614f778 v2.8.8
release.yml / release (push) Canceled after 0s
2023-04-21 16:21:23 +02:00
Nafees NazikandAlex van Andel 77b41a4623 feat: add support endpoint (#8404)
* feat: open intercom

* Remove redirect from middleware, use next.config.js instead

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-04-21 14:18:57 +00:00
Crowdin Bot c23738b885 New Crowdin translations by Github Action 2023-04-21 13:50:42 +00:00
alannncandAlex van Andel afe1f5b72f Fix/seats-cancel-links (#8394)
* Fix cancel links

* Use searchParams API to build cancelLink

* Fix cancel showSeats

* Added test for owner cancel attendee list

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-04-21 15:49:53 +02:00
Crowdin Bot 5ce341093b New Crowdin translations by Github Action 2023-04-21 11:48:02 +00:00
Crowdin Bot fc6d2839fb New Crowdin translations by Github Action 2023-04-21 11:47:08 +00:00
2fadba85f4 Fix workflow not firing for event types without video app as location (#8435)
* fix error when videoCallUrl was null

* make sure videocallurl is not null

* Revert "fix error when videoCallUrl was null"

This reverts commit f23776ec8be1be334245b7e3f8a977f7e73591f1.

* Update packages/features/bookings/lib/handleNewBooking.ts

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

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-04-21 11:46:23 +00:00
sean-brydon 473b36a6be Bulk update modal (#8433)
* Use checkbox component

* Update packages/features/eventtypes/components/BulkEditDefaultConferencingModal.tsx
2023-04-21 11:04:27 +00:00
Crowdin Bot f74b9eb70a New Crowdin translations by Github Action 2023-04-21 10:51:27 +00:00
Hariom Balhara a845841ad0 Prevent looping b/w dark and light (#8440) 2023-04-21 16:20:27 +05:30
Crowdin Bot dba80a1b64 New Crowdin translations by Github Action 2023-04-21 08:25:54 +00:00
sean-brydon 41558d297c Brand colour on setup tab (#8430) 2023-04-21 10:24:59 +02:00
Crowdin Bot cd3946d482 New Crowdin translations by Github Action 2023-04-21 00:46:08 +00:00
Alex van Andel dcbf695217 Remove getDirFromLang, in favour of doing this in the I18nLanguageHan… (#8425)
* Remove getDirFromLang, in favour of doing this in the I18nLanguageHandler exclusively

* Small tweak
2023-04-21 00:45:22 +00:00
Alex van Andel 49cbdda4cf Use modularizeImports for @heroicons (#8422) 2023-04-21 00:58:57 +02:00
Crowdin Bot 22f458cb74 New Crowdin translations by Github Action 2023-04-20 22:34:42 +00:00
Peer Richelsen 561d9f154b misc frontend fixes (#8416)
* (temp) dark mode changes (#8415)

* fixes skeletons, some wrong buttons, spacing, settings
2023-04-20 22:33:49 +00:00
Peer Richelsen 0c9b9bc296 Update package.json
release.yml / release (push) Canceled after 0s
2023-04-21 00:11:59 +02:00
Alex van Andel 552bfcc7db Moved forgot? behind the label (#8421) 2023-04-20 22:06:35 +00:00
986f9dca8d Fix: appKeys handling (#8406)
* Handle appKeys save and read correctly

* Add missing zod file for zohocrm which needs keys

* Show team invites regardless of user plan

* Linting

---------

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2023-04-20 21:14:50 +00:00
Omar López 3af5a7404e Show team invites regardless of user plan (#8417) 2023-04-20 20:56:38 +00:00
Crowdin Bot 10d3c6ecaa New Crowdin translations by Github Action 2023-04-20 20:02:05 +00:00
Hariom BalharaandKeith Williams a0c37ea4b7 Remove css vars handling from PageWrapper. It was removed from _app.tsx earlier (#8401)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-04-20 17:01:21 -03:00
Crowdin Bot 798999ad1a New Crowdin translations by Github Action 2023-04-20 18:54:58 +00:00
Crowdin Bot d40e306baa New Crowdin translations by Github Action 2023-04-20 18:54:12 +00:00
Crowdin Bot e558e61f6b New Crowdin translations by Github Action 2023-04-20 18:53:15 +00:00
Crowdin Bot 210eafcd6e New Crowdin translations by Github Action 2023-04-20 18:52:13 +00:00
Crowdin Bot c05e7d75a5 New Crowdin translations by Github Action 2023-04-20 18:51:26 +00:00
Crowdin Bot d0ebf3319a New Crowdin translations by Github Action 2023-04-20 18:50:44 +00:00
Keith Williams 9eb4187644 Added write permission for pull requests in cron action (#8391) 2023-04-20 11:49:52 -07:00
Crowdin Bot 958cebc82c New Crowdin translations by Github Action 2023-04-20 15:57:11 +00:00
Crowdin Bot 0c36dcac4c New Crowdin translations by Github Action 2023-04-20 15:56:18 +00:00
alannncandAlex van Andel fe996b4e41 Fix/user creds (#8393)
* fix-user-creds

* Type fixes

* Type fixes

* Type fixes

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-04-20 15:55:19 +00:00
Alex van Andel c1c95f711b v2.8.6
release.yml / release (push) Canceled after 0s
2023-04-20 15:54:26 +02:00
Syed Ali Shahbaz 7cb7006c54 CalDAV - improves the UTC offset eval to include DST (#8399)
* improves the UTC offset eval to include DST

* remove debug remnants
2023-04-20 15:51:39 +02:00
Alex van Andel e1403bfbeb Initial implementation, getAppLogos() (#8405)
* Initial implementation, getAppLogos()

* Handle localhost:3000 correctly

* New URL wasn't working with all hostnames

* Only get the first of the subdomainParts, join not needed

* rename url -> hostname

* getAppLogos -> getTeamLogos
2023-04-20 13:43:06 +00:00
Hariom Balhara c38dd2d072 Hotfix: Fix theme not changing once page has started loading (#8400)
* Fix theme not changing once page is loaded

* Add toggle case in playground
2023-04-20 11:57:28 +00:00
Crowdin Bot f9d9e74d6b New Crowdin translations by Github Action 2023-04-20 11:39:48 +00:00
sean-brydon 2ab597addc Fixing default event location URL for dynamic events (#8396)
* Set bookingLocaiton to URL if there is a default location

* Update packages/features/bookings/lib/handleNewBooking.ts

* Suggestions implmented

* Remove old implemntation

* Cleanup
2023-04-20 13:38:57 +02:00
Hariom Balhara 33e394d5ed One App shouldnt break other app, or the entire cal.com build. Also, handle slug change in seeding update (#8294) 2023-04-20 04:41:14 +00:00
Crowdin Bot 4577de3b17 New Crowdin translations by Github Action 2023-04-19 23:03:32 +00:00
Crowdin Bot 658b529840 New Crowdin translations by Github Action 2023-04-19 23:02:40 +00:00
Alex van Andel 4aed769da4 Invert domain check, make default logo work 2023-04-20 01:01:29 +02:00
Crowdin Bot 6c753290aa New Crowdin translations by Github Action
release.yml / release (push) Canceled after 0s
2023-04-19 22:20:17 +00:00
Alex van Andel 59c91607db v2.8.5 2023-04-20 00:19:14 +02:00
b0530d59af Remove Intl.Provider (#8208)
* Add payment option to schema

* Add payment option to Stripe zod

* Set payment option on event type

* Create manual payment intent in Stripe

* Set payment option from Stripe app

* Add payment option to DB

* Pass React.ReactNode to checkbox

* Create uncaptured payment intent

* WIP

* Capture card in setup intent

* Show charge card option

* Charge card from booking page

* Bug fixes

* Clean up

* Clean up app card

* Add no-show fee messaging on booking page

* Send payment email on payment & add price

* Fix messaging

* Create no show fee charged email

* Send charge fee collected email

* Disable submit on card failure

* Clean up

* Serverside prevent charging card again if already charged

* Only confirm booking if paid for

* Type fixes

* More type fixes

* More type fixes

* Type fix

* Type fixes

* UI changes

* Payment component rework

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

Co-authored-by: Alex van Andel <me@alexvanandel.com>

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

Co-authored-by: Alex van Andel <me@alexvanandel.com>

* Update apps/web/components/dialog/ChargeCardDialog.tsx

Co-authored-by: Alex van Andel <me@alexvanandel.com>

* Update packages/trpc/server/routers/viewer/payments.tsx

Co-authored-by: Alex van Andel <me@alexvanandel.com>

* Revert GTM config

* Adjust payment option dropdown

* Show alert when seats are set

* Small bug fixes

* Create collect card method

* clean up

* Prevent seats & charge no-show fee to be enabled together

* Do not charge no-show fee on unconfirmed bookings

* Add check to collect card method

* Webhook send request emails

* Fix some dark mode colours

* Change awaiting payment language

* Type fixes

* Set height of Select and TextField both to 38px to fix alignment

* Fix message seats & payment error message

* Type fix

* Remove Intl.Provider

* Add percentFeePercentage

* WIP

* Use i18n language

* Type fix

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Omar López <zomars@me.com>
2023-04-19 21:38:06 +00:00
Omar López b1ff829745 Warns about lodash usage (#8390) 2023-04-19 23:07:48 +02:00
sean-brydonandzomars d202b536b5 Feat/subdomain logochanges (#8264)
* logo map

* Archimed logo

* usememo

* Subdomain logo logic

* or our logo

* Update packages/features/orgs/SubdomainProvider.tsx

* Provider comments

* Move DB logic to db

* Fix a11y on alt tag

* Added example for dynamic endpoint

* Move to API approach

* Implement Icon logo on subdomain level

---------

Co-authored-by: zomars <zomars@me.com>
2023-04-19 13:55:40 -07:00
Joe Au-Yeung 2ac5ef10ba Add event type redirect URL (#8385) 2023-04-19 13:46:59 -07:00
8a2ab68e9a feat: add user hash for intercom (#8263)
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-19 22:22:07 +02:00
877220caa0 Event Type: Title not displayed in the mobile view (#7451)
* Event Type: Title not displayed in the mobile view

* Fix toggle not updating in UI

---------

Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-04-19 13:17:54 -07:00
0686c08de3 Fix/insights load speed (#8364)
* Adding skipBatch to trpc calls

* Use view in trpc insights

* Added performance data creation on seed-insights

* fix some scenarios

* Fix migration view fields

* Prevent revalidating too often

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2023-04-19 20:14:09 +00:00
Alex van Andel d3f1f8b906 Shaves off an easy 1.4kb win from the global package (#8382) 2023-04-19 16:23:49 -03:00
Crowdin Bot 33a69d3ef6 New Crowdin translations by Github Action 2023-04-19 18:25:28 +00:00
Alex van Andel d91b451d7b Also unpack React / don't directly import React (#8384) 2023-04-19 20:24:34 +02:00
Nafees Nazik 9e17149e5e fix: limit avatar upload size (#8135) 2023-04-19 18:19:31 +02:00
Peer Richelsen de61b032b7 RFC: added brandcolor for switch (#8213) 2023-04-19 14:39:23 +00:00
bdbf9bd04d perf: reduce bundle size (#8337)
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-04-19 19:45:08 +05:30
Henrique PachecoandUdit Takkar ccd589ef38 fix: addon wrapper to password input field (#8320)
* fix: addon wrapper to password input field

* fix: tooltip

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-04-19 14:12:55 +00:00
Crowdin Bot b4c3753566 New Crowdin translations by Github Action 2023-04-19 14:03:48 +00:00
2fdadb98ac fix the issue "ReferenceError: window is not defined" (#8193)
Co-authored-by: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Co-authored-by: nafees nazik <nafeesnazik21@gmail.com>
2023-04-19 19:32:55 +05:30
Crowdin Bot a33847a878 New Crowdin translations by Github Action 2023-04-19 13:03:16 +00:00
Nafees Nazik 88cf535c8e fix: address input (#8352) 2023-04-19 13:02:26 +00:00
Carina WollendorferandCarinaWolli ea0b59d7c0 Fixes editor not saving new value (#8379)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-04-19 17:10:18 +05:30
Hariom BalharaandPeer Richelsen 39b93cdb5b Fix Editor not triggering setText (#8368)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-19 12:25:05 +02:00
Nafees Nazik e27fc05d5f fix: frontend issues and dark mode regressions (#8373) 2023-04-19 12:14:59 +02:00
Carina WollendorferandCarinaWolli 20c010fef1 Revert "Date override fixes (#8330)" (#8369)
release.yml / release (push) Canceled after 0s
This reverts commit 3ef328421f.

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-04-19 08:43:08 +00:00
Peer Richelsen 1a7911e9e2 Update package.json
release.yml / release (push) Canceled after 0s
2023-04-19 08:57:31 +02:00
Crowdin Bot cb09b5bc5f New Crowdin translations by Github Action 2023-04-19 06:47:15 +00:00
Efraín Rochín a99031bdd2 fix: [CAL-1490] Google Cal/Outlook not actively syncing/self hosted (#8305) 2023-04-19 08:46:17 +02:00
ada8a98362 chore: add database indexes (#8361)
Co-authored-by: Mythie <me@lucasjamessmith.me>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-18 16:50:42 -07:00
Yagiz Nizipli e176e81bae perf: improve bookings performance (#8362) 2023-04-18 16:48:53 -07:00
zomars b64dd66c2c Fixes bundle comments
https://github.com/hashicorp/nextjs-bundle-analysis/issues/52#issuecomment-1513913373
2023-04-18 16:40:06 -07:00
Crowdin Bot 7fd162601b New Crowdin translations by Github Action 2023-04-18 23:29:50 +00:00
c1f346c935 Cal 1099 availability section defined hour mismatch (#7416)
* CAL-1099 Add prop for availability in formMethods

* fix: show correct availabilty name

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

* CAL - 1099 / fix 12 hr time format bug

* fix: remove useEffect and fix vertical tab item ui

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

* Fixes merge conflicts

* Update index.tsx

* More conflict fixes

* Update packages/ui/components/navigation/tabs/VerticalTabs.tsx

* Type fixes

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: zomars <zomars@me.com>
2023-04-18 23:29:02 +00:00
Keith Williams 36d47bd2d8 v2.8.3
release.yml / release (push) Canceled after 0s
2023-04-18 18:22:24 -03:00
26cdb6521f perf: Adds PageWrapper to pages (#8344)
* Adds PageWrapper to pages

* Add missing PageWrapper

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-04-18 15:45:32 -03:00
Leo Giovanetti 014f1986de Adding missing mutation (#8357)
* Adding missing mutation

* Returning to prop for disabled
2023-04-18 16:48:19 +00:00
Carina WollendorferandCarinaWolli 3ef328421f Date override fixes (#8330)
* fix date override for fixed round robin + time zone in date override

* check if slot is within working hours of fixed hosts

* add test for date override in different time zone

* fix date overrides for not fixed hosts (round robin)

* code clean up

* fix added test

* use the correct timezone of user for date overrides

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-04-18 17:21:27 +02:00
Ben HybertandHariom Balhara c4fe69d826 Add Plausible (Custom) App (#8189)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-04-18 19:24:51 +05:30
Hariom Balhara 6fd64b2209 Fix: cssVars API not working correctly on Booker Timeslot selection page (#8296)
* Use a method out of react scope to set CSS vars as that doesnt need react

* Refactor
2023-04-18 12:53:46 +00:00
Crowdin Bot 585e36793a New Crowdin translations by Github Action 2023-04-18 12:53:33 +00:00
Crowdin Bot 09b9370e13 New Crowdin translations by Github Action 2023-04-18 12:52:43 +00:00
Udit Takkar a76a4996b4 fix: address input (#8349)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-04-18 18:21:46 +05:30
Hariom Balhara b28d60b6dc API Support: Manage Booking Questions and responses (#8115) 2023-04-18 18:05:06 +05:30
Nafees Nazik a716eda14c fix: use the correct timezone of the user in booking detail page (#7915)
* fix: send timezone in query

* fiz: timezone

* fix: remove tz from createPayment

* fix: send timezone in query

* fiz: timezone

* fix: remove tz

* fix: remove tz

* fix: remove timezone query

* fix: timezone
2023-04-18 14:29:26 +02:00
Crowdin Bot 99f1524183 New Crowdin translations by Github Action 2023-04-18 12:10:14 +00:00
933 changed files with 26683 additions and 13457 deletions
-42
View File
@@ -1,42 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-outside-of-docker
{
"name": "Docker outside of Docker",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/base:bullseye",
"features": {
"ghcr.io/devcontainers/features/docker-from-docker:1": {
"version": "latest",
"enableNonRootDocker": "true",
"moby": "true"
},
"ghcr.io/devcontainers/features/node:1": {},
"ghcr.io/devcontainers-contrib/features/npm-package:1": {},
"ghcr.io/devcontainers-contrib/features/jest:2": {},
"ghcr.io/devcontainers-contrib/features/prisma:2": {},
"ghcr.io/guiyomh/features/vim:0": {}
},
// Use this environment variable if you need to bind mount your local source code into a new container.
"remoteEnv": {
"LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}"
},
"hostRequirements": {
"cpus": 4,
"memory": "8gb"
},
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "./deploy/install.sh"
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
+2 -1
View File
@@ -16,11 +16,12 @@
# - WEB3
# - SALESFORCE
# - ZOHOCRM
# - ZOHO_BIGIN
# - APP STORE **********************************************************************************************
# ⚠️ ⚠️ ⚠️ THESE WILL BE MIGRATED TO THE DATABASE TO PREVENT AWS's 4KB ENV QUOTA ⚠️ ⚠️ ⚠️
# - DAILY.CO VIDEO
# Enables Cal Video. to get your key
# Enables Cal Video. to get your key
# 1. Visit our [Daily.co Partnership Form](https://go.cal.com/daily) and enter your information
# 2. From within your dashboard, go to the [developers](https://dashboard.daily.co/developers) tab.
# @see https://github.com/calcom/cal.com#obtaining-daily-api-credentials
+12 -5
View File
@@ -79,6 +79,9 @@ CALENDSO_ENCRYPTION_KEY=
# Intercom Config
NEXT_PUBLIC_INTERCOM_APP_ID=
# Secret to enable Intercom Identity Verification
INTERCOM_SECRET=
# Zendesk Config
NEXT_PUBLIC_ZENDESK_KEY=
@@ -135,12 +138,16 @@ API_KEY_PREFIX=cal_
EMAIL_FROM='notifications@yourselfhostedcal.com'
# Configure SMTP settings (@see https://nodemailer.com/smtp/).
# Configuration to receive emails locally (mailhog)
EMAIL_SERVER_HOST='localhost'
EMAIL_SERVER_PORT=1025
# Note: The below configuration for Office 365 has been verified to work.
EMAIL_SERVER_HOST='smtp.office365.com'
EMAIL_SERVER_PORT=587
EMAIL_SERVER_USER='<office365_emailAddress>'
# EMAIL_SERVER_HOST='smtp.office365.com'
# EMAIL_SERVER_PORT=587
# EMAIL_SERVER_USER='<office365_emailAddress>'
# Keep in mind that if you have 2FA enabled, you will need to provision an App Password.
EMAIL_SERVER_PASSWORD='<office365_password>'
# EMAIL_SERVER_PASSWORD='<office365_password>'
# The following configuration for Gmail has been verified to work.
# EMAIL_SERVER_HOST='smtp.gmail.com'
@@ -175,4 +182,4 @@ CSP_POLICY=
# Vercel Edge Config
EDGE_CONFIG=
NEXT_PUBLIC_MINUTES_TO_BOOK=5 # Minutes
NEXT_PUBLIC_MINUTES_TO_BOOK=5 # Minutes
+1
View File
@@ -2,6 +2,7 @@ name: Cron - mark stale for inactive issues
permissions:
issues: write
pull-requests: write
on:
# "Scheduled workflows run on the latest commit on the default or base branch."
+2 -2
View File
@@ -24,7 +24,7 @@ jobs:
- name: Analyze bundle
run: |
cd apps/web
npx -p nextjs-bundle-analysis report
npx -p nextjs-bundle-analysis@0.5.0 report
- name: Upload bundle
uses: actions/upload-artifact@v2
@@ -76,7 +76,7 @@ jobs:
id: fc
with:
issue-number: ${{ github.event.number }}
body-includes: "<!-- __NEXTJS_BUNDLE -->"
body-includes: "<!-- __NEXTJS_BUNDLE_@calcom/web -->"
- name: Create Comment
uses: peter-evans/create-or-update-comment@v1.4.4
+1
View File
@@ -7,6 +7,7 @@ on:
paths-ignore:
- "**.md"
- ".github/CODEOWNERS"
merge_group:
workflow_dispatch:
concurrency:
@@ -1,7 +1,6 @@
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!) {
@@ -38,7 +37,7 @@ async function applyLabelFromLinkedIssueToPR(pr, token) {
headers: {
"Content-Type": "application/json",
"Content-Length": graphqlData.length,
"Authorization": "Bearer " + token,
Authorization: "Bearer " + token,
"User-Agent": "Node.js",
},
};
@@ -52,14 +51,7 @@ async function applyLabelFromLinkedIssueToPR(pr, token) {
responseBody += chunk;
});
response.on("end", () => {
resolve(
JSON.parse(responseBody)
?.data
?.repository
?.pullRequest
?.closingIssuesReferences
?.nodes
);
resolve(JSON.parse(responseBody)?.data?.repository?.pullRequest?.closingIssuesReferences?.nodes);
});
});
@@ -96,7 +88,7 @@ async function applyLabelFromLinkedIssueToPR(pr, token) {
headers: {
"Content-Type": "application/json",
"Content-Length": labelsData.length,
"Authorization": "Bearer " + token,
Authorization: "Bearer " + token,
"User-Agent": "Node.js",
},
};
@@ -126,11 +118,9 @@ async function applyLabelFromLinkedIssueToPR(pr, token) {
console.log(`Error labelling PR: ${labelResult.message}`);
continue;
}
console.log(
`Applied labels: ${labels.join(", ")} to PR #${
pr.number
} from linked issue #${issue.number}`
`Applied labels: ${labels.join(", ")} to PR #${pr.number} from linked issue #${issue.number}`
);
}
}
+2 -1
View File
@@ -65,8 +65,9 @@ tsconfig.tsbuildinfo
# turbo
.turbo
# Prisma-Zod
# Prisma generated files
packages/prisma/zod/*.ts
packages/prisma/enums
# Builds
dist
+1
View File
@@ -14,4 +14,5 @@ public
.DS_Store
.eslintignore
packages/prisma/zod
packages/prisma/enums
apps/web/public/embed
+1 -1
View File
@@ -6,6 +6,6 @@
},
"typescript.preferences.importModuleSpecifier": "non-relative",
"spellright.language": ["en"],
"spellright.documentTypes": ["markdown", "typescript"],
"spellright.documentTypes": ["markdown", "typescript", "typescriptreact"],
"tailwindCSS.experimental.classRegex": [["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]]
}
+55 -44
View File
@@ -24,7 +24,7 @@
<p align="center">
<a href="https://cal.com/slack"><img src="https://img.shields.io/badge/Slack-calendso.slack.com-%234A154B" alt="Join Cal.com Slack"></a>
<a href="https://www.producthunt.com/posts/calendso"><img src="https://img.shields.io/badge/Product%20Hunt-%231%20Product%20of%20the%20Month-%23DA552E" alt="Product Hunt"></a>
<a href="https://producthunt.com/posts/calendso"><img src="https://img.shields.io/badge/Product%20Hunt-%231%20Product%20of%20the%20Month-%23DA552E" alt="Product Hunt"></a>
<a href="https://status.cal.com"><img height="20px" src="https://betteruptime.com/status-badges/v1/monitor/a9kf.svg" alt="Uptime"></a>
<a href="https://github.com/calcom/cal.com/stargazers"><img src="https://img.shields.io/github/stars/calcom/cal.com" alt="Github Stars"></a>
<a href="https://news.ycombinator.com/item?id=34507672"><img src="https://img.shields.io/badge/Hacker%20News-%231-%23FF6600" alt="Hacker News"></a>
@@ -38,13 +38,13 @@
<a href="https://github.com/calcom/cal.com/issues?q=is:issue+is:open+label:%22%F0%9F%99%8B%F0%9F%8F%BB%E2%80%8D%E2%99%82%EF%B8%8Fhelp+wanted%22"><img src="https://img.shields.io/badge/Help%20Wanted-Contribute-blue"></a>
<a href="https://cal.com/figma"><img src="https://img.shields.io/badge/Figma-Design%20System-blueviolet"></a>
<a href="https://calendso.slack.com/archives/C02BY67GMMW"><img src="https://img.shields.io/badge/translations-contribute-brightgreen" /></a>
<a href="https://www.contributor-covenant.org/version/1/4/code-of-conduct/ "><img src="https://img.shields.io/badge/Contributor%20Covenant-1.4-purple" /></a>
<a href="https://contributor-covenant.org/version/1/4/code-of-conduct/ "><img src="https://img.shields.io/badge/Contributor%20Covenant-1.4-purple" /></a>
<a href="https://console.algora.io/org/cal/bounties?status=open"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Fcal%2Fbounties%3Fstatus%3Dopen" /></a>
</p>
<!-- ABOUT THE PROJECT -->
## About The Project
## About the Project
<img width="100%" alt="booking-screen" src="https://user-images.githubusercontent.com/8019099/176390354-f1bc7069-0341-437a-9bb8-eb41092b4016.gif">
@@ -53,13 +53,13 @@
The open source Calendly alternative. You are in charge
of your own data, workflow and appearance.
Calendly and other scheduling tools are awesome. It made our lives massively easier. We're using it for business meetings, seminars, yoga classes and even calls with our families. However, most tools are very limited in terms of control and customisations.
Calendly and other scheduling tools are awesome. It made our lives massively easier. We're using it for business meetings, seminars, yoga classes and even calls with our families. However, most tools are very limited in terms of control and customizations.
That's where Cal.com comes in. Self-hosted or hosted by us. White-label by design. API-driven and ready to be deployed on your own domain. Full control of your events and data.
## Recognition
#### Hacker News
#### [Hacker News](https://news.ycombinator.com/from?site=cal.com)
<a href="https://news.ycombinator.com/item?id=34507672">
<img
@@ -77,22 +77,22 @@ That's where Cal.com comes in. Self-hosted or hosted by us. White-label by desig
/>
</a>
#### [Product Hunt](https://www.producthunt.com/posts/calendso?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-calendso)
#### [Product Hunt](https://producthunt.com/products/cal-com?utm_source=badge-top-post-badge&utm_medium=badge)
<a href="https://www.producthunt.com/posts/calendso?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-calendso" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/top-post-badge.svg?post_id=291910&theme=light&period=monthly" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a> <a href="https://www.producthunt.com/posts/calendso?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-calendso" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=291910&theme=light" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a> <a href="https://www.producthunt.com/stories/how-this-open-source-calendly-alternative-rocketed-to-product-of-the-day" target="_blank"><img src="https://cal.com/maker-grant.svg" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<a href="https://producthunt.com/posts/calendso?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-calendso" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/top-post-badge.svg?post_id=291910&theme=light&period=monthly" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a> <a href="https://producthunt.com/posts/calendso?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-calendso" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=291910&theme=light" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a> <a href="https://producthunt.com/stories/how-this-open-source-calendly-alternative-rocketed-to-product-of-the-day" target="_blank"><img src="https://cal.com/maker-grant.svg" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
### Built With
- [Next.js](https://nextjs.org/?ref=cal.com)
- [tRPC](https://trpc.io/?ref=cal.com)
- [React](https://reactjs.org/?ref=cal.com)
- [Tailwind](https://tailwindcss.com/?ref=cal.com)
- [Prisma](https://prisma.io/?ref=cal.com)
- [React.js](https://reactjs.org/?ref=cal.com)
- [Tailwind CSS](https://tailwindcss.com/?ref=cal.com)
- [Prisma.io](https://prisma.io/?ref=cal.com)
- [Daily.co](https://go.cal.com/daily)
## Stay Up-to-Date
Cal officially launched as v.1.0 on 15th of September, however a lot of new features are coming. Watch **releases** of this repository to be notified for future updates:
Cal.com officially launched as v.1.0 on 15th of September, however a lot of new features are coming. Watch **releases** of this repository to be notified for future updates:
![cal-star-github](https://user-images.githubusercontent.com/8019099/154853944-a9e3c999-3da3-4048-b149-b4f73893c6fb.gif)
@@ -104,7 +104,7 @@ To get a local copy up and running, please follow these simple steps.
### Prerequisites
Here is what you need to be able to run Cal.
Here is what you need to be able to run Cal.com.
- Node.js (Version: >=15.x <17)
- PostgreSQL
@@ -121,6 +121,9 @@ Here is what you need to be able to run Cal.
```sh
git clone https://github.com/calcom/cal.com.git
```
> If you are on windows, run the following command on `gitbash` with admin privileges: <br>
```git clone -c core.symlinks=true https://github.com/calcom/cal.com.git``` <br>
See [docs](https://cal.com/docs/how-to-guides/how-to-troubleshoot-symbolic-link-issues-on-windows#enable-symbolic-links) for more details.
1. Go to the project folder
@@ -134,10 +137,10 @@ Here is what you need to be able to run Cal.
yarn
```
1. Set up your .env file
1. Set up your `.env` file
- Duplicate `.env.example` to `.env`
- Use `openssl rand -base64 32` to generate a key and add it under `NEXTAUTH_SECRET` in the .env file.
- Use `openssl rand -base64 24` to generate a key and add it under `CALENDSO_ENCRYPTION_KEY` in the .env file.
- Use `openssl rand -base64 32` to generate a key and add it under `NEXTAUTH_SECRET` in the `.env` file.
- Use `openssl rand -base64 24` to generate a key and add it under `CALENDSO_ENCRYPTION_KEY` in the `.env` file.
#### Quick start with `yarn dx`
@@ -150,7 +153,7 @@ yarn dx
#### Development tip
> Add `NEXT_PUBLIC_DEBUG=1` anywhere in your `.env` to get logging information for all the queries and mutations driven by **trpc**.
> Add `NEXT_PUBLIC_DEBUG=1` anywhere in your `.env` to get logging information for all the queries and mutations driven by **tRPC**.
```sh
echo 'NEXT_PUBLIC_DEBUG=1' >> .env
@@ -158,16 +161,16 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env
#### Manual setup
1. Configure environment variables in the `.env` file. Replace `<user>`, `<pass>`, `<db-host>`, `<db-port>` with their applicable values
1. Configure environment variables in the `.env` file. Replace `<user>`, `<pass>`, `<db-host>`, and `<db-port>` with their applicable values
```
DATABASE_URL='postgresql://<user>:<pass>@<db-host>:<db-port>'
```
<details>
<summary>If you don't know how to configure the DATABASE_URL, then follow the steps here to create a quick DB using Heroku</summary>
<summary>If you don't know how to configure the DATABASE_URL, then follow the steps here to create a quick DB using Heroku.</summary>
1. Create a free account with [Heroku](https://www.heroku.com/).
1. Create a free account with [Heroku](https://heroku.com).
2. Create a new app.
<img width="306" alt="Create an App" src="https://user-images.githubusercontent.com/16905768/115322780-b3d58c00-a17e-11eb-8a52-b758fb0ea942.png">
@@ -184,19 +187,25 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env
6. Once you completed the above steps, click on your newly created `Heroku Postgres` and go to its `Settings`.
![image](https://user-images.githubusercontent.com/16905768/115323367-e92ea980-a17f-11eb-9ff4-dec95f2ec349.png)
7. In `Settings`, copy your URI to your Cal.com .env file and replace the `postgresql://<user>:<pass>@<db-host>:<db-port>` with it.
7. In `Settings`, copy your URI to your Cal.com `.env` file and replace the `postgresql://<user>:<pass>@<db-host>:<db-port>` with it.
![image](https://user-images.githubusercontent.com/16905768/115323556-4591c900-a180-11eb-9808-2f55d2aa3995.png)
![image](https://user-images.githubusercontent.com/16905768/115323697-7a9e1b80-a180-11eb-9f08-a742b1037f90.png)
8. To view your DB, once you add new data in Prisma, you can use [Heroku Data Explorer](https://heroku-data-explorer.herokuapp.com/).
8. To view your DB, once you add new data in Prisma, you can use [Heroku Data Explorer](https://heroku-data-explorer.herokuapp.com).
</details>
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 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`)
```sh
yarn workspace @calcom/prisma db-deploy
```
1. Run [mailhog](https://github.com/mailhog/MailHog) to view emails sent during development
```sh
docker pull mailhog/mailhog
docker run -d -p 8025:8025 -p 1025:1025 mailhog/mailhog
```
1. Run (in development mode)
@@ -206,7 +215,7 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env
#### Setting up your first user
1. Open [Prisma Studio](https://www.prisma.io/studio) to look at or modify the database content:
1. Open [Prisma Studio](https://prisma.io/studio) to look at or modify the database content:
```sh
yarn db-studio
@@ -251,7 +260,7 @@ yarn playwright show-report test-results/reports/playwright-html-report
yarn workspace @calcom/prisma db-migrate
```
(this can clear your development database in some cases)
(This can clear your development database in some cases)
In a production environment, run:
@@ -285,7 +294,7 @@ yarn playwright show-report test-results/reports/playwright-html-report
### Docker
The Docker configuration for Cal is an effort powered by people within the community.
The Docker configuration for Cal.com is an effort powered by people within the community.
If you want to contribute to the Docker repository, [reply here](https://github.com/calcom/docker/discussions/32).
@@ -298,14 +307,14 @@ Cal.com, Inc. does not provide official support for Docker, but we will accept f
### Heroku
<a href="https://heroku.com/deploy?template=https://github.com/calcom/cal.com">
<img width="185px" height="auto" src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy">
<img width="185px" height="auto" src="https://herokucdn.com/deploy/button.svg" alt="Deploy">
</a>
### Railway
[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template/cal)
You can deploy Cal on [Railway](https://railway.app/) using the button above. The team at Railway also have a [detailed blog post](https://blog.railway.app/p/calendso) on deploying Cal on their platform.
You can deploy Cal.com on [Railway](https://railway.app) using the button above. The team at Railway also have a [detailed blog post](https://blog.railway.app/p/calendso) on deploying Cal.com on their platform.
### Vercel
@@ -386,7 +395,7 @@ Don't code but still want to contribute? Join our [slack](https://cal.com/slack)
8. Select Web Application as the Application Type.
9. Under Authorized redirect URI's, select Add URI and then add the URI `<Cal.com URL>/api/integrations/googlecalendar/callback` and `<Cal.com URL>/api/auth/callback/google` replacing Cal.com URL with the URI at which your application runs.
10. The key will be created and you will be redirected back to the Credentials page. Select the newly generated client ID under OAuth 2.0 Client IDs.
11. Select Download JSON. Copy the contents of this file and paste the entire JSON string in the .env file as the value for GOOGLE_API_CREDENTIALS key.
11. Select Download JSON. Copy the contents of this file and paste the entire JSON string in the `.env` file as the value for `GOOGLE_API_CREDENTIALS` key.
#### _Adding google calendar to Cal.com App Store_
@@ -423,7 +432,7 @@ following
5. Choose "User-managed app" as the app type.
6. De-select the option to publish the app on the Zoom App Marketplace.
7. Click "Create".
8. Now copy the Client ID and Client Secret to your .env file into the `ZOOM_CLIENT_ID` and `ZOOM_CLIENT_SECRET` fields.
8. Now copy the Client ID and Client Secret to your `.env` file into the `ZOOM_CLIENT_ID` and `ZOOM_CLIENT_SECRET` fields.
9. Set the Redirect URL for OAuth `<Cal.com URL>/api/integrations/zoomvideo/callback` replacing Cal.com URL with the URI at which your application runs.
10. Also add the redirect URL given above as a allow list URL and enable "Subdomain check". Make sure, it says "saved" below the form.
11. You don't need to provide basic information about your app. Instead click at "Scopes" and then at "+ Add Scopes". On the left, click the category "Meeting" and check the scope `meeting:write`.
@@ -435,8 +444,8 @@ following
1. Visit our [Daily.co Partnership Form](https://go.cal.com/daily) and enter your information
2. From within your dashboard, go to the [developers](https://dashboard.daily.co/developers) tab.
3. Copy your API key.
4. Now paste the API key to your .env file into the `DAILY_API_KEY` field in your .env file.
5. If you have the [Daily Scale Plan](https://www.daily.co/pricing) set the `DAILY_SCALE_PLAN` variable to `true` in order to use features like video recording.
4. Now paste the API key to your `.env` file into the `DAILY_API_KEY` field in your `.env` file.
5. If you have the [Daily Scale Plan](https://daily.co/pricing) set the `DAILY_SCALE_PLAN` variable to `true` in order to use features like video recording.
### Obtaining HubSpot Client ID and Secret
@@ -445,7 +454,7 @@ following
3. Click "Create app" button top right.
4. Fill in any information you want in the "App info" tab
5. Go to tab "Auth"
6. Now copy the Client ID and Client Secret to your .env file into the `HUBSPOT_CLIENT_ID` and `HUBSPOT_CLIENT_SECRET` fields.
6. Now copy the Client ID and Client Secret to your `.env` file into the `HUBSPOT_CLIENT_ID` and `HUBSPOT_CLIENT_SECRET` fields.
7. Set the Redirect URL for OAuth `<Cal.com URL>/api/integrations/hubspot/callback` replacing Cal.com URL with the URI at which your application runs.
8. In the "Scopes" section at the bottom of the page, make sure you select "Read" and "Write" for scope called `crm.objects.contacts`
9. Click the "Save" button at the bottom footer.
@@ -458,31 +467,33 @@ following
3. Click "ADD CLIENT" button top right and select "Server-based Applications".
4. Fill in any information you want in the "Client Details" tab
5. Go to tab "Client Secret" tab.
6. Now copy the Client ID and Client Secret to your .env file into the `ZOHOCRM_CLIENT_ID` and `ZOHOCRM_CLIENT_SECRET` fields.
6. Now copy the Client ID and Client Secret to your `.env` file into the `ZOHOCRM_CLIENT_ID` and `ZOHOCRM_CLIENT_SECRET` fields.
7. Set the Redirect URL for OAuth `<Cal.com URL>/api/integrations/zohocrm/callback` replacing Cal.com URL with the URI at which your application runs.
8. In the "Settings" section check the "Multi-DC" option if you wish to use the same OAuth credentials for all data centers.
9. Click the "Save"/ "UPDATE" button at the bottom footer.
10. You're good to go. Now you can easily add your ZohoCRM integration in the Cal.com settings.
### Obtaining Zoho Bigin Client ID and Secret
[Follow these steps](./packages/app-store/zoho-bigin/)
## Workflows
### Setting up SendGrid for Email reminders
1. Create a SendGrid account (https://signup.sendgrid.com/)
2. Go to Settings -> API keys and create an API key
3. Copy API key to your .env file into the SENDGRID_API_KEY field
3. Copy API key to your `.env` file into the `SENDGRID_API_KEY` field
4. Go to Settings -> Sender Authentication and verify a single sender
5. Copy the verified E-Mail to your .env file into the SENDGRID_EMAIL field
6. Add your custom sender name to the .env file into the NEXT_PUBLIC_SENDGRID_SENDER_NAME field (fallback is Cal.com)
5. Copy the verified E-Mail to your `.env` file into the `SENDGRID_EMAIL` field
6. Add your custom sender name to the `.env` file into the `NEXT_PUBLIC_SENDGRID_SENDER_NAME` field (fallback is Cal.com)
### Setting up Twilio for SMS reminders
1. Create a Twilio account (https://www.twilio.com/try-twilio)
1. Create a Twilio account (https://twilio.com/try-twilio)
2. Click ‘Get a Twilio phone number’
3. Copy Account SID to your .env file into the TWILIO_SID field
4. Copy Auth Token to your .env file into the TWILIO_TOKEN field
5. Copy your Twilio phone number to your .env file into the TWILIO_PHONE_NUMBER field
6. Add your own sender id to the .env file into the NEXT_PUBLIC_SENDER_ID field (fallback is Cal)
3. Copy Account SID to your `.env` file into the `TWILIO_SID` field
4. Copy Auth Token to your `.env` file into the `TWILIO_TOKEN` field
5. Copy your Twilio phone number to your `.env` file into the `TWILIO_PHONE_NUMBER` field
6. Add your own sender id to the `.env` file into the `NEXT_PUBLIC_SENDER_ID` field (fallback is Cal.com)
7. Create a messaging service (Develop -> Messaging -> Services)
8. Choose any name for the messaging service
9. Click 'Add Senders'
@@ -490,15 +501,15 @@ following
11. Add the listed phone number
12. Leave all other fields as they are
13. Complete setup and click ‘View my new Messaging Service’
14. Copy Messaging Service SID to your .env file into the TWILIO_MESSAGING_SID field
14. Copy Messaging Service SID to your `.env` file into the `TWILIO_MESSAGING_SID` field
15. Create a verify service
16. Copy Verify Service SID to your .env file into the TWILIO_VERIFY_SID field
16. Copy Verify Service SID to your `.env` file into the `TWILIO_VERIFY_SID` field
<!-- LICENSE -->
## License
Distributed under the AGPLv3 License. See `LICENSE` for more information.
Distributed under the [AGPLv3 License](https://github.com/calcom/cal.com/blob/main/LICENSE). See `LICENSE` for more information.
<!-- ACKNOWLEDGEMENTS -->
+2
View File
@@ -4,6 +4,7 @@ module.exports = {
stories: [
"../intro.stories.mdx",
"../../../packages/ui/components/**/*.stories.mdx",
"../../../packages/atoms/**/*.stories.mdx",
"../../../packages/features/**/*.stories.mdx",
"../../../packages/ui/components/**/*.stories.@(js|jsx|ts|tsx)",
],
@@ -70,4 +71,5 @@ module.exports = {
return config;
},
typescript: { reactDocgen: "react-docgen" },
};
+264 -273
View File
@@ -1,5 +1,6 @@
import { Collapsible, CollapsibleContent } from "@radix-ui/react-collapsible";
import classNames from "classnames";
import type { TFunction } from "next-i18next";
import type { NextRouter } from "next/router";
import { useRouter } from "next/router";
import type { MutableRefObject, RefObject } from "react";
@@ -21,11 +22,10 @@ import {
TextArea,
TextField,
ColorPicker,
Select,
} from "@calcom/ui";
import { Code, Trello, Sun, ArrowLeft } from "@calcom/ui/components/icon";
import Select from "@components/ui/form/Select";
type EmbedType = "inline" | "floating-popup" | "element-click";
type EmbedFramework = "react" | "HTML";
@@ -305,190 +305,189 @@ const getEmbedTypeSpecificString = ({
return "";
};
const embeds: {
illustration: React.ReactElement;
title: string;
subtitle: string;
type: EmbedType;
}[] = [
{
title: "Inline Embed",
subtitle: "Loads your event type directly inline with your other website content.",
type: "inline",
illustration: (
<svg
width="100%"
height="100%"
className="rounded-md"
viewBox="0 0 308 265"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path
d="M0 1.99999C0 0.895423 0.895431 0 2 0H306C307.105 0 308 0.895431 308 2V263C308 264.105 307.105 265 306 265H2C0.895431 265 0 264.105 0 263V1.99999Z"
fill="white"
/>
<rect x="24" width="260" height="38.5" rx="6" fill="#F3F4F6" />
<rect x="24.5" y="51" width="139" height="163" rx="1.5" fill="#F8F8F8" />
<rect opacity="0.8" x="48" y="74.5" width="80" height="8" rx="6" fill="#F3F4F6" />
<rect x="48" y="86.5" width="48" height="4" rx="6" fill="#F3F4F6" />
<rect x="49" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="61" y="99.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<rect x="73" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="85" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="97" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="109" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="121" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="133" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="85" y="113.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="97" y="113.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="109" y="113.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="121" y="113.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="133" y="113.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="49" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="61" y="125.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<path
d="M61 124.5H67V122.5H61V124.5ZM68 125.5V131.5H70V125.5H68ZM67 132.5H61V134.5H67V132.5ZM60 131.5V125.5H58V131.5H60ZM61 132.5C60.4477 132.5 60 132.052 60 131.5H58C58 133.157 59.3431 134.5 61 134.5V132.5ZM68 131.5C68 132.052 67.5523 132.5 67 132.5V134.5C68.6569 134.5 70 133.157 70 131.5H68ZM67 124.5C67.5523 124.5 68 124.948 68 125.5H70C70 123.843 68.6569 122.5 67 122.5V124.5ZM61 122.5C59.3431 122.5 58 123.843 58 125.5H60C60 124.948 60.4477 124.5 61 124.5V122.5Z"
fill="#3E3E3E"
/>
<rect x="73" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="85" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="97" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="109" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="121" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="133" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="49" y="137.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="61" y="137.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="73" y="137.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="85" y="137.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="97" y="137.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<rect x="109" y="137.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<rect x="121" y="137.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="133" y="137.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="49" y="149.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="61" y="149.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="73" y="149.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="85" y="149.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="97" y="149.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<rect x="109" y="149.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<rect x="121" y="149.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="133" y="149.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="49" y="161.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="61" y="161.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="73" y="161.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="85" y="161.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="97" y="161.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<rect x="109" y="161.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="24.5" y="51" width="139" height="163" rx="6" stroke="#292929" />
<rect x="176" y="50.5" width="108" height="164" rx="6" fill="#F3F4F6" />
<rect x="24" y="226.5" width="260" height="38.5" rx="6" fill="#F3F4F6" />
</svg>
),
},
{
title: "Floating pop-up button",
subtitle: "Adds a floating button on your site that launches Cal in a dialog.",
type: "floating-popup",
illustration: (
<svg
width="100%"
height="100%"
className="rounded-md"
viewBox="0 0 308 265"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path
d="M0 1.99999C0 0.895423 0.895431 0 2 0H306C307.105 0 308 0.895431 308 2V263C308 264.105 307.105 265 306 265H2C0.895431 265 0 264.105 0 263V1.99999Z"
fill="white"
/>
<rect x="24" width="260" height="38.5" rx="6" fill="#F3F4F6" />
<rect x="24" y="50.5" width="120" height="76" rx="6" fill="#F3F4F6" />
<rect x="24" y="138.5" width="120" height="76" rx="6" fill="#F3F4F6" />
<rect x="156" y="50.5" width="128" height="164" rx="6" fill="#F3F4F6" />
<rect x="24" y="226.5" width="260" height="38.5" rx="6" fill="#F3F4F6" />
<rect x="226" y="223.5" width="66" height="26" rx="6" fill="#292929" />
<rect x="242" y="235.5" width="34" height="2" rx="1" fill="white" />
</svg>
),
},
{
title: "Pop up via element click",
subtitle: "Open your Cal dialog when someone clicks an element.",
type: "element-click",
illustration: (
<svg
width="100%"
height="100%"
className="rounded-md"
viewBox="0 0 308 265"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path
d="M0 1.99999C0 0.895423 0.895431 0 2 0H306C307.105 0 308 0.895431 308 2V263C308 264.105 307.105 265 306 265H2C0.895431 265 0 264.105 0 263V1.99999Z"
fill="white"
/>
<rect x="24" y="0.50293" width="260" height="24" rx="6" fill="#F3F4F6" />
<rect x="24" y="35" width="259" height="192" rx="5.5" fill="#F9FAFB" />
<g filter="url(#filter0_i_3223_14162)">
<rect opacity="0.8" x="40" y="99" width="24" height="24" rx="2" fill="#E5E7EB" />
<rect x="40" y="127" width="48" height="8" rx="1" fill="#E5E7EB" />
<rect x="40" y="139" width="82" height="8" rx="1" fill="#E5E7EB" />
<rect x="40" y="151" width="34" height="4" rx="1" fill="#E5E7EB" />
<rect x="40" y="159" width="34" height="4" rx="1" fill="#E5E7EB" />
</g>
<rect x="152" y="48" width="2" height="169" rx="2" fill="#E5E7EB" />
const embeds = (t: TFunction) =>
(() => {
return [
{
title: t("inline_embed"),
subtitle: t("load_inline_content"),
type: "inline",
illustration: (
<svg
width="100%"
height="100%"
className="rounded-md"
viewBox="0 0 308 265"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path
d="M0 1.99999C0 0.895423 0.895431 0 2 0H306C307.105 0 308 0.895431 308 2V263C308 264.105 307.105 265 306 265H2C0.895431 265 0 264.105 0 263V1.99999Z"
fill="white"
/>
<rect x="24" width="260" height="38.5" rx="6" fill="#F3F4F6" />
<rect x="24.5" y="51" width="139" height="163" rx="1.5" fill="#F8F8F8" />
<rect opacity="0.8" x="48" y="74.5" width="80" height="8" rx="6" fill="#F3F4F6" />
<rect x="48" y="86.5" width="48" height="4" rx="6" fill="#F3F4F6" />
<rect x="49" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="61" y="99.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<rect x="73" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="85" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="97" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="109" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="121" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="133" y="99.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="85" y="113.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="97" y="113.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="109" y="113.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="121" y="113.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="133" y="113.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="49" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="61" y="125.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<path
d="M61 124.5H67V122.5H61V124.5ZM68 125.5V131.5H70V125.5H68ZM67 132.5H61V134.5H67V132.5ZM60 131.5V125.5H58V131.5H60ZM61 132.5C60.4477 132.5 60 132.052 60 131.5H58C58 133.157 59.3431 134.5 61 134.5V132.5ZM68 131.5C68 132.052 67.5523 132.5 67 132.5V134.5C68.6569 134.5 70 133.157 70 131.5H68ZM67 124.5C67.5523 124.5 68 124.948 68 125.5H70C70 123.843 68.6569 122.5 67 122.5V124.5ZM61 122.5C59.3431 122.5 58 123.843 58 125.5H60C60 124.948 60.4477 124.5 61 124.5V122.5Z"
fill="#3E3E3E"
/>
<rect x="73" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="85" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="97" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="109" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="121" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="133" y="125.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="49" y="137.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="61" y="137.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="73" y="137.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="85" y="137.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="97" y="137.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<rect x="109" y="137.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<rect x="121" y="137.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="133" y="137.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="49" y="149.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="61" y="149.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="73" y="149.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="85" y="149.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="97" y="149.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<rect x="109" y="149.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<rect x="121" y="149.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="133" y="149.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="49" y="161.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="61" y="161.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="73" y="161.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="85" y="161.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="97" y="161.5" width="6" height="6" rx="1" fill="#3E3E3E" />
<rect x="109" y="161.5" width="6" height="6" rx="1" fill="#C6C6C6" />
<rect x="24.5" y="51" width="139" height="163" rx="6" stroke="#292929" />
<rect x="176" y="50.5" width="108" height="164" rx="6" fill="#F3F4F6" />
<rect x="24" y="226.5" width="260" height="38.5" rx="6" fill="#F3F4F6" />
</svg>
),
},
{
title: t("floating_pop_up_button"),
subtitle: t("floating_button_trigger_modal"),
type: "floating-popup",
illustration: (
<svg
width="100%"
height="100%"
className="rounded-md"
viewBox="0 0 308 265"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path
d="M0 1.99999C0 0.895423 0.895431 0 2 0H306C307.105 0 308 0.895431 308 2V263C308 264.105 307.105 265 306 265H2C0.895431 265 0 264.105 0 263V1.99999Z"
fill="white"
/>
<rect x="24" width="260" height="38.5" rx="6" fill="#F3F4F6" />
<rect x="24" y="50.5" width="120" height="76" rx="6" fill="#F3F4F6" />
<rect x="24" y="138.5" width="120" height="76" rx="6" fill="#F3F4F6" />
<rect x="156" y="50.5" width="128" height="164" rx="6" fill="#F3F4F6" />
<rect x="24" y="226.5" width="260" height="38.5" rx="6" fill="#F3F4F6" />
<rect x="226" y="223.5" width="66" height="26" rx="6" fill="#292929" />
<rect x="242" y="235.5" width="34" height="2" rx="1" fill="white" />
</svg>
),
},
{
title: t("pop_up_element_click"),
subtitle: t("open_dialog_with_element_click"),
type: "element-click",
illustration: (
<svg
width="100%"
height="100%"
className="rounded-md"
viewBox="0 0 308 265"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path
d="M0 1.99999C0 0.895423 0.895431 0 2 0H306C307.105 0 308 0.895431 308 2V263C308 264.105 307.105 265 306 265H2C0.895431 265 0 264.105 0 263V1.99999Z"
fill="white"
/>
<rect x="24" y="0.50293" width="260" height="24" rx="6" fill="#F3F4F6" />
<rect x="24" y="35" width="259" height="192" rx="5.5" fill="#F9FAFB" />
<g filter="url(#filter0_i_3223_14162)">
<rect opacity="0.8" x="40" y="99" width="24" height="24" rx="2" fill="#E5E7EB" />
<rect x="40" y="127" width="48" height="8" rx="1" fill="#E5E7EB" />
<rect x="40" y="139" width="82" height="8" rx="1" fill="#E5E7EB" />
<rect x="40" y="151" width="34" height="4" rx="1" fill="#E5E7EB" />
<rect x="40" y="159" width="34" height="4" rx="1" fill="#E5E7EB" />
</g>
<rect x="152" y="48" width="2" height="169" rx="2" fill="#E5E7EB" />
<rect opacity="0.8" x="176" y="84" width="80" height="8" rx="2" fill="#E5E7EB" />
<rect x="176" y="96" width="48" height="4" rx="1" fill="#E5E7EB" />
<rect x="177" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="189" y="109" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="201" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="213" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="225" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="237" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="249" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="261" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="213" y="123" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="225" y="123" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="237" y="123" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="249" y="123" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="261" y="123" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="177" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="189" y="135" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="187.3" y="133.4" width="9" height="9" rx="1.5" stroke="#0D121D" />
<rect x="201" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="213" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="225" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="237" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="249" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="261" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="177" y="147" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="189" y="147" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="201" y="147" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="213" y="147" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="225" y="147" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="237" y="147" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="249" y="147" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="261" y="147" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="177" y="159" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="189" y="159" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="201" y="159" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="213" y="159" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="225" y="159" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="237" y="159" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="249" y="159" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="261" y="159" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="177" y="171" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="189" y="171" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="201" y="171" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="213" y="171" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="225" y="171" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="237" y="171" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="24" y="35" width="259" height="192" rx="5.5" stroke="#101010" />
<rect x="24" y="241.503" width="260" height="24" rx="6" fill="#F3F4F6" />
</svg>
),
},
];
})();
<rect opacity="0.8" x="176" y="84" width="80" height="8" rx="2" fill="#E5E7EB" />
<rect x="176" y="96" width="48" height="4" rx="1" fill="#E5E7EB" />
<rect x="177" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="189" y="109" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="201" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="213" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="225" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="237" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="249" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="261" y="109" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="213" y="123" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="225" y="123" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="237" y="123" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="249" y="123" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="261" y="123" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="177" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="189" y="135" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="187.3" y="133.4" width="9" height="9" rx="1.5" stroke="#0D121D" />
<rect x="201" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="213" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="225" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="237" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="249" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="261" y="135" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="177" y="147" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="189" y="147" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="201" y="147" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="213" y="147" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="225" y="147" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="237" y="147" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="249" y="147" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="261" y="147" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="177" y="159" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="189" y="159" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="201" y="159" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="213" y="159" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="225" y="159" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="237" y="159" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="249" y="159" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="261" y="159" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="177" y="171" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="189" y="171" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="201" y="171" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="213" y="171" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="225" y="171" width="6" height="6" rx="1" fill="#0D121D" />
<rect x="237" y="171" width="6" height="6" rx="1" fill="#E5E7EB" />
<rect x="24" y="35" width="259" height="192" rx="5.5" stroke="#101010" />
<rect x="24" y="241.503" width="260" height="24" rx="6" fill="#F3F4F6" />
</svg>
),
},
];
const tabs = [
{
name: "HTML",
@@ -534,11 +533,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previ
<!-- Cal ${embedType} embed code ends -->`
}
/>
<p className="text-subtle hidden text-sm">
{t(
"Need help? See our guides for embedding Cal on Wix, Squarespace, or WordPress, check our common questions, or explore advanced embed options."
)}
</p>
<p className="text-subtle hidden text-sm">{t("need_help_embedding")}</p>
</>
);
}),
@@ -622,7 +617,7 @@ Cal("init", {origin:"${WEBAPP_URL}"});
const ThemeSelectControl = ({ children, ...props }: ControlProps<{ value: Theme; label: string }, false>) => {
return (
<components.Control {...props}>
<Sun className="text-subtle ml-2 h-4 w-4" />
<Sun className="text-subtle mr-2 h-4 w-4" />
{children}
</components.Control>
);
@@ -642,7 +637,7 @@ const ChooseEmbedTypesDialogContent = () => {
</div>
</div>
<div className="items-start space-y-2 md:flex md:space-y-0">
{embeds.map((embed, index) => (
{embeds(t).map((embed, index) => (
<button
className="hover:bg-subtle bg-muted w-full rounded-md border border-transparent p-6 text-left hover:rounded-md ltr:mr-4 ltr:last:mr-0 rtl:ml-4 rtl:last:ml-0 lg:w-1/3"
key={index}
@@ -692,7 +687,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
});
const refOfEmbedCodesRefs = useRef(embedCodeRefs);
const embed = embeds.find((embed) => embed.type === embedType);
const embed = embeds(t).find((embed) => embed.type === embedType);
const [isEmbedCustomizationOpen, setIsEmbedCustomizationOpen] = useState(true);
const [isBookingCustomizationOpen, setIsBookingCustomizationOpen] = useState(true);
@@ -806,11 +801,11 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
const FloatingPopupPositionOptions = [
{
value: "bottom-right",
label: "Bottom Right",
label: "Bottom right",
},
{
value: "bottom-left",
label: "Bottom Left",
label: "Bottom left",
},
];
@@ -834,7 +829,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
</button>
{embed.title}
</h3>
<h4 className="text-emphasis mb-6 text-sm font-normal">{embed.subtitle}</h4>
<h4 className="text-subtle mb-6 text-sm font-normal">{embed.subtitle}</h4>
<div className="flex flex-col">
<div className={classNames("font-medium", embedType === "element-click" ? "hidden" : "")}>
<Collapsible
@@ -844,7 +839,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
<div className={classNames(embedType === "inline" ? "block" : "hidden")}>
{/*TODO: Add Auto/Fixed toggle from Figma */}
<div className="text-default mb-[9px] text-sm">Window sizing</div>
<div className="justify-left flex items-center !font-normal">
<div className="justify-left mb-6 flex items-center !font-normal ">
<div className="mr-[9px]">
<TextField
labelProps={{ className: "hidden" }}
@@ -892,10 +887,10 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
</div>
<div
className={classNames(
"mt-4 items-center justify-between",
"items-center justify-between",
embedType === "floating-popup" ? "text-emphasis" : "hidden"
)}>
<div className="mb-2 text-sm">Button Text</div>
<div className="mb-2 text-sm">Button text</div>
{/* Default Values should come from preview iframe */}
<TextField
labelProps={{ className: "hidden" }}
@@ -910,7 +905,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
};
});
}}
defaultValue="Book my Cal"
defaultValue={t("book_my_cal")}
required
/>
</div>
@@ -935,14 +930,14 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
});
}}
/>
<div className="text-default text-sm">Display Calendar Icon Button</div>
<div className="text-default my-2 text-sm">Display calendar icon</div>
</div>
<div
className={classNames(
"mt-4 items-center justify-between",
embedType === "floating-popup" ? "text-emphasis" : "hidden"
)}>
<div className="mb-2">Position of Button</div>
<div className="mb-2">Position of button</div>
<Select
onChange={(position) => {
setPreviewState((previewState) => {
@@ -959,54 +954,50 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
options={FloatingPopupPositionOptions}
/>
</div>
<div
className={classNames(
"mt-4",
embedType === "floating-popup" ? "text-emphasis" : "hidden"
)}>
<div>Button Color</div>
<div className="w-full">
<ColorPicker
popoverAlign="start"
container={dialogContentRef?.current ?? undefined}
defaultValue="#000000"
onChange={(color) => {
setPreviewState((previewState) => {
return {
...previewState,
floatingPopup: {
...previewState.floatingPopup,
buttonColor: color,
},
};
});
}}
/>
<div className="mt-3 flex flex-col xl:flex-row xl:justify-between">
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
<div className="whitespace-nowrap">Button color</div>
<div className="mt-2 w-40 xl:mt-0 xl:w-full">
<ColorPicker
className="w-[130px]"
popoverAlign="start"
container={dialogContentRef?.current ?? undefined}
defaultValue="#000000"
onChange={(color) => {
setPreviewState((previewState) => {
return {
...previewState,
floatingPopup: {
...previewState.floatingPopup,
buttonColor: color,
},
};
});
}}
/>
</div>
</div>
</div>
<div
className={classNames(
"mt-4",
embedType === "floating-popup" ? "text-emphasis" : "hidden"
)}>
<div>Text Color</div>
<div className="w-full">
<ColorPicker
popoverAlign="start"
container={dialogContentRef?.current ?? undefined}
defaultValue="#000000"
onChange={(color) => {
setPreviewState((previewState) => {
return {
...previewState,
floatingPopup: {
...previewState.floatingPopup,
buttonTextColor: color,
},
};
});
}}
/>
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
<div className="whitespace-nowrap">Text color</div>
<div className="mt-2 mb-6 w-40 xl:mt-0 xl:w-full">
<ColorPicker
className="w-[130px]"
popoverAlign="start"
container={dialogContentRef?.current ?? undefined}
defaultValue="#000000"
onChange={(color) => {
setPreviewState((previewState) => {
return {
...previewState,
floatingPopup: {
...previewState.floatingPopup,
buttonTextColor: color,
},
};
});
}}
/>
</div>
</div>
</div>
</CollapsibleContent>
@@ -1017,7 +1008,30 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
open={isBookingCustomizationOpen}
onOpenChange={() => setIsBookingCustomizationOpen((val) => !val)}>
<CollapsibleContent>
<div className="mt-6 text-sm">
<div className="text-sm">
<Label className="mb-6">
<div className="mb-2">Theme</div>
<Select
className="w-full"
defaultValue={ThemeOptions[0]}
components={{
Control: ThemeSelectControl,
IndicatorSeparator: () => null,
}}
onChange={(option) => {
if (!option) {
return;
}
setPreviewState((previewState) => {
return {
...previewState,
theme: option.value,
};
});
}}
options={ThemeOptions}
/>
</Label>
<div className="mb-6 flex items-center justify-start space-x-2 rtl:space-x-reverse">
<Switch
checked={previewState.hideEventTypeDetails}
@@ -1056,29 +1070,6 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
</div>
</Label>
))}
<Label>
<div className="mb-2">Theme</div>
<Select
className="w-full"
defaultValue={ThemeOptions[0]}
components={{
Control: ThemeSelectControl,
IndicatorSeparator: () => null,
}}
onChange={(option) => {
if (!option) {
return;
}
setPreviewState((previewState) => {
return {
...previewState,
theme: option.value,
};
});
}}
options={ThemeOptions}
/>
</Label>
</div>
</CollapsibleContent>
</Collapsible>
+9 -7
View File
@@ -1,7 +1,7 @@
import { isEmpty } from "lodash";
import { useTranslation } from "next-i18next";
import { useEffect } from "react";
import { getDirFromLang } from "@calcom/lib/i18n";
import { trpc } from "@calcom/trpc/react";
export function useViewerI18n() {
@@ -22,16 +22,18 @@ export function useViewerI18n() {
*/
const I18nLanguageHandler = (): null => {
const { i18n } = useTranslation("common");
const locale = useViewerI18n().data?.locale || "en";
const locale = useViewerI18n().data?.locale || i18n.language;
useEffect(() => {
if (locale && i18n.language && i18n.language !== locale) {
if (typeof i18n.changeLanguage === "function") i18n.changeLanguage(locale);
// bail early when i18n = {}
if (isEmpty(i18n)) return;
// if locale is ready and the i18n.language does != locale - changeLanguage
if (locale && i18n.language !== locale) {
i18n.changeLanguage(locale);
}
const dir = getDirFromLang(locale);
// set dir="rtl|ltr"
document.dir = i18n.dir();
document.documentElement.setAttribute("lang", locale);
document.documentElement.setAttribute("dir", dir);
}, [locale, i18n]);
return null;
-167
View File
@@ -1,167 +0,0 @@
import type { FormEvent } from "react";
import { useCallback, useEffect, useState } from "react";
import Cropper from "react-easy-crop";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button, Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui";
import type { Area } from "@lib/cropImage";
import { getCroppedImg } from "@lib/cropImage";
import { useFileReader } from "@lib/hooks/useFileReader";
import Slider from "@components/Slider";
type ImageUploaderProps = {
id: string;
buttonMsg: string;
handleAvatarChange: (imageSrc: string) => void;
imageSrc?: string;
target: string;
};
interface FileEvent<T = Element> extends FormEvent<T> {
target: EventTarget & T;
}
// This is separate to prevent loading the component until file upload
function CropContainer({
onCropComplete,
imageSrc,
}: {
imageSrc: string;
onCropComplete: (croppedAreaPixels: Area) => void;
}) {
const { t } = useLocale();
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const handleZoomSliderChange = (value: number) => {
value < 1 ? setZoom(1) : setZoom(value);
};
return (
<div className="crop-container h-40 max-h-40 w-40 rounded-full">
<div className="relative h-40 w-40 rounded-full">
<Cropper
image={imageSrc}
crop={crop}
zoom={zoom}
aspect={1}
onCropChange={setCrop}
onCropComplete={(croppedArea, croppedAreaPixels) => onCropComplete(croppedAreaPixels)}
onZoomChange={setZoom}
/>
</div>
<Slider
value={zoom}
min={1}
max={3}
step={0.1}
label={t("slide_zoom_drag_instructions")}
changeHandler={handleZoomSliderChange}
/>
</div>
);
}
/** @deprecated Use `packages/ui/v2/core/ImageUploader.tsx` */
export default function ImageUploader({
target,
id,
buttonMsg,
handleAvatarChange,
...props
}: ImageUploaderProps) {
const { t } = useLocale();
const [imageSrc, setImageSrc] = useState<string | null>(null);
const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);
const [{ result }, setFile] = useFileReader({
method: "readAsDataURL",
});
useEffect(() => {
if (props.imageSrc) setImageSrc(props.imageSrc);
}, [props.imageSrc]);
const onInputFile = (e: FileEvent<HTMLInputElement>) => {
if (!e.target.files?.length) {
return;
}
setFile(e.target.files[0]);
};
const showCroppedImage = useCallback(
async (croppedAreaPixels: Area | null) => {
try {
if (!croppedAreaPixels) return;
const croppedImage = await getCroppedImg(
result as string /* result is always string when using readAsDataUrl */,
croppedAreaPixels
);
setImageSrc(croppedImage);
handleAvatarChange(croppedImage);
} catch (e) {
console.error(e);
}
},
[result, handleAvatarChange]
);
return (
<Dialog
onOpenChange={
(opened) => !opened && setFile(null) // unset file on close
}>
<DialogTrigger asChild>
<div className="flex items-center">
<Button color="secondary" type="button" className="py-1 text-xs">
{buttonMsg}
</Button>
</div>
</DialogTrigger>
<DialogContent>
<div className="mb-4 sm:flex sm:items-start">
<div className="mt-3 text-center sm:mt-0 sm:text-left">
<h3 className="font-cal text-emphasis text-lg leading-6" id="modal-title">
{t("upload_target", { target })}
</h3>
</div>
</div>
<div className="mb-4">
<div className="cropper mt-6 flex flex-col items-center justify-center p-8">
{!result && (
<div className="bg-muted flex h-20 max-h-20 w-20 items-center justify-start rounded-full">
{!imageSrc && (
<p className="text-inverted w-full text-center text-sm sm:text-xs">
{t("no_target", { target })}
</p>
)}
{imageSrc && (
// eslint-disable-next-line @next/next/no-img-element
<img className="h-20 w-20 rounded-full" src={imageSrc} alt={target} />
)}
</div>
)}
{result && <CropContainer imageSrc={result as string} onCropComplete={setCroppedAreaPixels} />}
<label className="border-default bg-default hover:bg-mutedover:text-emphasis dark:hover:bg-inverted text-default dark:text-inverted focus:ring-empthasis mt-8 rounded-sm border px-3 py-1 text-xs font-medium leading-4 focus:outline-none focus:ring-2 focus:ring-offset-1 dark:border-gray-800 dark:bg-transparent">
<input
onInput={onInputFile}
type="file"
name={id}
placeholder={t("upload_image")}
className="pointer-events-none absolute mt-4 opacity-0"
accept="image/*"
/>
{t("choose_a_file")}
</label>
</div>
</div>
<div className="mt-5 gap-x-2 sm:mt-4 sm:flex sm:flex-row-reverse">
<DialogClose onClick={() => showCroppedImage(croppedAreaPixels)}>{t("save")}</DialogClose>
<DialogClose color="secondary">{t("cancel")}</DialogClose>
</div>
</DialogContent>
</Dialog>
);
}
+2 -2
View File
@@ -1,4 +1,3 @@
import { AdminRequired } from "components/ui/AdminRequired";
import { noop } from "lodash";
import type { LinkProps } from "next/link";
import Link from "next/link";
@@ -6,6 +5,7 @@ import { useRouter } from "next/router";
import type { FC, MouseEventHandler } from "react";
import { Fragment } from "react";
import { PermissionContainer } from "@calcom/features/auth/PermissionContainer";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import classNames from "@lib/classNames";
@@ -60,7 +60,7 @@ const NavTabs: FC<NavTabProps> = ({ tabs, linkProps, ...props }) => {
}
: noop;
const Component = tab.adminRequired ? AdminRequired : Fragment;
const Component = tab.adminRequired ? PermissionContainer : Fragment;
const className = tab.className || "";
return (
<Component key={tab.name}>
+82
View File
@@ -0,0 +1,82 @@
import { DefaultSeo } from "next-seo";
import { Inter } from "next/font/google";
import localFont from "next/font/local";
import Script from "next/script";
import "@calcom/embed-core/src/embed-iframe";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import type { AppProps } from "@lib/app-providers";
import AppProviders from "@lib/app-providers";
import { seoConfig } from "@lib/config/next-seo.config";
import I18nLanguageHandler from "@components/I18nLanguageHandler";
export interface CalPageWrapper {
(props?: any): JSX.Element;
PageWrapper?: AppProps["Component"]["PageWrapper"];
}
const interFont = Inter({ subsets: ["latin"], variable: "--font-inter", preload: true, display: "swap" });
const calFont = localFont({
src: "../fonts/CalSans-SemiBold.woff2",
variable: "--font-cal",
preload: true,
display: "swap",
});
function PageWrapper(props: AppProps) {
const { Component, pageProps, err, router } = props;
let pageStatus = "200";
if (router.pathname === "/404") {
pageStatus = "404";
} else if (router.pathname === "/500") {
pageStatus = "500";
}
// On client side don't let nonce creep into DOM
// It also avoids hydration warning that says that Client has the nonce value but server has "" because browser removes nonce attributes before DOM is built
// See https://github.com/kentcdodds/nonce-hydration-issues
// Set "" only if server had it set otherwise keep it undefined because server has to match with client to avoid hydration error
const nonce = typeof window !== "undefined" ? (pageProps.nonce ? "" : undefined) : pageProps.nonce;
const providerProps = {
...props,
pageProps: {
...props.pageProps,
nonce,
},
};
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => page);
return (
<AppProviders {...providerProps}>
<DefaultSeo {...seoConfig.defaultNextSeo} />
<I18nLanguageHandler />
<Script
nonce={nonce}
id="page-status"
dangerouslySetInnerHTML={{ __html: `window.CalComPageStatus = '${pageStatus}'` }}
/>
<style jsx global>{`
:root {
--font-inter: ${interFont.style.fontFamily};
--font-cal: ${calFont.style.fontFamily};
}
`}</style>
{getLayout(
Component.requiresLicense ? (
<LicenseRequired>
<Component {...pageProps} err={err} />
</LicenseRequired>
) : (
<Component {...pageProps} err={err} />
),
router
)}
</AppProviders>
);
}
export default PageWrapper;
+1 -1
View File
@@ -46,7 +46,7 @@ export default function SettingsShell({
...rest
}: { children: React.ReactNode } & ComponentProps<typeof Shell>) {
return (
<Shell {...rest}>
<Shell {...rest} hideHeadingOnMobile>
<div className="sm:mx-auto">
<NavTabs tabs={tabs} />
</div>
-27
View File
@@ -1,27 +0,0 @@
import * as SliderPrimitive from "@radix-ui/react-slider";
import React from "react";
const Slider = ({
value,
label,
changeHandler,
...props
}: Omit<SliderPrimitive.SliderProps, "value"> & {
value: number;
label: string;
changeHandler: (value: number) => void;
}) => (
<SliderPrimitive.Root
className="slider mt-2"
value={[value]}
aria-label={label}
onValueChange={(value: number[]) => changeHandler(value[0] ?? value)}
{...props}>
<SliderPrimitive.Track className="slider-track">
<SliderPrimitive.Range className="slider-range" />
</SliderPrimitive.Track>
<SliderPrimitive.Thumb className="slider-thumb" />
</SliderPrimitive.Root>
);
export default Slider;
+5 -7
View File
@@ -1,12 +1,11 @@
import Link from "next/link";
import { useRouter } from "next/router";
import type { IframeHTMLAttributes } from "react";
import React, { useState } from "react";
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
import { InstallAppButton, AppDependencyComponent } from "@calcom/app-store/components";
import DisconnectIntegration from "@calcom/features/apps/components/DisconnectIntegration";
import LicenseRequired from "@calcom/features/ee/common/components/v2/LicenseRequired";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import Shell from "@calcom/features/shell/Shell";
import classNames from "@calcom/lib/classNames";
import { APP_NAME, COMPANY_NAME, SUPPORT_MAIL_ADDRESS } from "@calcom/lib/constants";
@@ -41,9 +40,8 @@ const Component = ({
isTemplate,
dependencies,
}: Parameters<typeof App>[0]) => {
const { t } = useLocale();
const { t, i18n } = useLocale();
const hasDescriptionItems = descriptionItems && descriptionItems.length > 0;
const router = useRouter();
const mutation = useAddAppMutation(null, {
onSuccess: (data) => {
@@ -186,7 +184,7 @@ const Component = ({
label={t("disconnect")}
credentialId={existingCredentials[0]}
onSuccess={() => {
router.replace("/apps/installed");
appCredentials.refetch();
}}
/>
) : (
@@ -247,7 +245,7 @@ const Component = ({
t("free_to_use_apps")
) : (
<>
{Intl.NumberFormat("en-US", {
{Intl.NumberFormat(i18n.language, {
style: "currency",
currency: "USD",
useGrouping: false,
@@ -366,7 +364,7 @@ export default function App(props: {
dependencies?: string[];
}) {
return (
<Shell smallHeading isPublic heading={<ShellHeading />} backPath="/apps" withoutSeo>
<Shell smallHeading isPublic hideHeadingOnMobile heading={<ShellHeading />} backPath="/apps" withoutSeo>
<HeadSeo
title={props.name}
description={props.description}
@@ -1,9 +1,9 @@
import { useMutation } from "@tanstack/react-query";
import Link from "next/link";
import { Fragment } from "react";
import { InstallAppButton } from "@calcom/app-store/components";
import DisconnectIntegration from "@calcom/features/apps/components/DisconnectIntegration";
import { CalendarSwitch } from "@calcom/features/calendars/CalendarSwitch";
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
@@ -12,12 +12,10 @@ import {
Button,
EmptyScreen,
List,
showToast,
AppSkeletonLoader as SkeletonLoader,
Switch,
ShellSubHeading,
} from "@calcom/ui";
import { ArrowLeft, Calendar, Plus } from "@calcom/ui/components/icon";
import { Calendar, Plus } from "@calcom/ui/components/icon";
import { QueryCell } from "@lib/QueryCell";
@@ -31,83 +29,6 @@ type Props = {
destinationCalendarId?: string;
};
function CalendarSwitch(props: {
type: string;
externalId: string;
title: string;
defaultSelected: boolean;
destination?: boolean;
}) {
const { t } = useLocale();
const utils = trpc.useContext();
const mutation = useMutation<
unknown,
unknown,
{
isOn: boolean;
}
>(
async ({ isOn }) => {
const body = {
integration: props.type,
externalId: props.externalId,
};
if (isOn) {
const res = await fetch("/api/availability/calendar", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) {
throw new Error("Something went wrong");
}
} else {
const res = await fetch("/api/availability/calendar", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) {
throw new Error("Something went wrong");
}
}
},
{
async onSettled() {
await utils.viewer.integrations.invalidate();
},
onError() {
showToast(`Something went wrong when toggling "${props.title}""`, "error");
},
}
);
return (
<div className="flex flex-col py-1 sm:flex-row">
<Switch
key={props.externalId}
name="enabled"
label={props.title}
defaultChecked={props.defaultSelected}
onCheckedChange={(isOn: boolean) => {
mutation.mutate({ isOn });
}}
/>
{!!props.destination && (
<span className="bg-subtle text-default ml-8 inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm font-normal sm:ml-4">
<ArrowLeft className="h-4 w-4" />
{t("adding_events_to")}
</span>
)}
</div>
);
}
function CalendarList(props: Props) {
const { t } = useLocale();
const query = trpc.viewer.integrations.useQuery({ variant: "calendar", onlyInstalled: false });
@@ -185,14 +106,15 @@ function ConnectedCalendarsList(props: Props) {
{!fromOnboarding && (
<>
<p className="text-subtle px-5 pt-4 text-sm">{t("toggle_calendars_conflict")}</p>
<ul className="space-y-2 px-5 py-4">
<ul className="space-y-4 px-5 py-4">
{item.calendars.map((cal) => (
<CalendarSwitch
key={cal.externalId}
externalId={cal.externalId}
title={cal.name || "Nameless calendar"}
name={cal.name || "Nameless calendar"}
type={item.integration.type}
defaultSelected={cal.isSelected}
isChecked={cal.isSelected}
destination={cal.externalId === props.destinationCalendarId}
/>
))}
@@ -22,7 +22,7 @@ export default function AppsLayout({ children, actions, emptyStore, ...rest }: A
if (session.status === "loading") return <></>;
return (
<Shell {...rest} actions={actions?.("block")}>
<Shell {...rest} actions={actions?.("block")} hideHeadingOnMobile>
<div className="flex flex-col xl:flex-row">
<main className="w-full">
{emptyStore ? (
@@ -57,7 +57,7 @@ export default function InstalledAppsLayout({
}
return (
<Shell {...rest}>
<Shell {...rest} hideHeadingOnMobile>
<AppCategoryNavigation baseURL="/apps/installed" containerClassname="min-w-0 w-full">
{children}
</AppCategoryNavigation>
@@ -1,4 +1,3 @@
import { UserPermissionRole } from "@prisma/client";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import type { ComponentProps } from "react";
@@ -6,6 +5,7 @@ import React, { useEffect } from "react";
import SettingsLayout from "@calcom/features/settings/layouts/SettingsLayout";
import type Shell from "@calcom/features/shell/Shell";
import { UserPermissionRole } from "@calcom/prisma/enums";
import { ErrorBoundary } from "@calcom/ui";
export default function AdminLayout({
@@ -41,13 +41,13 @@ export function AvailableEventLocations({ locations }: { locations: Props["event
return (
<div key={`${location.type}-${index}`} className="flex flex-row items-center text-sm font-medium">
{eventLocationType.iconUrl === "/link.svg" ? (
<Link className="text-default min-h-4 min-w-4 ml-[2px] opacity-70 ltr:mr-[10px] rtl:ml-[10px] " />
<Link className="text-default ml-[2px] h-4 w-4 ltr:mr-[10px] rtl:ml-[10px] " />
) : (
<img
src={eventLocationType.iconUrl}
className={classNames(
"ml-[2px] h-4 w-4 opacity-70 ltr:mr-[10px] rtl:ml-[10px] dark:opacity-100 ",
!eventLocationType.iconUrl?.includes("api") ? "dark:invert-[.65]" : ""
!eventLocationType.iconUrl?.startsWith("/app-store") ? "dark:invert-[.65]" : ""
)}
alt={`${eventLocationType.label} icon`}
/>
@@ -11,7 +11,7 @@ import useMediaQuery from "@calcom/lib/hooks/useMediaQuery";
import { TimeFormat } from "@calcom/lib/timeFormat";
import { nameOfDay } from "@calcom/lib/weekday";
import { trpc } from "@calcom/trpc/react";
import type { Slot } from "@calcom/trpc/server/routers/viewer/slots";
import type { Slot } from "@calcom/trpc/server/routers/viewer/slots/types";
import { SkeletonContainer, SkeletonText, ToggleGroup } from "@calcom/ui";
import classNames from "@lib/classNames";
@@ -72,6 +72,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
slotUtcStartDate: slot.time,
eventTypeId,
slotUtcEndDate: dayjs(slot.time).utc().add(duration, "minutes").format(),
bookingAttendees: bookingAttendees || undefined,
});
};
@@ -1,10 +1,10 @@
import { SchedulingType } from "@prisma/client";
import type { FC, ReactNode } from "react";
import { useEffect } from "react";
import dayjs from "@calcom/dayjs";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { SchedulingType } from "@calcom/prisma/enums";
import { Badge } from "@calcom/ui";
import { CheckSquare, Clock } from "@calcom/ui/components/icon";
@@ -1,5 +1,5 @@
import { i18n } from "next-i18next";
import type { TFunction } from "next-i18next";
import { FormattedNumber, IntlProvider } from "react-intl";
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
import { CreditCard } from "@calcom/ui/components/icon";
@@ -7,28 +7,29 @@ import { CreditCard } from "@calcom/ui/components/icon";
const BookingDescriptionPayment = (props: {
eventType: Parameters<typeof getPaymentAppData>[0];
t: TFunction;
i18n: typeof i18n;
}) => {
const paymentAppData = getPaymentAppData(props.eventType);
if (!paymentAppData || paymentAppData.price <= 0) return null;
const params = {
amount: paymentAppData.price / 100.0,
formatParams: { amount: { currency: paymentAppData.currency } },
};
return (
<p className="text-bookinglight -ml-2 px-2 text-sm ">
<CreditCard className="ml-[2px] -mt-1 inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
{paymentAppData.paymentOption === "HOLD" ? (
<>
{props.t("no_show_fee_amount", {
amount: paymentAppData.price / 100.0,
formatParams: { amount: { currency: paymentAppData.currency } },
})}
</>
<>{props.t("no_show_fee_amount", params)}</>
) : (
<IntlProvider locale="en">
<FormattedNumber
value={paymentAppData.price / 100.0}
style="currency"
currency={paymentAppData.currency?.toUpperCase()}
/>
</IntlProvider>
<>
{/* If undefined this will default to the browser locale */}
{new Intl.NumberFormat(i18n?.language, {
style: "currency",
currency: paymentAppData.currency,
}).format(paymentAppData.price / 100)}
</>
)}
</p>
);
+18 -11
View File
@@ -1,4 +1,3 @@
import { BookingStatus } from "@prisma/client";
import { useRouter } from "next/router";
import { useState } from "react";
@@ -10,8 +9,10 @@ import "@calcom/dayjs/locales";
import ViewRecordingsDialog from "@calcom/features/ee/video/ViewRecordingsDialog";
import classNames from "@calcom/lib/classNames";
import { formatTime } from "@calcom/lib/date-fns";
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
import { BookingStatus } from "@calcom/prisma/enums";
import type { RouterInputs, RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import type { ActionType } from "@calcom/ui";
@@ -86,6 +87,8 @@ function BookingListItem(booking: BookingItemProps) {
const isTabRecurring = booking.listingStatus === "recurring";
const isTabUnconfirmed = booking.listingStatus === "unconfirmed";
const paymentAppData = getPaymentAppData(booking.eventType);
const bookingConfirm = async (confirm: boolean) => {
let body = {
bookingId: booking.id,
@@ -277,13 +280,13 @@ function BookingListItem(booking: BookingItemProps) {
isOpenDialog={isOpenSetLocationDialog}
setShowLocationModal={setIsOpenLocationDialog}
/>
{booking.paid && (
{booking.paid && booking.payment[0] && (
<ChargeCardDialog
isOpenDialog={chargeCardDialogIsOpen}
setIsOpenDialog={setChargeCardDialogIsOpen}
bookingId={booking.id}
paymentAmount={booking?.payment[0].amount}
paymentCurrency={booking?.payment[0].currency}
paymentAmount={booking.payment[0].amount}
paymentCurrency={booking.payment[0].currency}
/>
)}
{showRecordingsButtons && (
@@ -351,11 +354,15 @@ function BookingListItem(booking: BookingItemProps) {
{booking.eventType.team.name}
</Badge>
)}
{booking.paid && (
{booking.paid && !booking.payment[0] ? (
<Badge className="ltr:mr-2 rtl:ml-2" variant="orange">
{t("error_collecting_card")}
</Badge>
) : booking.paid ? (
<Badge className="ltr:mr-2 rtl:ml-2" variant="green">
{booking.payment[0].paymentOption === "HOLD" ? t("card_held") : t("paid")}
</Badge>
)}
) : null}
{recurringDates !== undefined && (
<div className="text-muted mt-2 text-sm">
<RecurringBookingsTooltip booking={booking} recurringDates={recurringDates} />
@@ -413,7 +420,7 @@ function BookingListItem(booking: BookingItemProps) {
{title}
<span> </span>
{!!booking?.eventType?.price && !booking.paid && (
{paymentAppData.enabled && !booking.paid && booking.payment.length && (
<Badge className="ms-2 me-2 hidden sm:inline-flex" variant="orange">
{t("pending_payment")}
</Badge>
@@ -455,7 +462,7 @@ function BookingListItem(booking: BookingItemProps) {
<RequestSentMessage />
</div>
)}
{booking.status === "ACCEPTED" && booking.paid && booking?.payment[0]?.paymentOption === "HOLD" && (
{booking.status === "ACCEPTED" && booking.paid && booking.payment[0]?.paymentOption === "HOLD" && (
<div className="ml-2">
<TableActions actions={chargeCardActions} />
</div>
@@ -480,12 +487,12 @@ const RecurringBookingsTooltip = ({ booking, recurringDates }: RecurringBookings
i18n: { language },
} = useLocale();
const now = new Date();
const recurringCount = recurringDates.filter((date) => {
const recurringCount = recurringDates.filter((recurringDate) => {
return (
date >= now &&
recurringDate >= now &&
!booking.recurringInfo?.bookings[BookingStatus.CANCELLED]
.map((date) => date.toDateString())
.includes(date.toDateString())
.includes(recurringDate.toDateString())
);
}).length;
+1 -1
View File
@@ -29,7 +29,7 @@ const TimeOptions: FC<Props> = ({ onSelectTimeZone }) => {
classNames={{
singleValue: () => "text-default",
dropdownIndicator: () => "text-default",
menu: () => "!w-64 max-w-[90vw]",
menu: () => "!w-64 max-w-[90vw] shadow-dropdown bg-default border-subtle border rounded-md mt-1",
}}
variant="minimal"
value={selectedTimeZone}
@@ -17,7 +17,7 @@ export function TimezoneDropdown({
return (
<>
<div className="dark:focus-within:bg-darkgray-200 dark:bg-darkgray-100 dark:hover:bg-darkgray-200 !mt-3 flex w-full max-w-[20rem] items-center rounded-[4px] px-1 text-sm font-medium focus-within:bg-gray-200 hover:bg-gray-100 lg:max-w-[12rem] [&_p]:focus-within:text-gray-900 dark:[&_p]:focus-within:text-white [&_svg]:focus-within:text-gray-900 dark:[&_svg]:focus-within:text-white">
<div className="dark:focus-within:bg-darkgray-200 dark:hover:bg-darkgray-200 !mt-3 flex w-full max-w-[20rem] items-center rounded-[4px] px-1 text-sm font-medium focus-within:bg-gray-200 hover:bg-gray-100 lg:max-w-[12rem] [&_p]:focus-within:text-gray-900 dark:[&_p]:focus-within:text-white [&_svg]:focus-within:text-gray-900 dark:[&_svg]:focus-within:text-white">
<Globe className="dark:text-darkgray-600 flex h-4 w-4 text-gray-600 ltr:mr-2 rtl:ml-2" />
<TimeOptions onSelectTimeZone={handleSelectTimeZone} />
</div>
@@ -1,7 +1,6 @@
import dynamic from "next/dynamic";
import { useRouter } from "next/router";
import { useEffect, useMemo, useReducer, useState } from "react";
import { FormattedNumber, IntlProvider } from "react-intl";
import { z } from "zod";
import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager";
@@ -37,7 +36,7 @@ import type { AvailabilityPageProps } from "../../../pages/[user]/[type]";
import type { DynamicAvailabilityPageProps } from "../../../pages/d/[link]/[slug]";
import type { AvailabilityTeamPageProps } from "../../../pages/team/[slug]/[type]";
const PoweredByCal = dynamic(() => import("@components/ui/PoweredByCal"));
const PoweredBy = dynamic(() => import("@calcom/ee/components/PoweredBy"));
const Toaster = dynamic(() => import("react-hot-toast").then((mod) => mod.Toaster), { ssr: false });
/*const SlotPicker = dynamic(() => import("../SlotPicker").then((mod) => mod.SlotPicker), {
@@ -75,7 +74,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
brandColor: profile.brandColor,
darkBrandColor: profile.darkBrandColor,
});
const { t } = useLocale();
const { t, i18n } = useLocale();
const availabilityDatePickerEmbedStyles = useEmbedStyles("availabilityDatePicker");
//TODO: Plan to remove shouldAlignCentrallyInEmbed config
const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left";
@@ -124,16 +123,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
[timeZone]
);
const paymentAppData = getPaymentAppData(eventType);
const paymentAmount = () => {
return;
<IntlProvider locale="en">
<FormattedNumber
value={paymentAppData.price / 100.0}
style="currency"
currency={paymentAppData.currency?.toUpperCase()}
/>
</IntlProvider>;
};
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.
@@ -257,13 +247,12 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
})}
</>
) : (
<IntlProvider locale="en">
<FormattedNumber
value={paymentAppData.price / 100.0}
style="currency"
currency={paymentAppData.currency?.toUpperCase()}
/>
</IntlProvider>
<>
{new Intl.NumberFormat(i18n.language, {
style: "currency",
currency: paymentAppData.currency,
}).format(paymentAppData.price / 100)}
</>
)}
</p>
)}
@@ -312,7 +301,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
</div>
</div>
{/* FIXME: We don't show branding in Embed yet because we need to place branding on top of the main content. Keeping it outside the main content would have visibility issues because outside main content background is transparent */}
{!restProps.isBrandingHidden && !isEmbed && <PoweredByCal />}
{!restProps.isBrandingHidden && !isEmbed && <PoweredBy />}
</div>
</main>
</div>
@@ -21,6 +21,7 @@ import {
useIsBackgroundTransparent,
useIsEmbed,
} from "@calcom/embed-core/embed-iframe";
import { createBooking, createRecurringBooking } from "@calcom/features/bookings/lib";
import {
getBookingFieldsWithSystemFields,
SystemField,
@@ -38,6 +39,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import useTheme from "@calcom/lib/hooks/useTheme";
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery";
import { HttpError } from "@calcom/lib/http-error";
import { parseDate, parseRecurringDates } from "@calcom/lib/parse-dates";
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { TimeFormat } from "@calcom/lib/timeFormat";
@@ -47,9 +49,6 @@ import { AlertTriangle, Calendar, RefreshCw, User } from "@calcom/ui/components/
import { timeZone } from "@lib/clock";
import useRouterQuery from "@lib/hooks/useRouterQuery";
import createBooking from "@lib/mutations/bookings/create-booking";
import createRecurringBooking from "@lib/mutations/bookings/create-recurring-booking";
import { parseRecurringDates, parseDate } from "@lib/parseDate";
import type { Gate, GateState } from "@components/Gates";
import Gates from "@components/Gates";
@@ -231,12 +230,14 @@ const BookingPage = ({
}),
{}
);
const reserveSlot = () => {
if (queryDuration) {
reserveSlotMutation.mutate({
eventTypeId: eventType.id,
slotUtcStartDate: dayjs(queryDate).utc().format(),
slotUtcEndDate: dayjs(queryDate).utc().add(parseInt(queryDuration), "minutes").format(),
bookingAttendees: currentSlotBooking ? currentSlotBooking.attendees.length : undefined,
});
}
};
@@ -433,7 +434,6 @@ const BookingPage = ({
// Calculate the booking date(s)
let recurringStrings: string[] = [],
recurringDates: Date[] = [];
if (eventType.recurringEvent?.freq && recurringEventCount !== null) {
[recurringStrings, recurringDates] = parseRecurringDates(
{
@@ -443,7 +443,7 @@ const BookingPage = ({
recurringCount: parseInt(recurringEventCount.toString()),
selectedTimeFormat: timeFormat,
},
i18n
i18n.language
);
}
@@ -552,11 +552,11 @@ const BookingPage = ({
)}>
<div className="sm:flex">
{showEventTypeDetails && (
<div className="sm:border-subtle text-default flex flex-col px-6 pt-6 pb-0 sm:w-1/2 sm:border-r sm:pb-6">
<div className="sm:border-subtle text-default flex flex-col px-6 pt-6 pb-0 sm:w-1/2 sm:border-r sm:pb-6">
<BookingDescription isBookingPage profile={profile} eventType={eventType}>
<BookingDescriptionPayment eventType={eventType} t={t} />
<BookingDescriptionPayment eventType={eventType} t={t} i18n={i18n} />
{!rescheduleUid && eventType.recurringEvent?.freq && recurringEventCount && (
<div className="dark:text-inverted text-default items-start text-sm font-medium">
<div className="text-default items-start text-sm font-medium">
<RefreshCw className="ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
<p className="-ml-2 inline-block items-center px-2">
{getEveryFreqFor({
@@ -572,7 +572,7 @@ const BookingPage = ({
<div className="text-sm font-medium">
{isClientTimezoneAvailable &&
(rescheduleUid || !eventType.recurringEvent?.freq) &&
`${parseDate(date, i18n, timeFormat)}`}
`${parseDate(date, i18n.language, { selectedTimeFormat: timeFormat })}`}
{isClientTimezoneAvailable &&
!rescheduleUid &&
eventType.recurringEvent?.freq &&
@@ -602,7 +602,9 @@ const BookingPage = ({
<Calendar className="ml-[2px] -mt-1 inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
{isClientTimezoneAvailable &&
typeof booking.startTime === "string" &&
parseDate(dayjs(booking.startTime), i18n, timeFormat)}
parseDate(dayjs(booking.startTime), i18n.language, {
selectedTimeFormat: timeFormat,
})}
</p>
</div>
)}
@@ -1,7 +1,5 @@
import { Trans } from "next-i18next";
import { useState } from "react";
import type { Dispatch, SetStateAction } from "react";
import { IntlProvider, FormattedNumber } from "react-intl";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
@@ -40,6 +38,11 @@ export const ChargeCardDialog = (props: IRescheduleDialog) => {
},
});
const currencyStringParams = {
amount: props.paymentAmount / 100.0,
formatParams: { amount: { currency: props.paymentCurrency } },
};
return (
<Dialog open={isOpenDialog} onOpenChange={setIsOpenDialog}>
<DialogContent>
@@ -49,19 +52,7 @@ export const ChargeCardDialog = (props: IRescheduleDialog) => {
</div>
<div className="pt-1">
<DialogHeader title={t("charge_card")} />
<Trans i18nKey="charge_card_dialog_body">
<p className="text-sm text-gray-500">
You are about to charge the attendee{" "}
<IntlProvider locale="en">
<FormattedNumber
value={props.paymentAmount / 100.0}
style="currency"
currency={props.paymentCurrency?.toUpperCase()}
/>
</IntlProvider>
. Are you sure you want to continue?
</p>
</Trans>
<p>{t("charge_card_dialog_body", currencyStringParams)}</p>
{chargeError && (
<div className="mt-4 flex text-red-500">
@@ -80,16 +71,7 @@ export const ChargeCardDialog = (props: IRescheduleDialog) => {
bookingId,
})
}>
<Trans i18nKey="charge_card_confirm">
Charge attendee{" "}
<IntlProvider locale="en">
<FormattedNumber
value={props.paymentAmount / 100.0}
style="currency"
currency={props.paymentCurrency?.toUpperCase()}
/>
</IntlProvider>
</Trans>
{t("charge_attendee", currencyStringParams)}
</Button>
</DialogFooter>
</div>
@@ -174,7 +174,6 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
id="locationInput"
placeholder={t(eventLocationType.organizerInputPlaceholder || "")}
required
className="border-default block w-full rounded-sm text-sm"
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
defaultValue={
defaultLocation ? defaultLocation[eventLocationType.defaultValueVariable] : undefined
+1 -1
View File
@@ -1,6 +1,6 @@
import React from "react";
import { HttpError } from "@lib/core/http/error";
import { HttpError } from "@calcom/lib/http-error";
type Props = {
statusCode?: number | null;
@@ -11,6 +11,10 @@ import { getEventName } from "@calcom/core/event";
import getLocationsOptionsForSelect from "@calcom/features/bookings/lib/getLocationOptionsForSelect";
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
import {
allowDisablingAttendeeConfirmationEmails,
allowDisablingHostConfirmationEmails,
} from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails";
import { FormBuilder } from "@calcom/features/form-builder/FormBuilder";
import { classNames } from "@calcom/lib";
import { APP_NAME, CAL_URL } from "@calcom/lib/constants";
@@ -43,6 +47,8 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
const bookingFields: Prisma.JsonObject = {};
const workflows = eventType.workflows.map((workflowOnEventType) => workflowOnEventType.workflow);
eventType.bookingFields.forEach(({ name }) => {
bookingFields[name] = name + " input";
});
@@ -338,7 +344,51 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
</>
)}
/>
{allowDisablingAttendeeConfirmationEmails(workflows) && (
<>
<hr className="border-subtle" />
<Controller
name="metadata.disableStandardEmails.confirmation.attendee"
control={formMethods.control}
render={({ field: { value, onChange } }) => (
<>
<SettingsToggle
title={t("disable_attendees_confirmation_emails")}
description={t("disable_attendees_confirmation_emails_description")}
checked={value || false}
onCheckedChange={(e) => {
formMethods.setValue("metadata.disableStandardEmails.confirmation.attendee", e);
onChange(e);
}}
/>
</>
)}
/>
</>
)}
{allowDisablingHostConfirmationEmails(workflows) && (
<>
<hr className="border-subtle" />
<Controller
name="metadata.disableStandardEmails.confirmation.host"
control={formMethods.control}
defaultValue={!!eventType.seatsPerTimeSlot}
render={({ field: { value, onChange } }) => (
<>
<SettingsToggle
title={t("disable_host_confirmation_emails")}
description={t("disable_host_confirmation_emails_description")}
checked={value || false}
onCheckedChange={(e) => {
formMethods.setValue("metadata.disableStandardEmails.confirmation.host", e);
onChange(e);
}}
/>
</>
)}
/>
</>
)}
{showEventNameTip && (
<CustomEventTypeModal
close={closeEventNameTip}
@@ -106,7 +106,7 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
{!shouldLockDisableProps("apps").disabled && (
<div>
{!isLoading && notInstalledApps?.length ? (
<h2 className="text-emphasis mt-0 mb-2 text-lg font-semibold">{t("available_apps")}</h2>
<h2 className="text-emphasis my-2 text-lg font-semibold">{t("available_apps")}</h2>
) : null}
<div className="before:border-0">
{notInstalledApps?.map((app) => (
@@ -1,21 +1,23 @@
import { SchedulingType } from "@prisma/client";
import type { FormValues, EventTypeSetup } from "pages/event-types/[type]";
import type { EventTypeSetup, FormValues } from "pages/event-types/[type]";
import { useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import type { OptionProps, SingleValueProps } from "react-select";
import { components } from "react-select";
import dayjs from "@calcom/dayjs";
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
import { NewScheduleButton } from "@calcom/features/schedules";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { weekdayNames } from "@calcom/lib/weekday";
import { SchedulingType } from "@calcom/prisma/enums";
import { trpc } from "@calcom/trpc/react";
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
import { Badge, Button, Select, SettingsToggle, SkeletonText, EmptyScreen } from "@calcom/ui";
import { ExternalLink, Globe, Clock } from "@calcom/ui/components/icon";
import { Badge, Button, Select, SettingsToggle, SkeletonText } from "@calcom/ui";
import { ExternalLink, Globe } from "@calcom/ui/components/icon";
type AvailabilityOption = {
import { SelectSkeletonLoader } from "@components/availability/SkeletonLoader";
export type AvailabilityOption = {
label: string;
value: number;
isDefault: boolean;
@@ -62,40 +64,12 @@ const SingleValue = ({ ...props }: SingleValueProps<AvailabilityOption>) => {
);
};
const AvailabilitySelect = ({
className = "",
options,
value,
...props
}: {
className?: string;
name: string;
value: AvailabilityOption | undefined;
options: AvailabilityOption[];
isDisabled?: boolean;
onBlur: () => void;
onChange: (value: AvailabilityOption | null) => void;
}) => {
const { t } = useLocale();
return (
<Select
placeholder={t("select")}
options={options}
isDisabled={props.isDisabled}
isSearchable={false}
onChange={props.onChange}
className={classNames("block w-full min-w-0 flex-1 rounded-sm text-sm", className)}
defaultValue={value}
components={{ Option, SingleValue }}
isMulti={false}
/>
);
};
const format = (date: Date, hour12: boolean) =>
Intl.DateTimeFormat(undefined, { hour: "numeric", minute: "numeric", hour12 }).format(
new Date(dayjs.utc(date).format("YYYY-MM-DDTHH:mm:ss"))
);
Intl.DateTimeFormat(undefined, {
hour: "numeric",
minute: "numeric",
hourCycle: hour12 ? "h12" : "h24",
}).format(new Date(dayjs.utc(date).format("YYYY-MM-DDTHH:mm:ss")));
const EventTypeScheduleDetails = ({
isManagedEventType,
@@ -180,69 +154,66 @@ const EventTypeScheduleDetails = ({
const EventTypeSchedule = ({ eventType }: { eventType: EventTypeSetup }) => {
const { t } = useLocale();
const { shouldLockIndicator, shouldLockDisableProps, isManagedEventType, isChildrenManagedEventType } =
useLockedFieldsManager(
eventType,
t("locked_fields_admin_description"),
t("locked_fields_member_description")
);
const { shouldLockIndicator, isManagedEventType, isChildrenManagedEventType } = useLockedFieldsManager(
eventType,
t("locked_fields_admin_description"),
t("locked_fields_member_description")
);
const { watch } = useFormContext<FormValues>();
const watchSchedule = watch("schedule");
const formMethods = useFormContext<FormValues>();
const [options, setOptions] = useState<AvailabilityOption[]>([]);
const { data, isLoading } = trpc.viewer.availability.list.useQuery();
const { isLoading } = trpc.viewer.availability.list.useQuery(undefined, {
onSuccess: ({ schedules }) => {
const options = schedules.map((schedule) => ({
value: schedule.id,
label: schedule.name,
isDefault: schedule.isDefault,
isManaged: false,
}));
if (!data?.schedules.length && !isLoading)
return (
<EmptyScreen
Icon={Clock}
headline={t("new_schedule_heading")}
description={t("new_schedule_description")}
buttonRaw={<NewScheduleButton fromEventType />}
border={false}
/>
);
// We are showing a managed event for a team admin, so adding the option to let members choose their schedule
if (isManagedEventType) {
options.push({
value: 0,
label: t("members_default_schedule"),
isDefault: false,
isManaged: false,
});
}
const schedules = data?.schedules || [];
// We are showing a managed event for a member and team owner selected their own schedule, so adding
// the managed schedule option
if (
isChildrenManagedEventType &&
watchSchedule &&
!schedules.find((schedule) => schedule.id === watchSchedule)
) {
options.push({
value: watchSchedule,
label: eventType.scheduleName ?? t("default_schedule_name"),
isDefault: false,
isManaged: false,
});
}
const options = schedules.map((schedule) => ({
value: schedule.id,
label: schedule.name,
isDefault: schedule.isDefault,
isManaged: false,
}));
setOptions(options);
// We are showing a managed event for a team admin, so adding the option to let members choose their schedule
if (isManagedEventType) {
options.push({
value: 0,
label: t("members_default_schedule"),
isDefault: false,
isManaged: false,
});
}
const scheduleId = formMethods.getValues("schedule");
const value = options.find((option) =>
scheduleId
? option.value === scheduleId
: isManagedEventType
? option.value === 0
: option.value === schedules.find((schedule) => schedule.isDefault)?.id
);
// We are showing a managed event for a member and team owner selected their own schedule, so adding
// the managed schedule option
if (
isChildrenManagedEventType &&
watchSchedule &&
!schedules.find((schedule) => schedule.id === watchSchedule)
) {
options.push({
value: watchSchedule,
label: eventType.scheduleName ?? t("default_schedule_name"),
isDefault: false,
isManaged: false,
});
}
formMethods.setValue("availability", value);
},
});
const value = options.find((option) =>
watchSchedule
? option.value === watchSchedule
: isManagedEventType
? option.value === 0
: option.value === schedules.find((schedule) => schedule.isDefault)?.id
);
const availabilityValue = formMethods.watch("availability");
return (
<div className="space-y-4">
@@ -251,27 +222,33 @@ const EventTypeSchedule = ({ eventType }: { eventType: EventTypeSetup }) => {
{t("availability")}
{shouldLockIndicator("availability")}
</label>
<Controller
name="schedule"
render={({ field }) => (
<>
<AvailabilitySelect
value={value}
options={options}
onBlur={field.onBlur}
isDisabled={shouldLockDisableProps("schedule").disabled}
name={field.name}
onChange={(selected) => {
field.onChange(selected?.value || null);
}}
/>
</>
)}
/>
{isLoading && <SelectSkeletonLoader />}
{!isLoading && (
<Controller
name="schedule"
render={({ field }) => {
return (
<Select
placeholder={t("select")}
options={options}
isSearchable={false}
onChange={(selected) => {
field.onChange(selected?.value || null);
if (selected?.value) formMethods.setValue("availability", selected);
}}
className="block w-full min-w-0 flex-1 rounded-sm text-sm"
value={availabilityValue}
components={{ Option, SingleValue }}
isMulti={false}
/>
);
}}
/>
)}
</div>
{value?.value !== 0 ? (
{availabilityValue?.value !== 0 ? (
<EventTypeScheduleDetails
selectedScheduleValue={value}
selectedScheduleValue={availabilityValue}
isManagedEventType={isManagedEventType || isChildrenManagedEventType}
/>
) : (
@@ -13,7 +13,7 @@ import type { DurationType } from "@calcom/lib/convertToNewDurationType";
import convertToNewDurationType from "@calcom/lib/convertToNewDurationType";
import findDurationType from "@calcom/lib/findDurationType";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { PeriodType } from "@calcom/prisma/client";
import type { PeriodType } from "@calcom/prisma/enums";
import type { IntervalLimit } from "@calcom/types/Calendar";
import { Button, DateRangePicker, InputField, Label, Select, SettingsToggle, TextField } from "@calcom/ui";
import { Plus, Trash } from "@calcom/ui/components/icon";
@@ -12,6 +12,7 @@ import { z } from "zod";
import type { EventLocationType } from "@calcom/app-store/locations";
import { getEventLocationType, MeetLocationType, LocationType } from "@calcom/app-store/locations";
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
import cx from "@calcom/lib/classNames";
import { CAL_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { md } from "@calcom/lib/markdownIt";
@@ -80,6 +81,7 @@ const DescriptionEditor = (props: DescriptionEditorProps) => {
const [mounted, setIsMounted] = useState(false);
const { t } = useLocale();
const { description } = props;
const [firstRender, setFirstRender] = useState(true);
useEffect(() => {
setIsMounted(true);
}, []);
@@ -91,6 +93,8 @@ const DescriptionEditor = (props: DescriptionEditorProps) => {
excludedToolbarItems={["blockType"]}
placeholder={t("quick_video_meeting")}
editable={props.editable}
firstRender={firstRender}
setFirstRender={setFirstRender}
/>
) : (
<SkeletonContainer>
@@ -272,12 +276,18 @@ export const EventSetupTab = (
return (
<li
key={`${location.type}${index}`}
className="border-default text-default mb-2 rounded-md border py-1.5 px-2 hover:cursor-pointer">
className="border-default text-default mb-2 h-9 rounded-md border py-1.5 px-2 hover:cursor-pointer">
<div className="flex items-center justify-between">
<div className="flex items-center">
<img
src={eventLocationType.iconUrl}
className="h-4 w-4"
className={cx(
"h-4 w-4",
// invert all the icons except app icons
eventLocationType.iconUrl &&
!eventLocationType.iconUrl.startsWith("/app-store") &&
"dark:invert"
)}
alt={`${eventLocationType.label} logo`}
/>
<span className="line-clamp-1 ms-1 text-sm">{eventLabel}</span>
@@ -456,6 +466,7 @@ export const EventSetupTab = (
defaultValue={eventType.length ?? 15}
{...formMethods.register("length")}
addOnSuffix={<>{t("minutes")}</>}
min={1}
/>
)}
{!lengthLockedProps.disabled && (
@@ -1,4 +1,3 @@
import { SchedulingType } from "@prisma/client";
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
import { useEffect, useRef } from "react";
import type { ComponentProps } from "react";
@@ -10,6 +9,7 @@ import CheckedTeamSelect from "@calcom/features/eventtypes/components/CheckedTea
import ChildrenEventTypeSelect from "@calcom/features/eventtypes/components/ChildrenEventTypeSelect";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { SchedulingType } from "@calcom/prisma/enums";
import { Label, Select } from "@calcom/ui";
interface IUserToValue {
@@ -1,4 +1,3 @@
import { SchedulingType } from "@prisma/client";
import { Webhook as TbWebhook } from "lucide-react";
import type { TFunction } from "next-i18next";
import { Trans } from "next-i18next";
@@ -13,6 +12,7 @@ import { classNames } from "@calcom/lib";
import { CAL_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { HttpError } from "@calcom/lib/http-error";
import { SchedulingType } from "@calcom/prisma/enums";
import { trpc, TRPCClientError } from "@calcom/trpc/react";
import {
Button,
@@ -51,6 +51,7 @@ import {
} from "@calcom/ui/components/icon";
import { EmbedButton, EmbedDialog } from "@components/Embed";
import type { AvailabilityOption } from "@components/eventtype/EventAvailabilityTab";
type Props = {
children: React.ReactNode;
@@ -63,6 +64,7 @@ type Props = {
enabledWorkflowsNumber: number;
formMethods: UseFormReturn<FormValues>;
isUpdateMutationLoading?: boolean;
availability?: AvailabilityOption;
};
function getNavigation(props: {
@@ -71,8 +73,10 @@ function getNavigation(props: {
enabledAppsNumber: number;
enabledWorkflowsNumber: number;
installedAppsNumber: number;
availability: AvailabilityOption | undefined;
}) {
const { eventType, t, enabledAppsNumber, installedAppsNumber, enabledWorkflowsNumber } = props;
const { eventType, t, enabledAppsNumber, installedAppsNumber, enabledWorkflowsNumber, availability } =
props;
const duration =
eventType.metadata?.multipleDuration?.map((duration) => ` ${duration}`) || eventType.length;
@@ -128,6 +132,7 @@ function EventTypeSingleLayout({
enabledWorkflowsNumber,
isUpdateMutationLoading,
formMethods,
availability,
}: Props) {
const utils = trpc.useContext();
const { t } = useLocale();
@@ -171,6 +176,7 @@ function EventTypeSingleLayout({
enabledAppsNumber,
installedAppsNumber,
enabledWorkflowsNumber,
availability,
});
navigation.splice(1, 0, {
name: "availability",
@@ -214,7 +220,7 @@ function EventTypeSingleLayout({
});
}
return navigation;
}, [t, eventType, installedAppsNumber, enabledAppsNumber, enabledWorkflowsNumber, team]);
}, [t, eventType, installedAppsNumber, enabledAppsNumber, enabledWorkflowsNumber, team, availability]);
const permalink = `${CAL_URL}/${team ? `team/${team.slug}` : eventType.users[0].username}/${
eventType.slug
@@ -232,7 +238,7 @@ function EventTypeSingleLayout({
<div className="flex items-center justify-end">
{!eventType.metadata.managedEventConfig && (
<>
<div className="sm:hover:bg-subtle hidden items-center rounded-md px-2 lg:flex">
<div className="sm:hover:bg-muted hidden items-center rounded-md px-2 lg:flex">
<Skeleton
as={Label}
htmlFor="hiddenSwitch"
@@ -374,6 +380,8 @@ function EventTypeSingleLayout({
tabs={EventTypeTabs}
sticky
linkProps={{ shallow: true }}
itemClassname="items-start"
iconClassName="md:mt-px"
/>
</div>
<div className="p-2 md:mx-0 md:p-0 xl:hidden">
@@ -1,4 +1,4 @@
import { CalendarSwitch } from "./CalendarSwitch";
import { CalendarSwitch } from "@calcom/features/calendars/CalendarSwitch";
interface IConnectedCalendarItem {
name: string;
@@ -50,7 +50,7 @@ const SetupAvailability = (props: ISetupAvailabilityProps) => {
const updateSchedule = trpc.viewer.availability.schedule.update.useMutation(mutationOptions);
return (
<Form
className="bg-default dark:text-inverted text-emphasis w-full dark:bg-opacity-5"
className="bg-default dark:text-inverted text-emphasis w-full [--cal-brand-accent:#fafafa] dark:bg-opacity-5"
form={availabilityForm}
handleSubmit={async (values) => {
try {
@@ -35,6 +35,7 @@ const UserProfile = (props: IUserProfileProps) => {
const router = useRouter();
const createEventType = trpc.viewer.eventTypes.create.useMutation();
const telemetry = useTelemetry();
const [firstRender, setFirstRender] = useState(true);
const mutation = trpc.viewer.updateProfile.useMutation({
onSuccess: async (_data, context) => {
@@ -147,6 +148,8 @@ const UserProfile = (props: IUserProfileProps) => {
getText={() => md.render(getValues("bio") || user?.bio || "")}
setText={(value: string) => setValue("bio", turndown(value))}
excludedToolbarItems={["blockType"]}
firstRender={firstRender}
setFirstRender={setFirstRender}
/>
<p className="dark:text-inverted text-default mt-2 font-sans text-sm font-normal">
{t("few_sentences_about_yourself")}
@@ -1,85 +0,0 @@
import { useMutation } from "@tanstack/react-query";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { Badge, showToast, Switch } from "@calcom/ui";
import { ArrowLeft } from "@calcom/ui/components/icon";
export function CalendarSwitch(props: {
type: string;
externalId: string;
title: string;
defaultSelected: boolean;
isSelected: boolean;
}) {
const { t } = useLocale();
const utils = trpc.useContext();
const mutation = useMutation<
unknown,
unknown,
{
isOn: boolean;
}
>(
async ({ isOn }) => {
const body = {
integration: props.type,
externalId: props.externalId,
};
if (isOn) {
const res = await fetch("/api/availability/calendar", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) {
throw new Error("Something went wrong");
}
} else {
const res = await fetch("/api/availability/calendar", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) {
throw new Error("Something went wrong");
}
}
},
{
async onSettled() {
await utils.viewer.integrations.invalidate();
},
onError() {
showToast(`Something went wrong when toggling "${props.title}""`, "error");
},
}
);
return (
<div className="flex space-x-2 py-1 rtl:space-x-reverse">
<Switch
key={props.externalId}
name="enabled"
label={props.title}
defaultChecked={props.isSelected}
onCheckedChange={(isOn: boolean) => {
mutation.mutate({ isOn });
}}
/>
{props.defaultSelected && (
<Badge variant="gray">
<div className="flex items-center">
<ArrowLeft className="text-default mr-1 h-4 w-4" /> {t("adding_events_to")}
</div>
</Badge>
)}
</div>
);
}
-17
View File
@@ -1,17 +0,0 @@
import { useSession } from "next-auth/react";
import type { FC } from "react";
import { Fragment } from "react";
type AdminRequiredProps = {
as?: keyof JSX.IntrinsicElements;
children?: React.ReactNode;
};
/** @deprecated use PermssionContainer instead. Will delete once V2 goes live */
export const AdminRequired: FC<AdminRequiredProps> = ({ children, as, ...rest }) => {
const session = useSession();
if (session.data?.user.role !== "ADMIN") return null;
const Component = as ?? Fragment;
return <Component {...rest}>{children}</Component>;
};
+2 -6
View File
@@ -1,7 +1,6 @@
import classNames from "classnames";
import { APP_NAME, LOGO } from "@calcom/lib/constants";
import { HeadSeo } from "@calcom/ui";
import { HeadSeo, Logo } from "@calcom/ui";
import Loader from "@components/Loader";
@@ -18,10 +17,7 @@ export default function AuthContainer(props: React.PropsWithChildren<Props>) {
return (
<div className="flex min-h-screen flex-col justify-center bg-[#f3f4f6] py-12 sm:px-6 lg:px-8">
<HeadSeo title={props.title} description={props.description} />
{props.showLogo && (
// eslint-disable-next-line @next/next/no-img-element
<img className="mb-auto h-4" src={LOGO} alt={`${APP_NAME} Logo`} />
)}
{props.showLogo && <Logo small inline={false} className="mx-auto mb-auto" />}
<div className={classNames(props.showLogo ? "text-center" : "", "sm:mx-auto sm:w-full sm:max-w-md")}>
{props.heading && <h2 className="font-cal text-emphasis text-center text-3xl">{props.heading}</h2>}
+5 -3
View File
@@ -16,8 +16,10 @@ const EditableHeading = function EditableHeading({
const [isEditing, setIsEditing] = useState(false);
const enableEditing = () => setIsEditing(true);
return (
<div className="group relative cursor-pointer" onClick={enableEditing}>
<div className="flex items-center">
<div
className="group pointer-events-none relative truncate sm:pointer-events-auto"
onClick={enableEditing}>
<div className="flex cursor-pointer items-center">
<label className="min-w-8 relative inline-block">
<span className="whitespace-pre text-xl tracking-normal text-transparent">{value}&nbsp;</span>
{!isEditing && isReady && (
@@ -29,7 +31,7 @@ const EditableHeading = function EditableHeading({
value={value}
required
className={classNames(
"text-emphasis hover:text-default focus:text-emphasis absolute top-0 left-0 w-full cursor-pointer border-none bg-transparent p-0 align-top text-xl focus:outline-none focus:ring-0"
"text-emphasis hover:text-default focus:text-emphasis absolute top-0 left-0 w-full cursor-pointer truncate border-none bg-transparent p-0 align-top text-xl focus:outline-none focus:ring-0"
)}
onFocus={(e) => {
setIsEditing(true);
-29
View File
@@ -1,29 +0,0 @@
import Link from "next/link";
import { useIsEmbed } from "@calcom/embed-core/embed-iframe";
import { POWERED_BY_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
const PoweredByCal = () => {
const { t } = useLocale();
const isEmbed = useIsEmbed();
return (
<div className={"p-2 text-center text-xs sm:text-right" + (isEmbed ? " max-w-3xl" : "")}>
<Link href={POWERED_BY_URL} target="_blank" className="text-subtle opacity-50 hover:opacity-100">
{t("powered_by")}{" "}
<img
className="relative -mt-px inline h-[10px] w-auto dark:hidden"
src="/cal-logo-word.svg"
alt="Cal.com Logo"
/>
<img
className="relativ -mt-px hidden h-[10px] w-auto dark:inline"
src="/cal-logo-word-dark.svg"
alt="Cal.com Logo"
/>
</Link>
</div>
);
};
export default PoweredByCal;
@@ -195,7 +195,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
<span
className={classNames(
isInputUsernamePremium ? "border border-orange-400 " : "",
"border-default bg-muted text-subtle hidden h-9 items-center rounded-l-md border border-r-0 border-r-gray-300 px-3 text-sm md:inline-flex"
"border-default bg-muted text-subtle hidden h-9 items-center rounded-l-md border border-r-0 px-3 text-sm md:inline-flex"
)}>
{process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")}/
</span>
@@ -215,7 +215,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
: "border focus:border",
markAsError
? "focus:shadow-0 focus:ring-shadow-0 border-red-500 focus:border-red-500 focus:outline-none"
: "border-l-gray-300",
: "border-l-default",
disabled ? "bg-subtle text-muted focus:border-0" : ""
)}
value={inputUsernameValue}
@@ -234,8 +234,8 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
isInputUsernamePremium ? "text-orange-400" : "",
usernameIsAvailable ? "" : ""
)}>
{isInputUsernamePremium ? <StarIconSolid className="mt-[2px] w-6" /> : <></>}
{!isInputUsernamePremium && usernameIsAvailable ? <Check className="mt-2 w-6" /> : <></>}
{isInputUsernamePremium ? <StarIconSolid className="mt-[2px] h-4 w-4" /> : <></>}
{!isInputUsernamePremium && usernameIsAvailable ? <Check className="mt-2 h-4 w-4" /> : <></>}
</span>
</div>
</div>
@@ -136,8 +136,8 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
/>
{currentUsername !== inputUsernameValue && (
<div className="absolute right-[2px] top-6 flex flex-row">
<span className={classNames("mx-2 py-2")}>
{usernameIsAvailable ? <Check className="w-6" /> : <></>}
<span className={classNames("mx-2 py-2.5")}>
{usernameIsAvailable ? <Check className="h-4 w-4" /> : <></>}
</span>
</div>
)}
@@ -49,7 +49,7 @@ const CheckboxField = forwardRef<HTMLInputElement, Props>(
{...rest}
ref={ref}
type="checkbox"
className="text-primary-600 focus:ring-primary-500 border-default h-4 w-4 rounded"
className="text-primary-600 focus:ring-primary-500 border-default bg-default h-4 w-4 rounded"
/>
</div>
<span className="ms-3 text-sm">{description}</span>
@@ -3,6 +3,7 @@ import { components } from "react-select";
import type { EventLocationType } from "@calcom/app-store/locations";
import { classNames } from "@calcom/lib";
import cx from "@calcom/lib/classNames";
import { Select } from "@calcom/ui";
export type LocationOption = {
@@ -19,10 +20,14 @@ export type GroupOptionType = GroupBase<LocationOption>;
const OptionWithIcon = ({ icon, label }: { icon?: string; label: string }) => {
return (
<div className="flex items-center gap-3">
{/* TODO: figure out a way to invert icons when in dark mode. We can't just
dark:invert due to google meet cal etc all breaking when we do this
*/}
{icon && <img src={icon} alt="cover" className="h-3.5 w-3.5 dark:hidden" />}
{icon && (
<img
src={icon}
alt="cover"
// invert all the icons except app icons
className={cx("h-3.5 w-3.5", icon && !icon.startsWith("/app-store") && "dark:invert")}
/>
)}
<span className={classNames("text-sm font-medium")}>{label}</span>
</div>
);
+5 -3
View File
@@ -19,6 +19,8 @@ import { MetaProvider } from "@calcom/ui";
import usePublicPage from "@lib/hooks/usePublicPage";
import type { WithNonceProps } from "@lib/withNonce";
import { useViewerI18n } from "@components/I18nLanguageHandler";
const I18nextAdapter = appWithTranslation<NextJsAppProps<SSRConfig> & { children: React.ReactNode }>(
({ children }) => <>{children}</>
);
@@ -30,6 +32,7 @@ export type AppProps = Omit<NextAppProps<WithNonceProps & Record<string, unknown
isThemeSupported?: boolean;
isBookingPage?: boolean | ((arg: { router: NextRouter }) => boolean);
getLayout?: (page: React.ReactElement, router: NextRouter) => ReactNode;
PageWrapper?: (props: AppProps) => JSX.Element;
};
/** Will be defined only is there was an error */
@@ -45,9 +48,7 @@ const CustomI18nextProvider = (props: AppPropsWithChildren) => {
* i18n should never be clubbed with other queries, so that it's caching can be managed independently.
* We intend to not cache i18n query
**/
const { i18n, locale } = trpc.viewer.public.i18n.useQuery(undefined, {
trpc: { context: { skipBatch: true } },
}).data ?? {
const { i18n, locale } = useViewerI18n().data ?? {
locale: "en",
};
@@ -154,6 +155,7 @@ const AppProviders = (props: AppPropsWithChildren) => {
<SessionProvider session={session || undefined}>
<CustomI18nextProvider {...props}>
<TooltipProvider>
{/* color-scheme makes background:transparent not work which is required by embed. We need to ensure next-theme adds color-scheme to `body` instead of `html`(https://github.com/pacocoursey/next-themes/blob/main/src/index.tsx#L74). Once that's done we can enable color-scheme support */}
<CalcomThemeProvider
nonce={props.pageProps.nonce}
isThemeSupported={props.Component.isThemeSupported}
@@ -1,13 +0,0 @@
import type { BookingCreateBody } from "@calcom/prisma/zod-utils";
import * as fetch from "@lib/core/http/fetch-wrapper";
import type { BookingResponse } from "@lib/types/booking";
type BookingCreateBodyForMutation = Omit<BookingCreateBody, "location">;
const createBooking = async (data: BookingCreateBodyForMutation) => {
const response = await fetch.post<BookingCreateBodyForMutation, BookingResponse>("/api/book/event", data);
return response;
};
export default createBooking;
+13 -14
View File
@@ -7,9 +7,16 @@ 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);
// 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
@@ -61,27 +68,19 @@ const middleware: NextMiddleware = async (req) => {
}
if (url.pathname.startsWith("/api/trpc/")) {
const requestHeaders = new Headers(req.headers);
requestHeaders.set("x-cal-timezone", req.headers.get("x-vercel-ip-timezone") ?? "");
return NextResponse.next({
request: {
headers: requestHeaders,
},
});
}
if (url.pathname.startsWith("/auth/login")) {
const moreHeaders = new Headers(req.headers);
// Use this header to actually enforce CSP, otherwise it is running in Report Only mode on all pages.
moreHeaders.set("x-csp-enforce", "true");
return NextResponse.next({
request: {
headers: moreHeaders,
},
});
requestHeaders.set("x-csp-enforce", "true");
}
return NextResponse.next();
return NextResponse.next({
request: {
headers: requestHeaders,
},
});
};
export const config = {
+34
View File
@@ -1,6 +1,7 @@
require("dotenv").config({ path: "../../.env" });
const CopyWebpackPlugin = require("copy-webpack-plugin");
const os = require("os");
const glob = require("glob");
const { withAxiom } = require("next-axiom");
const { i18n } = require("./next-i18next.config");
@@ -66,6 +67,18 @@ if (process.env.ANALYZE === "true") {
}
plugins.push(withAxiom);
/** Needed to rewrite public booking page, gets all static pages but [user] */
const pages = glob
.sync("pages/**/[^_]*.{tsx,js,ts}", { cwd: __dirname })
.map((filename) =>
filename
.substr(6)
.replace(/(\.tsx|\.js|\.ts)/, "")
.replace(/\/.*/, "")
)
.filter((v, i, self) => self.indexOf(v) === i && !v.startsWith("[user]"));
/** @type {import("next").NextConfig} */
const nextConfig = {
i18n,
@@ -98,6 +111,12 @@ const nextConfig = {
transform: "lucide-react/dist/esm/icons/{{ kebabCase member }}",
preventFullImport: true,
},
"@heroicons/react/solid": {
transform: "@heroicons/react/solid/esm/{{ member }}",
},
"@heroicons/react/outline": {
transform: "@heroicons/react/outline/esm/{{ member }}",
},
"@calcom/features/insights/components": {
transform: "@calcom/features/insights/components/{{member}}",
skipDefaultConversion: true,
@@ -192,6 +211,16 @@ const nextConfig = {
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" }],
},
];
},
async headers() {
@@ -291,6 +320,11 @@ const nextConfig = {
destination: "/api/link?action=:action&email=:email&bookingUid=:bookingUid&oldToken=:oldToken",
permanent: true,
},
{
source: "/support",
destination: "/event-types?openIntercom=true",
permanent: true,
},
];
if (process.env.NEXT_PUBLIC_WEBAPP_URL === "https://app.cal.com") {
+1 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "2.8.2",
"version": "2.8.13",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
@@ -115,8 +115,6 @@
"react-select": "^5.7.0",
"react-timezone-select": "^1.4.0",
"react-use-intercom": "1.5.1",
"react-virtualized-auto-sizer": "^1.0.6",
"react-window": "^1.8.7",
"remark": "^14.0.2",
"rrule": "^2.7.1",
"sanitize-html": "^2.10.0",
@@ -155,8 +153,6 @@
"@types/qrcode": "^1.4.3",
"@types/react": "18.0.26",
"@types/react-phone-number-input": "^3.0.14",
"@types/react-virtualized-auto-sizer": "^1.0.1",
"@types/react-window": "^1.8.5",
"@types/sanitize-html": "^2.9.0",
"@types/stripe": "^8.0.417",
"@types/uuid": "8.3.1",
+4
View File
@@ -8,6 +8,8 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { HeadSeo } from "@calcom/ui";
import { BookOpen, Check, ChevronRight, FileText } from "@calcom/ui/components/icon";
import PageWrapper from "@components/PageWrapper";
import { ssgInit } from "@server/lib/ssg";
export default function Custom404() {
@@ -366,6 +368,8 @@ export default function Custom404() {
);
}
Custom404.PageWrapper = PageWrapper;
export const getStaticProps = async (context: GetStaticPropsContext) => {
const ssr = await ssgInit(context);
+4
View File
@@ -6,6 +6,8 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button, showToast } from "@calcom/ui";
import { Copy } from "@calcom/ui/components/icon";
import PageWrapper from "@components/PageWrapper";
export default function Error500() {
const { t } = useLocale();
const router = useRouter();
@@ -52,3 +54,5 @@ export default function Error500() {
</div>
);
}
Error500.PageWrapper = PageWrapper;
+6 -3
View File
@@ -32,6 +32,8 @@ import { ArrowRight } from "@calcom/ui/components/icon";
import type { inferSSRProps } from "@lib/types/inferSSRProps";
import type { EmbedProps } from "@lib/withEmbedSsr";
import PageWrapper from "@components/PageWrapper";
import { ssrInit } from "@server/lib/ssr";
export default function User(props: inferSSRProps<typeof getServerSideProps> & EmbedProps) {
@@ -57,7 +59,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
{eventTypes.map((type, index) => (
<li
key={index}
className=" border-subtle bg-default hover:bg-muted group relative border-b first:rounded-t-md last:rounded-b-md last:border-b-0">
className=" border-subtle bg-default dark:bg-muted dark:hover:bg-emphasis hover:bg-muted group relative border-b first:rounded-t-md last:rounded-b-md last:border-b-0">
<ArrowRight className="text-emphasis absolute right-3 top-3 h-4 w-4 opacity-0 transition-opacity group-hover:opacity-100" />
<Link
href={getUsernameSlugLink({ users: props.users, slug: type.slug })}
@@ -121,7 +123,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
<main
className={classNames(
shouldAlignCentrally ? "mx-auto" : "",
isEmbed ? " border-booker border-booker-width bg-default rounded-md border" : "",
isEmbed ? "border-booker border-booker-width bg-default rounded-md border" : "",
"max-w-3xl py-24 px-4"
)}>
{isSingleUser && ( // When we deal with a single user, not dynamic group
@@ -159,7 +161,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
<div
key={type.id}
style={{ display: "flex", ...eventTypeListItemEmbedStyles }}
className=" border-subtle bg-default hover:bg-muted group relative border-b first:rounded-t-md last:rounded-b-md last:border-b-0">
className="bg-default border-subtle dark:bg-muted dark:hover:bg-emphasis hover:bg-muted group relative border-b first:rounded-t-md last:rounded-b-md last:border-b-0">
<ArrowRight className="text-emphasis absolute right-4 top-4 h-4 w-4 opacity-0 transition-opacity group-hover:opacity-100" />
{/* Don't prefetch till the time we drop the amount of javascript in [user][type] page which is impacting score for [user] page */}
<div className="block w-full p-5">
@@ -196,6 +198,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
}
User.isBookingPage = true;
User.PageWrapper = PageWrapper;
const getEventTypesWithHiddenFromDB = async (userId: number) => {
return (
+2
View File
@@ -12,6 +12,7 @@ import { isBrandingHidden } from "@lib/isBrandingHidden";
import type { inferSSRProps } from "@lib/types/inferSSRProps";
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;
@@ -55,6 +56,7 @@ export default function Type(props: AvailabilityPageProps) {
}
Type.isBookingPage = true;
Type.PageWrapper = PageWrapper;
const paramsSchema = z.object({ type: z.string(), user: z.string() });
async function getUserPageProps(context: GetStaticPropsContext) {
+4 -2
View File
@@ -5,6 +5,8 @@ import type { LocationObject } from "@calcom/app-store/locations";
import { privacyFilteredLocations } from "@calcom/app-store/locations";
import { getAppFromSlug } from "@calcom/app-store/utils";
import dayjs from "@calcom/dayjs";
import getBooking from "@calcom/features/bookings/lib/get-booking";
import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
import { parseRecurringEvent } from "@calcom/lib";
import {
@@ -13,8 +15,6 @@ import {
getGroupName,
getUsernameList,
} from "@calcom/lib/defaultEvents";
import getBooking from "@calcom/lib/getBooking";
import type { GetBookingType } from "@calcom/lib/getBooking";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import prisma, { bookEventTypeSelect } from "@calcom/prisma";
@@ -26,6 +26,7 @@ import {
import type { inferSSRProps } from "@lib/types/inferSSRProps";
import PageWrapper from "@components/PageWrapper";
import BookingPage from "@components/booking/pages/BookingPage";
import { ssrInit } from "@server/lib/ssr";
@@ -70,6 +71,7 @@ export default function Book(props: BookPageProps) {
}
Book.isBookingPage = true;
Book.PageWrapper = PageWrapper;
const querySchema = z.object({
bookingUid: z.string().optional(),
@@ -18,7 +18,7 @@ export const getStaticProps: GetStaticProps<
{ user: string }
> = async (context) => {
const { user: username, month } = paramsSchema.parse(context.params);
const user = await prisma.user.findUnique({
const userWithCredentials = await prisma.user.findUnique({
where: {
username,
},
@@ -34,14 +34,15 @@ export const getStaticProps: GetStaticProps<
).startOf("day");
const endDate = startDate.endOf("month");
try {
const results = user?.credentials
const results = userWithCredentials?.credentials
? await getCachedResults(
user?.credentials,
userWithCredentials?.credentials,
startDate.format(),
endDate.format(),
user?.selectedCalendars
userWithCredentials?.selectedCalendars
)
: [];
return {
props: { results, date: new Date().toISOString() },
revalidate: 1,
+3 -91
View File
@@ -1,101 +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 { useEmbedUiConfig } from "@calcom/embed-core/src/embed-iframe";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import { trpc } from "@calcom/trpc/react";
import type { AppProps } from "@lib/app-providers";
import AppProviders from "@lib/app-providers";
import { seoConfig } from "@lib/config/next-seo.config";
import I18nLanguageHandler from "@components/I18nLanguageHandler";
import "../styles/globals.css";
const interFont = Inter({ subsets: ["latin"], variable: "--font-inter", preload: true, display: "swap" });
const calFont = localFont({
src: "../fonts/CalSans-SemiBold.woff2",
variable: "--font-cal",
preload: true,
display: "swap",
});
function MyApp(props: AppProps) {
const { Component, pageProps, err, router } = props;
let pageStatus = "200";
const { cssVarsPerTheme } = useEmbedUiConfig();
const cssVarsStyle = [];
if (cssVarsPerTheme) {
for (const [themeName, cssVars] of Object.entries(cssVarsPerTheme)) {
cssVarsStyle.push(`.${themeName} {`);
for (const [cssVarName, value] of Object.entries(cssVars)) {
cssVarsStyle.push(`--${cssVarName}: ${value};`);
}
cssVarsStyle.push(`}`);
}
}
if (router.pathname === "/404") {
pageStatus = "404";
} else if (router.pathname === "/500") {
pageStatus = "500";
}
// On client side don't let nonce creep into DOM
// It also avoids hydration warning that says that Client has the nonce value but server has "" because browser removes nonce attributes before DOM is built
// See https://github.com/kentcdodds/nonce-hydration-issues
// Set "" only if server had it set otherwise keep it undefined because server has to match with client to avoid hydration error
const nonce = typeof window !== "undefined" ? (pageProps.nonce ? "" : undefined) : pageProps.nonce;
const providerProps = {
...props,
pageProps: {
...props.pageProps,
nonce,
},
};
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => page);
return (
<AppProviders {...providerProps}>
<DefaultSeo {...seoConfig.defaultNextSeo} />
<I18nLanguageHandler />
<Script
nonce={nonce}
id="page-status"
dangerouslySetInnerHTML={{ __html: `window.CalComPageStatus = '${pageStatus}'` }}
/>
<style jsx global>{`
:root {
--font-inter: ${interFont.style.fontFamily};
--font-cal: ${calFont.style.fontFamily};
}
`}</style>
<style jsx global>
{`
${cssVarsStyle.join("")}
`}
</style>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
</Head>
{getLayout(
Component.requiresLicense ? (
<LicenseRequired>
<Component {...pageProps} err={err} />
</LicenseRequired>
) : (
<Component {...pageProps} err={err} />
),
router
)}
</AppProviders>
);
const { Component, pageProps } = props;
if (Component.PageWrapper !== undefined) return Component.PageWrapper(props);
return <Component {...pageProps} />;
}
export default trpc.withTRPC(MyApp);
+13 -9
View File
@@ -1,23 +1,29 @@
import type { NextPageContext } from "next";
import type { DocumentContext, DocumentProps } from "next/document";
import Document, { Head, Html, Main, NextScript } from "next/document";
import Script from "next/script";
import { z } from "zod";
import { getDirFromLang } from "@calcom/lib/i18n";
import { csp } from "@lib/csp";
type Props = Record<string, unknown> & DocumentProps;
function setHeader(ctx: NextPageContext, name: string, value: string) {
try {
ctx.res?.setHeader(name, value);
} catch (e) {
// Getting "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client" when revalidate calendar chache
console.log(`Error setting header ${name}=${value} for ${ctx.asPath || "unknown asPath"}`, e);
}
}
class MyDocument extends Document<Props> {
static async getInitialProps(ctx: DocumentContext) {
const { nonce } = csp(ctx.req || null, ctx.res || null);
if (!process.env.CSP_POLICY) {
ctx.res?.setHeader("x-csp", "not-opted-in");
setHeader(ctx, "x-csp", "not-opted-in");
} else if (!ctx.res?.getHeader("x-csp")) {
// If x-csp not set by gSSP, then it's initialPropsOnly
ctx.res?.setHeader("x-csp", "initialPropsOnly");
setHeader(ctx, "x-csp", "initialPropsOnly");
}
const asPath = ctx.asPath || "";
// Use a dummy URL as default so that URL parsing works for relative URLs as well. We care about searchParams and pathname only
const parsedUrl = new URL(asPath, "https://dummyurl");
@@ -31,9 +37,8 @@ class MyDocument extends Document<Props> {
const { isEmbed } = this.props;
const nonceParsed = z.string().safeParse(this.props.nonce);
const nonce = nonceParsed.success ? nonceParsed.data : "";
const dir = getDirFromLang(locale);
return (
<Html lang={locale} dir={dir}>
<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" />
@@ -42,7 +47,6 @@ class MyDocument extends Document<Props> {
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#000000" />
<meta name="msapplication-TileColor" content="#ff0000" />
<meta name="theme-color" content="var(--cal-bg)" />
<Script src="/embed-init-iframe.js" strategy="beforeInteractive" />
</Head>
<body
+1 -2
View File
@@ -8,10 +8,9 @@ import NextError from "next/error";
import React from "react";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { HttpError } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import { HttpError } from "@lib/core/http/error";
import { ErrorPage } from "@components/error/error-page";
// Adds HttpException to the list of possible error types.
+1 -1
View File
@@ -1,4 +1,3 @@
import { IdentityProvider } from "@prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";
import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode";
@@ -6,6 +5,7 @@ import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { hashPassword } from "@calcom/features/auth/lib/hashPassword";
import { verifyPassword } from "@calcom/features/auth/lib/verifyPassword";
import prisma from "@calcom/prisma";
import { IdentityProvider } from "@calcom/prisma/enums";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const session = await getServerSession({ req, res });
+1 -2
View File
@@ -1,8 +1,7 @@
import type { NextApiRequest, NextApiResponse } from "next";
import jackson from "@calcom/features/ee/sso/lib/jackson";
import { HttpError } from "@lib/core/http/error";
import { HttpError } from "@calcom/lib/http-error";
// This is the callback endpoint for the OIDC provider
// A team must set this endpoint in the OIDC provider's configuration
+1 -2
View File
@@ -2,8 +2,7 @@ import type { OAuthReq } from "@boxyhq/saml-jackson";
import type { NextApiRequest, NextApiResponse } from "next";
import jackson from "@calcom/features/ee/sso/lib/jackson";
import type { HttpError } from "@lib/core/http/error";
import type { HttpError } from "@calcom/lib/http-error";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { oauthController } = await jackson();
+1 -1
View File
@@ -1,4 +1,3 @@
import { IdentityProvider } from "@prisma/client";
import type { NextApiRequest } from "next";
import z from "zod";
@@ -8,6 +7,7 @@ import { HttpError } from "@calcom/lib/http-error";
import { defaultHandler, defaultResponder } from "@calcom/lib/server";
import slugify from "@calcom/lib/slugify";
import prisma from "@calcom/prisma";
import { IdentityProvider } from "@calcom/prisma/enums";
const querySchema = z.object({
username: z
+15 -4
View File
@@ -1,10 +1,10 @@
import { IdentityProvider } from "@prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";
import { hashPassword } from "@calcom/features/auth/lib/hashPassword";
import slugify from "@calcom/lib/slugify";
import { closeComUpsertTeamUser } from "@calcom/lib/sync/SyncServiceManager";
import prisma from "@calcom/prisma";
import { IdentityProvider } from "@calcom/prisma/enums";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "POST") {
@@ -36,14 +36,25 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return;
}
// There is actually an existingUser if username matches
// OR if email matches and both username and password are set
// 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
const existingUser = await prisma.user.findFirst({
where: {
OR: [
{ username },
{
AND: [{ email: userEmail }],
AND: [
{ email: userEmail },
{
OR: [
{ emailVerified: { not: null } },
{
AND: [{ password: { not: null } }, { username: { not: null } }],
},
],
},
],
},
],
},
@@ -1,4 +1,3 @@
import { IdentityProvider } from "@prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";
import { authenticator } from "otplib";
import qrcode from "qrcode";
@@ -8,6 +7,7 @@ import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { verifyPassword } from "@calcom/features/auth/lib/verifyPassword";
import { symmetricEncrypt } from "@calcom/lib/crypto";
import prisma from "@calcom/prisma";
import { IdentityProvider } from "@calcom/prisma/enums";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "POST") {
+4 -4
View File
@@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return;
}
const user = await prisma.user.findUnique({
const userWithCredentials = await prisma.user.findUnique({
where: {
id: session.user.id,
},
@@ -25,11 +25,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
selectedCalendars: true,
},
});
if (!user) {
if (!userWithCredentials) {
res.status(401).json({ message: "Not authenticated" });
return;
}
const { credentials, ...user } = userWithCredentials;
if (req.method === "POST") {
await prisma.selectedCalendar.upsert({
@@ -80,7 +80,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
// get user's credentials + their connected integrations
const calendarCredentials = getCalendarCredentials(user.credentials);
const calendarCredentials = getCalendarCredentials(credentials);
// get all the connected integrations' calendars (from third party)
const { connectedCalendars } = await getConnectedCalendars(calendarCredentials, user.selectedCalendars);
const calendars = connectedCalendars.flatMap((c) => c.calendars).filter(notEmpty);
+1 -1
View File
@@ -1,4 +1,3 @@
import { BookingStatus, ReminderType } from "@prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";
import dayjs from "@calcom/dayjs";
@@ -7,6 +6,7 @@ import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventR
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
import { getTranslation } from "@calcom/lib/server/i18n";
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
import { BookingStatus, ReminderType } from "@calcom/prisma/enums";
import type { CalendarEvent } from "@calcom/types/Calendar";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
+1 -2
View File
@@ -4,12 +4,11 @@ import type { Session } from "next-auth";
import getInstalledAppPath from "@calcom/app-store/_utils/getInstalledAppPath";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { deriveAppDictKeyFromType } from "@calcom/lib/deriveAppDictKeyFromType";
import { HttpError } from "@calcom/lib/http-error";
import { revalidateCalendarCache } from "@calcom/lib/server/revalidateCalendarCache";
import prisma from "@calcom/prisma";
import type { AppDeclarativeHandler, AppHandler } from "@calcom/types/AppHandler";
import { HttpError } from "@lib/core/http/error";
const defaultIntegrationAddHandler = async ({
slug,
supportsMultipleInstalls,
+28
View File
@@ -0,0 +1,28 @@
import type { NextApiRequest, NextApiResponse } from "next";
import crypto from "node:crypto";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { defaultHandler } from "@calcom/lib/server";
async function handler(req: NextApiRequest, res: NextApiResponse) {
const session = await getServerSession({ req, res });
const secret = process.env.INTERCOM_SECRET;
if (!session) {
return res.status(401).json({ message: "user not authenticated" });
}
if (!secret) {
return res.status(400).json({ message: "Intercom Identity Verification secret not set" });
}
const hmac = crypto.createHmac("sha256", secret);
hmac.update(String(session.user.id));
const hash = hmac.digest("hex");
return res.status(200).json({ hash });
}
export default defaultHandler({
GET: Promise.resolve({ default: handler }),
});
+4 -18
View File
@@ -1,4 +1,3 @@
import { UserPermissionRole } from "@prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
@@ -7,7 +6,7 @@ import { defaultResponder } from "@calcom/lib/server";
import prisma from "@calcom/prisma";
import { TRPCError } from "@calcom/trpc/server";
import { createContext } from "@calcom/trpc/server/createContext";
import { viewerRouter } from "@calcom/trpc/server/routers/viewer";
import { viewerRouter } from "@calcom/trpc/server/routers/viewer/_router";
enum DirectAction {
ACCEPT = "accept",
@@ -27,31 +26,18 @@ const decryptedSchema = z.object({
async function handler(req: NextApiRequest, res: NextApiResponse<Response>) {
const { action, token, reason } = querySchema.parse(req.query);
const { bookingUid, userId } = decryptedSchema.parse(
const { bookingUid } = decryptedSchema.parse(
JSON.parse(symmetricDecrypt(decodeURIComponent(token), process.env.CALENDSO_ENCRYPTION_KEY || ""))
);
/** 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 */,
};
}
const booking = await prisma.booking.findUniqueOrThrow({
where: { uid: bookingUid },
});
try {
/** @see https://trpc.io/docs/server-side-calls */
const ctx = await createContext({ req, res }, sessionGetter);
const caller = viewerRouter.createCaller(ctx);
const ctx = await createContext({ req, res });
const caller = viewerRouter.createCaller({ ...ctx, req, res });
await caller.bookings.confirm({
bookingId: booking.id,
recurringEventId: booking.recurringEventId || undefined,
+80
View File
@@ -0,0 +1,80 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import { IS_SELF_HOSTED, LOGO, LOGO_ICON, WEBAPP_URL } from "@calcom/lib/constants";
function removePort(url: string) {
return url.replace(/:\d+$/, "");
}
function extractSubdomainAndDomain(hostname: string) {
const hostParts = removePort(hostname).split(".");
const subdomainParts = hostParts.slice(0, hostParts.length - 2);
const domain = hostParts.slice(hostParts.length - 2).join(".");
return [subdomainParts[0], domain];
}
const logoApiSchema = z.object({
icon: z.coerce.boolean().optional(),
});
const SYSTEM_SUBDOMAINS = ["console", "app", "www"];
async function getTeamLogos(subdomain: string) {
if (
// if not cal.com
IS_SELF_HOSTED ||
// missing subdomain (empty string)
!subdomain ||
// in SYSTEM_SUBDOMAINS list
SYSTEM_SUBDOMAINS.includes(subdomain)
) {
return {
appLogo: `${WEBAPP_URL}${LOGO}`,
appIconLogo: `${WEBAPP_URL}${LOGO_ICON}`,
};
}
// load from DB
const { default: prisma } = await import("@calcom/prisma");
const team = await prisma.team.findUniqueOrThrow({
where: {
slug: subdomain,
},
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}`,
};
}
/**
* This API endpoint is used to serve the logo associated with a team if no logo is found we serve our default logo
*/
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { query } = req;
const parsedQuery = logoApiSchema.parse(query);
const hostname = req?.headers["host"];
if (!hostname) throw new Error("No hostname");
const domains = extractSubdomainAndDomain(hostname);
if (!domains) throw new Error("No domains");
const [subdomain] = domains;
const { appLogo, appIconLogo } = await getTeamLogos(subdomain);
const filteredLogo = parsedQuery?.icon ? appIconLogo : appLogo;
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);
}
+26
View File
@@ -0,0 +1,26 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import { defaultResponder } from "@calcom/lib/server";
const newBookerSchema = z.object({
status: z.enum(["enable", "disable"]),
});
/**
* Very basic temporary api route to enable/disable new booker access.
*/
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { status } = newBookerSchema.parse(req.query);
if (status === "enable") {
const expires = new Date();
expires.setFullYear(expires.getFullYear() + 1);
res.setHeader("Set-Cookie", `new-booker-enabled=true; path=/; expires=${expires.toUTCString()}`);
} else {
res.setHeader("Set-Cookie", "new-booker-enabled=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT");
}
res.send({ status: 200, body: `Done – ${status}` });
}
export default defaultResponder(handler);
@@ -54,7 +54,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
user: {
select: {
id: true,
credentials: true,
timeZone: true,
email: true,
name: true,
-83
View File
@@ -1,83 +0,0 @@
/**
* This file contains tRPC's HTTP response handler
*/
import { z } from "zod";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import * as trpcNext from "@calcom/trpc/server/adapters/next";
import { createContext as createTrpcContext } from "@calcom/trpc/server/createContext";
import { appRouter } from "@calcom/trpc/server/routers/_app";
export default trpcNext.createNextApiHandler({
router: appRouter,
/**
* @link https://trpc.io/docs/context
*/
createContext: ({ req, res }) => {
const sessionGetter = () => getServerSession({ req, res });
return createTrpcContext({ req, res }, sessionGetter);
},
/**
* @link https://trpc.io/docs/error-handling
*/
onError({ error }) {
if (error.code === "INTERNAL_SERVER_ERROR") {
// send to bug reporting
console.error("Something went wrong", error);
}
},
/**
* Enable query batching
*/
batching: {
enabled: true,
},
/**
* @link https://trpc.io/docs/caching#api-response-caching
*/
responseMeta({ ctx, paths, type, errors }) {
// Some helpers relevant to this function only
const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
// assuming you have all your public routes with the keyword `public` in them
const allPublic = paths && paths.every((path) => path.startsWith("viewer.public."));
// checking that no procedures errored
const allOk = errors.length === 0;
// checking we're doing a query request
const isQuery = type === "query";
const noHeaders = {};
// We cannot set headers on SSG queries
if (!ctx?.res) return noHeaders;
const defaultHeaders: Record<"headers", Record<string, string>> = {
headers: {},
};
const timezone = z.string().safeParse(ctx.req?.headers["x-vercel-ip-timezone"]);
if (timezone.success) defaultHeaders.headers["x-cal-timezone"] = timezone.data;
// We need all these conditions to be true to set cache headers
if (!(allPublic && allOk && isQuery)) return defaultHeaders;
// No cache by default
defaultHeaders.headers["cache-control"] = `no-cache`;
// Our cache can change depending on our current paths value. Since paths is an array,
// we want to create a map that can match potential paths with their desired cache value
const cacheRules = {
"viewer.public.session": `no-cache`,
"viewer.public.i18n": `no-cache`,
// Revalidation time here should be 1 second, per https://github.com/calcom/cal.com/pull/6823#issuecomment-1423215321
"viewer.public.slots.getSchedule": `no-cache`, // FIXME
"viewer.public.cityTimezones": `max-age=${ONE_DAY_IN_SECONDS}, stale-while-revalidate`,
} as const;
// Find which element above is an exact match for this group of paths
const matchedPath = paths.find((v) => v in cacheRules) as keyof typeof cacheRules;
if (matchedPath) defaultHeaders.headers["cache-control"] = cacheRules[matchedPath];
return defaultHeaders;
},
});
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { apiKeysRouter } from "@calcom/trpc/server/routers/viewer/apiKeys/_router";
export default createNextApiHandler(apiKeysRouter);
@@ -0,0 +1,4 @@
import appRoutingForms from "@calcom/app-store/routing-forms/trpc-router";
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
export default createNextApiHandler(appRoutingForms);
+4
View File
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { appsRouter } from "@calcom/trpc/server/routers/viewer/apps/_router";
export default createNextApiHandler(appsRouter);
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { appsRouter } from "@calcom/trpc/server/routers/viewer/apps/_router";
export default createNextApiHandler(appsRouter);
+4
View File
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { authRouter } from "@calcom/trpc/server/routers/viewer/auth/_router";
export default createNextApiHandler(authRouter);
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { availabilityRouter } from "@calcom/trpc/server/routers/viewer/availability/_router";
export default createNextApiHandler(availabilityRouter);
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { bookingsRouter } from "@calcom/trpc/server/routers/viewer/bookings/_router";
export default createNextApiHandler(bookingsRouter);
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { deploymentSetupRouter } from "@calcom/trpc/server/routers/viewer/deploymentSetup/_router";
export default createNextApiHandler(deploymentSetupRouter);
+4
View File
@@ -0,0 +1,4 @@
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 { eventTypesRouter } from "@calcom/trpc/server/routers/viewer/eventTypes/_router";
export default createNextApiHandler(eventTypesRouter);
@@ -0,0 +1,4 @@
import { featureFlagRouter } from "@calcom/features/flags/server/router";
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
export default createNextApiHandler(featureFlagRouter);
@@ -0,0 +1,4 @@
import { insightsRouter } from "@calcom/features/insights/server/trpc-router";
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
export default createNextApiHandler(insightsRouter);
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { paymentsRouter } from "@calcom/trpc/server/routers/viewer/payments/_router";
export default createNextApiHandler(paymentsRouter);
+4
View File
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { publicViewerRouter } from "@calcom/trpc/server/routers/publicViewer/_router";
export default createNextApiHandler(publicViewerRouter, true);
+4
View File
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { ssoRouter } from "@calcom/trpc/server/routers/viewer/sso/_router";
export default createNextApiHandler(ssoRouter);
+4
View File
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { slotsRouter } from "@calcom/trpc/server/routers/viewer/slots/_router";
export default createNextApiHandler(slotsRouter);

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