+22



![kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)





Omar López
GitHub
Peer Richelsen
Carina Wollendorfer
CarinaWolli
kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Hariom Balhara
Syed Ali Shahbaz
Joe Au-Yeung
Leo Giovanetti
Alex van Andel
Bailey Pumfleet
GitStart
gitstart
Arthur Cruz
Mitchell Moore
Agusti Fernandez Pardo
Agusti Fernandez Pardo
sean-brydon
andreaestefania12
Miguel Nieto A
alannnc
Hashen
buschco
iamkun
github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Crowdin Bot
Ankit Gordhandas
depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
Ken Miller
Júlio Piubello da Silva Cabral
Arun Kumar
81d33b51bc
* Turbo fixes * Make apps single pages public * fixed /booking skeleton (#2722) * fixed /booking skeleton * nit * Type fixes * Test fixes * Update playwright.config.ts * More test fixes * Update dynamic-booking-pages.test.ts * add invite link to Zapier setup page (#2696) * add invite link and toaster to zapier setup page * create env variable for invite link and save in database * fetch invite link form getStaticProps * add getStaticPath method * clean code * Moves app setup and index page * Moves Loader to ui * Trying new way to handle dynamic app store pages * Cleanup * Update tailwind.config.js * zapier invite link fixes * Tests fixes Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: zomars <zomars@me.com> * Add more embed events (#2719) * Add more embed events * Add more embed events Co-authored-by: Peer Richelsen <peeroke@gmail.com> * adds availability select loader (#2718) * Improve logs and Fix unwanted 500 to reduce noise in logs (#2674) * Improve logs * Fix unintentional 500 Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Change date format for RecurringBookings (#2707) * Change date format for RecurringBookings * Missing bookingId query param Co-authored-by: Leo Giovanetti <hello@leog.me> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * added giphy description (#2730) * fixes #2732 (#2732) * Hotfix : Fix Infinite loading of Bookings (#2729) * Add more embed events * Add more embed events * Fix nextCursor calculation logic Co-authored-by: Peer Richelsen <peeroke@gmail.com> * Hotfix: Success page for recurring event (#2725) * Merge pull request #2672 from calcom/main v1.5.4 * Turbo fixes * Make apps single pages public * Fix preview.html not built and thus served during depooy (#2713) * Hotfix: Success page layout broken due to duplicate "When" (#2716) * Update BookingPage.tsx * Reverting unchaged lines * Fixing recurrenceRule for ICS files Co-authored-by: Omar López <zomars@me.com> Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> * Fix preview.html not built and thus served during depooy (#2727) Co-authored-by: Omar López <zomars@me.com> * Allow deletion of a disabled event (#2737) * allows deletion of disabled event * some visual fixes Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Multiple E2E improvements * Parallelizes some tests * Update booking-pages.test.ts * E2E and paid bookings fixes * Add 'free' and 'workingElsewhere' as a non-blocking event (#2652) * Add 'free' and 'workingElsewhere' to non-blocking event - this will allow bookings at these times * Update CalendarService.ts Co-authored-by: zomars <zomars@me.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * TODO marks blocking test to be fixed * Update testUtils.ts * Update testUtils.ts * getBusyTimes consolidation * Fixes delete-me test * E2E fixing attemps * Adjusting Zapier endpoints for publishing integration (#2728) * add /me endpoint for zapier API testing * remove cacellationReason from listBookings response Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Omar López <zomars@me.com> * Fix reschedule not happening in calendar if two calendards are there (#2733) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> * Increases timeout temporarily * Merge pull request #2745 from calcom/apps/multiple-categories Allow apps to belong to multiple categories * Build fixes * Populate msteams key in db (#2743) * Populate msteams key in db * Fix calendar credentials to teams * Clarify account dialog Co-authored-by: Omar López <zomars@me.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Bailey Pumfleet <pumfleet@hey.com> * Improving Email DRYness (#2486) * Email DRY * WIP * Improve email DRYness Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * E2E fixtures (#2747) * Sign in button should be changed or disabled after click #2654 (#2749) Co-authored-by: gitstart <gitstart@users.noreply.github.com> * Fixing ESLint warnings (#2723) * Fixing warnings * Reverting and disabling ESLint in some cases * Reverting Next Images * Reverting file, bad merge * Targeting ESLint to line * Additional warnings * New warning squished * More tweaks and major fixes * Uneeded conf Co-authored-by: Omar López <zomars@me.com> * Fix/avoid multiple schedule deletions (#2602) * Prevents users from deleting the same schedule multiple times due to delay before the schedule disappears. It also applies the same fix to team disband. Schedule deletion:  Team disband:  Fixes issue [#2569](https://github.com/calcom/cal.com/issues/2569) Bug fix (non-breaking change which fixes an issue) **apps/web/components/LightLoader.tsx** → this file was created in order to make a light color loading spinner available. It's necessary when we need to display a loading spinner above dark backgrounds. **apps/web/components/availability/ScheduleListItem.tsx** → this component was created in order to give a schedule list item its own state. * Removing a "setTimeout" that was only used for testing purposes * Adding a code review suggestion to my modifications * Changing loading style * Cleanup * Avoids using unnecessary state * Revert "Adding a code review suggestion to my modifications" This reverts commit b5e40062d71157ca1d9384fb1f5c30d50625809d. * Reverts some changes * Renames isLoading Co-authored-by: Alex van Andel <me@alexvanandel.com> Co-authored-by: Omar López <zomars@me.com> * Added check on create eventtype to see is user has slug that already exists (#2757) * Added check on create eventtype to see is user has slug that already exists, added error check on onError * revert yarn.lock back * Catches prisma known error instead of making an additional query Co-authored-by: zomars <zomars@me.com> * Fix white border (#2761) * fix: remove hardcoded redirect in signin url email verification (#2764) Co-authored-by: Agusti Fernandez Pardo <git@agusti.me> * Bad UX when user wants to set the default Event Type Title #2245 (#2760) Co-authored-by: gitstart <gitstart@users.noreply.github.com> * Skip sending emails in E2E * Users Phone Number Option (#2669) * Users Phone Number Option * Implemented improvments * Add validation to form Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: zomars <zomars@me.com> * Fix/unpaid unconfirmed (#2553) * Fix merge errors * Errors prettier/prettier * Update apps/web/pages/api/book/event.ts Co-authored-by: Miguel Nieto A <39246879+miguelnietoa@users.noreply.github.com> * Update apps/web/pages/api/book/event.ts Co-authored-by: Miguel Nieto A <39246879+miguelnietoa@users.noreply.github.com> * Update apps/web/pages/api/integrations.ts Co-authored-by: Miguel Nieto A <39246879+miguelnietoa@users.noreply.github.com> * Fix merge errors * Errors prettier/prettier * Update apps/web/pages/api/book/confirm.ts Co-authored-by: alannnc <alannnc@gmail.com> * Modal window before delete stripe integration * ESLint Report * Test fixes Co-authored-by: Miguel Nieto A <39246879+miguelnietoa@users.noreply.github.com> Co-authored-by: alannnc <alannnc@gmail.com> Co-authored-by: zomars <zomars@me.com> * Mutually exclusive options (#2755) Co-authored-by: Omar López <zomars@me.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Readd steps to create a new user #2665 (#2759) Co-authored-by: gitstart <gitstart@users.noreply.github.com> * remove redundant conditional expressions (#2756) * remove redundant conditional expressions * remove redundant conditional expression Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> * Fix adds redirect callback that support app.cal.com (#2768) * Fix adds redirect callback that support app.cal.com * Update apps/web/pages/api/auth/[...nextauth].tsx Check origin of website and baseurl Co-authored-by: Omar López <zomars@me.com> * fix: lint issue extra space removed Co-authored-by: Agusti Fernandez Pardo <git@agusti.me> Co-authored-by: Omar López <zomars@me.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * respect local set timezone and update url on mismatch (#2506) * ensure `timeZone()` will make its way to the URL fixes https://github.com/calcom/cal.com/issues/2482 * keep `timeZone()` and the offset from URL in sync Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Squashed commit of the following: commit27540b09ceAuthor: Agusti Fernandez Pardo <me@agusti.me> Date: Mon May 16 17:34:13 2022 +0200 fix: remove hardcoded redirect in signin url email verification (#2764) Co-authored-by: Agusti Fernandez Pardo <git@agusti.me> commitae15a7d739Author: Hariom Balhara <hariombalhara@gmail.com> Date: Tue May 10 14:30:43 2022 +0530 Fix time issue commit2a5a89fe50Author: Leo Giovanetti <hello@leog.me> Date: Wed May 11 10:21:46 2022 -0300 Missing fix for success page commit2ce1e78053Author: Leo Giovanetti <hello@leog.me> Date: Wed May 11 10:12:59 2022 -0300 Hotfix: Success page for recurring event (#2725) * Merge pull request #2672 from calcom/main v1.5.4 * Turbo fixes * Make apps single pages public * Fix preview.html not built and thus served during depooy (#2713) * Hotfix: Success page layout broken due to duplicate "When" (#2716) * Update BookingPage.tsx * Reverting unchaged lines * Fixing recurrenceRule for ICS files Co-authored-by: Omar López <zomars@me.com> Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> commit2d6d1cb444Author: Hariom Balhara <hariombalhara@gmail.com> Date: Tue May 10 14:49:46 2022 +0530 Hotfix: Success page layout broken due to duplicate "When" (#2716) commitef68f4f4f8Author: Hariom Balhara <hariombalhara@gmail.com> Date: Tue May 10 10:54:20 2022 +0530 Fix preview.html not built and thus served during depooy (#2713) commit18c28cc3fdAuthor: zomars <zomars@me.com> Date: Mon May 9 16:17:07 2022 -0600 Make apps single pages public commitd40e8caff9Author: zomars <zomars@me.com> Date: Mon May 9 16:08:03 2022 -0600 Turbo fixes commit3161cc4d45Merge:ed808c3be4099a477dAuthor: zomars <zomars@me.com> Date: Mon May 9 14:58:33 2022 -0600 Merge branch 'main' into production commited808c3be6Author: Omar López <zomars@me.com> Date: Mon May 9 14:56:23 2022 -0600 Merge pull request #2672 from calcom/main v1.5.4 * Typo * Typo * Update apps/web/pages/apps/categories/[category].tsx * Apply suggestions from code review * Alert to describe exclusion of options (#2770) * Alert to describe exclusion of options * Update apps/web/pages/event-types/[type].tsx Co-authored-by: Omar López <zomars@me.com> * Update apps/web/components/eventtype/RecurringEventController.tsx Co-authored-by: Omar López <zomars@me.com> * Formatting Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Omar López <zomars@me.com> * fix: split time correctly if the local working hours are just across mid night (#2766) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Update crowdin.yml * New Crowdin translations by Github Action (#2773) * New Crowdin translations by Github Action * Update vital.json * Update vital.json Co-authored-by: Crowdin Bot <support+bot@crowdin.com> Co-authored-by: Omar López <zomars@me.com> * Add Google cal extneral calendar id to booking reference (#2671) * Set google cal event id to use our uid * Save calendar external id to bookingRef * Pass external calendar ids to update and delete * Create migration * Fix type errors * Fix prisma url Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Omar López <zomars@me.com> * v1.6 * v1.6 * 2FA submit disabled (#2790) * fixing the hyperlink for open startup (#2777) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Add login event (#2784) Co-authored-by: Bailey Pumfleet <pumfleet@hey.com> * Fixes infinite loop * Fixes infinite loop * Fixes infinite loop * Update all Yarn dependencies (2022-05-16) (#2769) * Update all Yarn dependencies (2022-05-16) * Upgrade dependencies * Removes deprecated packages * Upgrades deps * Updates submodules * Update yarn.lock * Linting * Linting * Update website * Build fixes * TODO: fix this * Module resolving * Type fixes * Intercom fixes on SSG * Fixes infinite loop * Upgrades to React 18 * Type fixes * Locks node version to 14 * Upgrades daily-js * Readds missing types * Upgrades playwright * Noop when intercom is not installed * Update website * Removed yarn.lock in favor of monorepo Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> Co-authored-by: zomars <zomars@me.com> * Create ci.yml * Update ci.yml * Reintroduces typescript-eslint Buckle up! * Type fixes * Update ci.yml * Update api * Update admin * Reusable inferSSRProps * Linting * Linting * Prisma fixes * Update ci.yml * Cache testing * Update e2e.yml * Update DatePicker.tsx * Update e2e.yml * Revert "Linting" This reverts commitadf817766e. * Revert "Linting" This reverts commit1b59dacd64. * Linting * Update e2e.yml * Ci updates * Add team Id to hash url (#2803) * Fix missing tabs - Embed (#2804) * Fix missing tabs * Fix Eslint error * Fix Eslint errors * Add import statement (#2812) * Add import statement * Update apps/docs/next.config.js Co-authored-by: Omar López <zomars@me.com> * Show success page if booking was deleted on calendar (#2808) * Add exception to 410 * Fix type error * Add GoogelCalError type * only show invite link for app.cal.dev (#2807) Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Omar López <zomars@me.com> * fix: update eslint config to test .ts and .js separately (#2805) * fix: update eslint config * fix: update ts ignore * fix: update eslint config * Update TeamAvailabilityScreen.tsx * Type fixes * Update useIntercom.ts Co-authored-by: Omar López <zomars@me.com> * fix: sync api to latest commit (#2810) Co-authored-by: Agusti Fernandez Pardo <git@agusti.me> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Embed React improvements (#2782) * Add off support. Add getApi export. * Add publish command * Add embed-snippet in prod deps * Update README * Update package.json Co-authored-by: Bailey Pumfleet <pumfleet@hey.com> Co-authored-by: zomars <zomars@me.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Consolidates test-results * Add vscode tasks.json (#2801) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * save additional inputs as json + view details of booking (#2796) * move custom inputs from description to own json object * show custom inputs on success page * fix type error * add custom inputs to email and webhook * add custom inputs to all emails * add values for custom inputs when rescheduling * add custom input everywhere description is shown * fix bug with boolean value * fix issues with null values * disable custom inputs and add notes for organizer * don't show custom input with empty string * don't show custom inputs with empty string in calender event and email * add link to booking details page * redirect to success page to see booking details * add functionality to cancel and reschedule booking * fix bookings that require confirmation * clean code * fix infinite lopp in useEffect of success page * show web conference details message when integration as location * improve design of cancelling event * clean code * disable darkmode for organizer on booking details page * fix dark mode for cancelling booking * fix build error * Fixes infinite loop * Fixes infinite loop * Fixes infinite loop * Update all Yarn dependencies (2022-05-16) (#2769) * Update all Yarn dependencies (2022-05-16) * Upgrade dependencies * Removes deprecated packages * Upgrades deps * Updates submodules * Update yarn.lock * Linting * Linting * Update website * Build fixes * TODO: fix this * Module resolving * Type fixes * Intercom fixes on SSG * Fixes infinite loop * Upgrades to React 18 * Type fixes * Locks node version to 14 * Upgrades daily-js * Readds missing types * Upgrades playwright * Noop when intercom is not installed * Update website * Removed yarn.lock in favor of monorepo Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> Co-authored-by: zomars <zomars@me.com> * Create ci.yml * Update ci.yml * Reintroduces typescript-eslint Buckle up! * Type fixes * Update ci.yml * Update api * Update admin * Reusable inferSSRProps * Linting * Linting * Prisma fixes * Update ci.yml * Cache testing * Update e2e.yml * Update DatePicker.tsx * Update e2e.yml * Revert "Linting" This reverts commitadf817766e. * Revert "Linting" This reverts commit1b59dacd64. * Linting * Update e2e.yml * Ci updates * Add team Id to hash url (#2803) * Fix missing tabs - Embed (#2804) * Fix missing tabs * Fix Eslint error * Fix Eslint errors * Add import statement (#2812) * Add import statement * Update apps/docs/next.config.js Co-authored-by: Omar López <zomars@me.com> * Show success page if booking was deleted on calendar (#2808) * Add exception to 410 * Fix type error * Add GoogelCalError type * only show invite link for app.cal.dev (#2807) Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Omar López <zomars@me.com> * fix: update eslint config to test .ts and .js separately (#2805) * fix: update eslint config * fix: update ts ignore * fix: update eslint config * Update TeamAvailabilityScreen.tsx * Type fixes * Update useIntercom.ts Co-authored-by: Omar López <zomars@me.com> * fix: sync api to latest commit (#2810) Co-authored-by: Agusti Fernandez Pardo <git@agusti.me> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Embed React improvements (#2782) * Add off support. Add getApi export. * Add publish command * Add embed-snippet in prod deps * Update README * Update package.json Co-authored-by: Bailey Pumfleet <pumfleet@hey.com> Co-authored-by: zomars <zomars@me.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Consolidates test-results * Type fixes * Abstracts minimal booking select * Type fixes * Update listBookings.ts * Update common.json * Update bookingReminder.ts * Consolidates isOutOfBounds * Update webhookResponse-chromium.txt * Update TableActions.tsx * Type fixes * Update BookingPage.tsx * Update webhookResponse-chromium.txt Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Alex van Andel <me@alexvanandel.com> Co-authored-by: Bailey Pumfleet <pumfleet@hey.com> Co-authored-by: zomars <zomars@me.com> Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Co-authored-by: iamkun <kunhello@outlook.com> Co-authored-by: Agusti Fernandez Pardo <me@agusti.me> Co-authored-by: Agusti Fernandez Pardo <git@agusti.me> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Update check-types.yml * adding organizer as attendee to google calendar events (#2779) * Fix auto-select and close of dropdown (#2819) * fixes dynamic color and typefix for tfunction after react upgrade (#2821) * New Crowdin translations by Github Action (#2791) Co-authored-by: Crowdin Bot <support+bot@crowdin.com> * Relocates admin to console # Conflicts: # apps/admin * Relocates admin to console * Fix login submit (#2849) * fix: long string overflowing calendar div (#2842) Co-authored-by: gitstart <gitstart@users.noreply.github.com> Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev> * Adding labels (#2783) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Wrong username is identified if query params are present and user doesn't exist (#2838) Co-authored-by: gitstart <gitstart@users.noreply.github.com> Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev> * Meet/Zoom Email Clarification (#2828) * Add clarificaiton to email * Update apps/web/lib/emails/templates/organizer-scheduled-email.ts * Add to attendee scheduled email Co-authored-by: Omar López <zomars@me.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Request the inclusion Assuncion Time Zone (#2840) Co-authored-by: gitstart <gitstart@users.noreply.github.com> Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev> * Fix UI of dialog (#2788) * removed large mandatory height and scroll * added z index using css * cleanup * fixed TS errors * extract dialog out of dropdown * Adds custom loading text to confirmation dialog * rename update * utilizing mutation loading state Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Hotfix: Fixing Security Issues (#2848) * Fixing Privilege Escalation * Fixing critical obj ref in availability * Fixing reschedule security issue Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> * Merge production to main * Update vital.json * Update vital.json * Fix login page CTA disabled state (#2832) * fix(ui/login): better disabled state for login CTA The fix involves tracking the form submission with a dedicated state `submitInProgress` with React. `formState` from `react-hook-form` does not take time taken for the network call into account. For example, if the api takes 5 seconds to complete, we would expect the `formState.isSubmitting` to be true for `5` seconds. But, surprisingly this is not the case and `formState` from `react-hook-form` resolves immediately after it makes a successful connection to the endpoint. A dedicated state (with `useState`) is introduced that is enabled when the user clicks on the login CTA, and disabled when the api call is resolved, either successfully or with an error. * Update login.tsx * Update login.tsx * Fixes isSubmitting state Co-authored-by: zomars <zomars@me.com> * Playwright binaries shouldn't be on deps Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Co-authored-by: Leo Giovanetti <hello@leog.me> Co-authored-by: Alex van Andel <me@alexvanandel.com> Co-authored-by: Bailey Pumfleet <pumfleet@hey.com> Co-authored-by: GitStart <1501599+gitstart@users.noreply.github.com> Co-authored-by: gitstart <gitstart@users.noreply.github.com> Co-authored-by: Arthur Cruz <42497300+arthur1041@users.noreply.github.com> Co-authored-by: Mitchell Moore <47459168+Mitchell-Moore@users.noreply.github.com> Co-authored-by: Agusti Fernandez Pardo <me@agusti.me> Co-authored-by: Agusti Fernandez Pardo <git@agusti.me> Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: andreaestefania12 <19562383+andreaestefania12@users.noreply.github.com> Co-authored-by: Miguel Nieto A <39246879+miguelnietoa@users.noreply.github.com> Co-authored-by: alannnc <alannnc@gmail.com> Co-authored-by: Hashen <37979557+Hashen110@users.noreply.github.com> Co-authored-by: buschco <colin@busch.dev> Co-authored-by: iamkun <kunhello@outlook.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Crowdin Bot <support+bot@crowdin.com> Co-authored-by: Ankit Gordhandas <agordhandas@gmail.com> Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> Co-authored-by: Ken Miller <kemiller@users.noreply.github.com> Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev> Co-authored-by: Arun Kumar <palerdot@users.noreply.github.com>
904 lines
36 KiB
TypeScript
904 lines
36 KiB
TypeScript
import { ArrowLeftIcon, ChevronRightIcon, CodeIcon, EyeIcon, SunIcon } from "@heroicons/react/solid";
|
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
|
|
import classNames from "classnames";
|
|
import { useRouter } from "next/router";
|
|
import { useRef, useState } from "react";
|
|
import { components, ControlProps } from "react-select";
|
|
|
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|
import showToast from "@calcom/lib/notification";
|
|
import { EventType } from "@calcom/prisma/client";
|
|
import { Button, Switch } from "@calcom/ui";
|
|
import { Dialog, DialogClose, DialogContent } from "@calcom/ui/Dialog";
|
|
import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields";
|
|
|
|
import { EMBED_LIB_URL, WEBAPP_URL } from "@lib/config/constants";
|
|
import { trpc } from "@lib/trpc";
|
|
|
|
import NavTabs from "@components/NavTabs";
|
|
import ColorPicker from "@components/ui/colorpicker";
|
|
import Select from "@components/ui/form/Select";
|
|
|
|
type EmbedType = "inline" | "floating-popup" | "element-click";
|
|
const queryParamsForDialog = ["embedType", "tabName", "eventTypeId"];
|
|
|
|
const embeds: {
|
|
illustration: React.ReactElement;
|
|
title: string;
|
|
subtitle: string;
|
|
type: EmbedType;
|
|
}[] = [
|
|
{
|
|
title: "Inline Embed",
|
|
subtitle: "Loads your Cal scheduling page 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="2" fill="#E1E1E1" />
|
|
<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="2" fill="#E1E1E1" />
|
|
<rect x="48" y="86.5" width="48" height="4" rx="1" fill="#E1E1E1" />
|
|
<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="1.5" stroke="#292929" />
|
|
<rect x="176" y="50.5" width="108" height="164" rx="2" fill="#E1E1E1" />
|
|
<rect x="24" y="226.5" width="260" height="38.5" rx="2" fill="#E1E1E1" />
|
|
{/* <path
|
|
d="M2 1H306V-1H2V1ZM307 2V263H309V2H307ZM306 264H2V266H306V264ZM1 263V1.99999H-1V263H1ZM2 264C1.44772 264 1 263.552 1 263H-1C-1 264.657 0.343147 266 2 266V264ZM307 263C307 263.552 306.552 264 306 264V266C307.657 266 309 264.657 309 263H307ZM306 1C306.552 1 307 1.44772 307 2H309C309 0.343145 307.657 -1 306 -1V1ZM2 -1C0.343151 -1 -1 0.343133 -1 1.99999H1C1 1.44771 1.44771 1 2 1V-1Z"
|
|
fill="#CFCFCF"
|
|
/> */}
|
|
</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="2" fill="#E1E1E1" />
|
|
<rect x="24" y="50.5" width="120" height="76" rx="2" fill="#E1E1E1" />
|
|
<rect x="24" y="138.5" width="120" height="76" rx="2" fill="#E1E1E1" />
|
|
<rect x="156" y="50.5" width="128" height="164" rx="2" fill="#E1E1E1" />
|
|
<rect x="24" y="226.5" width="260" height="38.5" rx="2" fill="#E1E1E1" />
|
|
<rect x="226" y="223.5" width="66" height="26" rx="2" fill="#292929" />
|
|
<rect x="242" y="235.5" width="34" height="2" rx="1" fill="white" />
|
|
{/* <path
|
|
d="M2 1H306V-1H2V1ZM307 2V263H309V2H307ZM306 264H2V266H306V264ZM1 263V1.99999H-1V263H1ZM2 264C1.44772 264 1 263.552 1 263H-1C-1 264.657 0.343147 266 2 266V264ZM307 263C307 263.552 306.552 264 306 264V266C307.657 266 309 264.657 309 263H307ZM306 1C306.552 1 307 1.44772 307 2H309C309 0.343145 307.657 -1 306 -1V1ZM2 -1C0.343151 -1 -1 0.343133 -1 1.99999H1C1 1.44771 1.44771 1 2 1V-1Z"
|
|
fill="#CFCFCF"
|
|
/> */}
|
|
</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" width="260" height="38.5" rx="2" fill="#E1E1E1" />
|
|
<rect x="24" y="50.5" width="120" height="76" rx="2" fill="#E1E1E1" />
|
|
<rect x="24" y="138.5" width="120" height="76" rx="2" fill="#E1E1E1" />
|
|
<rect x="156" y="50.5" width="128" height="164" rx="2" fill="#E1E1E1" />
|
|
<rect x="24" y="226.5" width="260" height="38.5" rx="2" fill="#E1E1E1" />
|
|
<rect x="84.5" y="61.5" width="139" height="141" rx="1.5" fill="#F8F8F8" />
|
|
<rect opacity="0.8" x="108" y="85" width="80" height="8" rx="2" fill="#E1E1E1" />
|
|
<rect x="108" y="97" width="48" height="4" rx="1" fill="#E1E1E1" />
|
|
<rect x="109" y="110" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="121" y="110" width="6" height="6" rx="1" fill="#3E3E3E" />
|
|
<rect x="133" y="110" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="145" y="110" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="157" y="110" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="169" y="110" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="181" y="110" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="193" y="110" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="145" y="124" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="157" y="124" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="169" y="124" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="181" y="124" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="193" y="124" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="109" y="136" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="121" y="136" width="6" height="6" rx="1" fill="#3E3E3E" />
|
|
<path
|
|
d="M121 135H127V133H121V135ZM128 136V142H130V136H128ZM127 143H121V145H127V143ZM120 142V136H118V142H120ZM121 143C120.448 143 120 142.552 120 142H118C118 143.657 119.343 145 121 145V143ZM128 142C128 142.552 127.552 143 127 143V145C128.657 145 130 143.657 130 142H128ZM127 135C127.552 135 128 135.448 128 136H130C130 134.343 128.657 133 127 133V135ZM121 133C119.343 133 118 134.343 118 136H120C120 135.448 120.448 135 121 135V133Z"
|
|
fill="#3E3E3E"
|
|
/>
|
|
<rect x="133" y="136" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="145" y="136" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="157" y="136" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="169" y="136" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="181" y="136" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="193" y="136" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="109" y="148" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="121" y="148" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="133" y="148" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="145" y="148" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="157" y="148" width="6" height="6" rx="1" fill="#3E3E3E" />
|
|
<rect x="169" y="148" width="6" height="6" rx="1" fill="#3E3E3E" />
|
|
<rect x="181" y="148" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="193" y="148" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="109" y="160" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="121" y="160" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="133" y="160" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="145" y="160" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="157" y="160" width="6" height="6" rx="1" fill="#3E3E3E" />
|
|
<rect x="169" y="160" width="6" height="6" rx="1" fill="#3E3E3E" />
|
|
<rect x="181" y="160" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="193" y="160" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="109" y="172" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="121" y="172" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="133" y="172" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="145" y="172" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="157" y="172" width="6" height="6" rx="1" fill="#3E3E3E" />
|
|
<rect x="169" y="172" width="6" height="6" rx="1" fill="#C6C6C6" />
|
|
<rect x="84.5" y="61.5" width="139" height="141" rx="1.5" stroke="#292929" />
|
|
{/* <path
|
|
d="M2 1H306V-1H2V1ZM307 2V263H309V2H307ZM306 264H2V266H306V264ZM1 263V1.99999H-1V263H1ZM2 264C1.44772 264 1 263.552 1 263H-1C-1 264.657 0.343147 266 2 266V264ZM307 263C307 263.552 306.552 264 306 264V266C307.657 266 309 264.657 309 263H307ZM306 1C306.552 1 307 1.44772 307 2H309C309 0.343145 307.657 -1 306 -1V1ZM2 -1C0.343151 -1 -1 0.343133 -1 1.99999H1C1 1.44771 1.44771 1 2 1V-1Z"
|
|
fill="#CFCFCF"
|
|
/> */}
|
|
</svg>
|
|
),
|
|
},
|
|
];
|
|
|
|
function getEmbedSnippetString() {
|
|
// TODO: Import this string from @calcom/embed-snippet
|
|
return `
|
|
(function (C, A, L) { let p = function (a, ar) { a.q.push(ar); }; let d = C.document; C.Cal = C.Cal || function () { let cal = C.Cal; let ar = arguments; if (!cal.loaded) { cal.ns = {}; cal.q = cal.q || []; d.head.appendChild(d.createElement("script")).src = A; cal.loaded = true; } if (ar[0] === L) { const api = function () { p(api, arguments); }; const namespace = ar[1]; api.q = api.q || []; typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar); return; } p(cal, ar); }; })(window, "${EMBED_LIB_URL}", "init");
|
|
Cal("init", {origin:"${WEBAPP_URL}"});
|
|
`;
|
|
}
|
|
|
|
const EmbedNavBar = () => {
|
|
const { t } = useLocale();
|
|
const tabs = [
|
|
{
|
|
name: t("Embed"),
|
|
tabName: "embed-code",
|
|
icon: CodeIcon,
|
|
},
|
|
{
|
|
name: t("Preview"),
|
|
tabName: "embed-preview",
|
|
icon: EyeIcon,
|
|
},
|
|
];
|
|
|
|
return <NavTabs data-testid="embed-tabs" tabs={tabs} linkProps={{ shallow: true }} />;
|
|
};
|
|
const ThemeSelectControl = ({
|
|
children,
|
|
...props
|
|
}: ControlProps<{ value: string; label: string }, false>) => {
|
|
return (
|
|
<components.Control {...props}>
|
|
<SunIcon className="h-[32px] w-[32px] text-gray-500" />
|
|
{children}
|
|
</components.Control>
|
|
);
|
|
};
|
|
|
|
const ChooseEmbedTypesDialogContent = () => {
|
|
const { t } = useLocale();
|
|
const router = useRouter();
|
|
return (
|
|
<DialogContent size="lg">
|
|
<div className="mb-4">
|
|
<h3 className="text-lg font-bold leading-6 text-gray-900" id="modal-title">
|
|
{t("how_you_want_add_cal_site")}
|
|
</h3>
|
|
<div>
|
|
<p className="text-sm text-gray-500">{t("choose_ways_put_cal_site")}</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex">
|
|
{embeds.map((embed, index) => (
|
|
<button
|
|
className="mr-2 w-1/3 p-3 text-left hover:rounded-md hover:border hover:bg-neutral-100"
|
|
key={index}
|
|
data-testid={embed.type}
|
|
onClick={() => {
|
|
router.push({
|
|
query: {
|
|
...router.query,
|
|
embedType: embed.type,
|
|
},
|
|
});
|
|
}}>
|
|
<div className="order-none box-border flex-none rounded-sm border border-solid bg-white">
|
|
{embed.illustration}
|
|
</div>
|
|
<div className="mt-2 font-medium text-neutral-900">{embed.title}</div>
|
|
<p className="text-sm text-gray-500">{embed.subtitle}</p>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</DialogContent>
|
|
);
|
|
};
|
|
|
|
const EmbedTypeCodeAndPreviewDialogContent = ({
|
|
eventTypeId,
|
|
embedType,
|
|
}: {
|
|
eventTypeId: EventType["id"];
|
|
embedType: EmbedType;
|
|
}) => {
|
|
const { t } = useLocale();
|
|
const router = useRouter();
|
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
const embedCode = useRef<HTMLTextAreaElement>(null);
|
|
const embed = embeds.find((embed) => embed.type === embedType);
|
|
|
|
const { data: eventType, isLoading } = trpc.useQuery([
|
|
"viewer.eventTypes.get",
|
|
{
|
|
id: +eventTypeId,
|
|
},
|
|
]);
|
|
|
|
const [isEmbedCustomizationOpen, setIsEmbedCustomizationOpen] = useState(true);
|
|
const [isBookingCustomizationOpen, setIsBookingCustomizationOpen] = useState(true);
|
|
const [previewState, setPreviewState] = useState({
|
|
inline: {
|
|
width: "100%",
|
|
height: "100%",
|
|
},
|
|
theme: "auto",
|
|
floatingPopup: {},
|
|
elementClick: {},
|
|
palette: {
|
|
brandColor: "#000000",
|
|
},
|
|
});
|
|
|
|
const close = () => {
|
|
const noPopupQuery = {
|
|
...router.query,
|
|
};
|
|
|
|
delete noPopupQuery.dialog;
|
|
|
|
queryParamsForDialog.forEach((queryParam) => {
|
|
delete noPopupQuery[queryParam];
|
|
});
|
|
|
|
router.push({
|
|
query: noPopupQuery,
|
|
});
|
|
};
|
|
|
|
// Use embed-code as default tab
|
|
if (!router.query.tabName) {
|
|
router.query.tabName = "embed-code";
|
|
router.push({
|
|
query: {
|
|
...router.query,
|
|
},
|
|
});
|
|
}
|
|
|
|
if (isLoading) {
|
|
return null;
|
|
}
|
|
|
|
if (!embed || !eventType) {
|
|
close();
|
|
return null;
|
|
}
|
|
|
|
const calLink = `${eventType.team ? `team/${eventType.team.slug}` : eventType.users[0].username}/${
|
|
eventType.slug
|
|
}`;
|
|
|
|
// TODO: Not sure how to make these template strings look better formatted.
|
|
// This exact formatting is required to make the code look nicely formatted together.
|
|
const getEmbedUIInstructionString = () =>
|
|
`Cal("ui", {
|
|
${getThemeForSnippet() ? 'theme: "' + previewState.theme + '",\n ' : ""}styles: {
|
|
branding: ${JSON.stringify(previewState.palette)}
|
|
}
|
|
})`;
|
|
|
|
const getEmbedTypeSpecificString = () => {
|
|
if (embedType === "inline") {
|
|
return `
|
|
Cal("inline", {
|
|
elementOrSelector:"#my-cal-inline",
|
|
calLink: "${calLink}"
|
|
});
|
|
${getEmbedUIInstructionString().trim()}`;
|
|
} else if (embedType === "floating-popup") {
|
|
const floatingButtonArg = {
|
|
calLink,
|
|
...previewState.floatingPopup,
|
|
};
|
|
return `
|
|
Cal("floatingButton", ${JSON.stringify(floatingButtonArg)});
|
|
${getEmbedUIInstructionString().trim()}`;
|
|
} else if (embedType === "element-click") {
|
|
return `//Important: Also, add data-cal-link="${calLink}" attribute to the element you want to open Cal on click
|
|
${getEmbedUIInstructionString().trim()}`;
|
|
}
|
|
return "";
|
|
};
|
|
|
|
const getThemeForSnippet = () => {
|
|
return previewState.theme !== "auto" ? previewState.theme : null;
|
|
};
|
|
|
|
const getDimension = (dimension: string) => {
|
|
if (dimension.match(/^\d+$/)) {
|
|
dimension = `${dimension}%`;
|
|
}
|
|
return dimension;
|
|
};
|
|
|
|
const addToPalette = (update: typeof previewState["palette"]) => {
|
|
setPreviewState((previewState) => {
|
|
return {
|
|
...previewState,
|
|
palette: {
|
|
...previewState.palette,
|
|
...update,
|
|
},
|
|
};
|
|
});
|
|
};
|
|
|
|
const previewInstruction = (instruction: { name: string; arg: unknown }) => {
|
|
iframeRef.current?.contentWindow?.postMessage(
|
|
{
|
|
mode: "cal:preview",
|
|
type: "instruction",
|
|
instruction,
|
|
},
|
|
"*"
|
|
);
|
|
};
|
|
|
|
const inlineEmbedDimensionUpdate = ({ width, height }: { width: string; height: string }) => {
|
|
iframeRef.current?.contentWindow?.postMessage(
|
|
{
|
|
mode: "cal:preview",
|
|
type: "inlineEmbedDimensionUpdate",
|
|
data: {
|
|
width: getDimension(width),
|
|
height: getDimension(height),
|
|
},
|
|
},
|
|
"*"
|
|
);
|
|
};
|
|
|
|
previewInstruction({
|
|
name: "ui",
|
|
arg: {
|
|
theme: previewState.theme,
|
|
styles: {
|
|
branding: {
|
|
...previewState.palette,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (embedType === "floating-popup") {
|
|
previewInstruction({
|
|
name: "floatingButton",
|
|
arg: {
|
|
attributes: {
|
|
id: "my-floating-button",
|
|
},
|
|
...previewState.floatingPopup,
|
|
},
|
|
});
|
|
}
|
|
|
|
if (embedType === "inline") {
|
|
inlineEmbedDimensionUpdate({
|
|
width: previewState.inline.width,
|
|
height: previewState.inline.height,
|
|
});
|
|
}
|
|
|
|
const ThemeOptions = [
|
|
{ value: "auto", label: "Auto Theme" },
|
|
{ value: "dark", label: "Dark Theme" },
|
|
{ value: "light", label: "Light Theme" },
|
|
];
|
|
|
|
const FloatingPopupPositionOptions = [
|
|
{
|
|
value: "bottom-right",
|
|
label: "Bottom Right",
|
|
},
|
|
{
|
|
value: "bottom-left",
|
|
label: "Bottom Left",
|
|
},
|
|
];
|
|
|
|
return (
|
|
<DialogContent size="xl">
|
|
<div className="flex">
|
|
<div className="flex w-1/3 flex-col bg-white p-6">
|
|
<h3 className="mb-2 flex text-xl font-bold leading-6 text-gray-900" id="modal-title">
|
|
<button
|
|
onClick={() => {
|
|
const newQuery = { ...router.query };
|
|
delete newQuery.embedType;
|
|
delete newQuery.tabName;
|
|
router.push({
|
|
query: {
|
|
...newQuery,
|
|
},
|
|
});
|
|
}}>
|
|
<ArrowLeftIcon className="mr-4 w-4"></ArrowLeftIcon>
|
|
</button>
|
|
{embed.title}
|
|
</h3>
|
|
<hr className={classNames("mt-4", embedType === "element-click" ? "hidden" : "")}></hr>
|
|
<div className={classNames("mt-4 font-medium", embedType === "element-click" ? "hidden" : "")}>
|
|
<Collapsible
|
|
open={isEmbedCustomizationOpen}
|
|
onOpenChange={() => setIsEmbedCustomizationOpen((val) => !val)}>
|
|
<CollapsibleTrigger
|
|
type="button"
|
|
className="flex w-full items-center text-base font-medium text-neutral-900">
|
|
<div>
|
|
{embedType === "inline"
|
|
? "Inline Embed Customization"
|
|
: embedType === "floating-popup"
|
|
? "Floating Popup Customization"
|
|
: "Element Click Customization"}
|
|
</div>
|
|
<ChevronRightIcon
|
|
className={`${
|
|
isEmbedCustomizationOpen ? "rotate-90 transform" : ""
|
|
} ml-auto h-5 w-5 text-neutral-500`}
|
|
/>
|
|
</CollapsibleTrigger>
|
|
<CollapsibleContent className="text-sm">
|
|
<div className={classNames("mt-6", embedType === "inline" ? "block" : "hidden")}>
|
|
{/*TODO: Add Auto/Fixed toggle from Figma */}
|
|
<div className="text-sm">Embed Window Sizing</div>
|
|
<div className="justify-left flex items-center">
|
|
<TextField
|
|
name="width"
|
|
labelProps={{ className: "hidden" }}
|
|
required
|
|
value={previewState.inline.width}
|
|
onChange={(e) => {
|
|
setPreviewState((previewState) => {
|
|
const width = e.target.value || "100%";
|
|
|
|
return {
|
|
...previewState,
|
|
inline: {
|
|
...previewState.inline,
|
|
width,
|
|
},
|
|
};
|
|
});
|
|
}}
|
|
addOnLeading={<InputLeading>W</InputLeading>}
|
|
/>
|
|
<span className="p-2">x</span>
|
|
<TextField
|
|
labelProps={{ className: "hidden" }}
|
|
name="height"
|
|
value={previewState.inline.height}
|
|
required
|
|
onChange={(e) => {
|
|
const height = e.target.value || "100%";
|
|
|
|
setPreviewState((previewState) => {
|
|
return {
|
|
...previewState,
|
|
inline: {
|
|
...previewState.inline,
|
|
height,
|
|
},
|
|
};
|
|
});
|
|
}}
|
|
addOnLeading={<InputLeading>H</InputLeading>}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div
|
|
className={classNames(
|
|
"mt-4 items-center justify-between",
|
|
embedType === "floating-popup" ? "flex" : "hidden"
|
|
)}>
|
|
<div className="text-sm">Button Text</div>
|
|
{/* Default Values should come from preview iframe */}
|
|
<TextField
|
|
name="buttonText"
|
|
labelProps={{ className: "hidden" }}
|
|
onChange={(e) => {
|
|
setPreviewState((previewState) => {
|
|
return {
|
|
...previewState,
|
|
floatingPopup: {
|
|
...previewState.floatingPopup,
|
|
buttonText: e.target.value,
|
|
},
|
|
};
|
|
});
|
|
}}
|
|
defaultValue="Book my Cal"
|
|
required
|
|
/>
|
|
</div>
|
|
<div
|
|
className={classNames(
|
|
"mt-4 flex items-center justify-between",
|
|
embedType === "floating-popup" ? "flex" : "hidden"
|
|
)}>
|
|
<div className="text-sm">Display Calendar Icon Button</div>
|
|
<Switch
|
|
defaultChecked={true}
|
|
onCheckedChange={(checked) => {
|
|
setPreviewState((previewState) => {
|
|
return {
|
|
...previewState,
|
|
floatingPopup: {
|
|
...previewState.floatingPopup,
|
|
hideButtonIcon: !checked,
|
|
},
|
|
};
|
|
});
|
|
}}></Switch>
|
|
</div>
|
|
<div
|
|
className={classNames(
|
|
"mt-4 flex items-center justify-between",
|
|
embedType === "floating-popup" ? "flex" : "hidden"
|
|
)}>
|
|
<div>Position of Button</div>
|
|
<Select
|
|
onChange={(position) => {
|
|
setPreviewState((previewState) => {
|
|
return {
|
|
...previewState,
|
|
floatingPopup: {
|
|
...previewState.floatingPopup,
|
|
buttonPosition: position?.value,
|
|
},
|
|
};
|
|
});
|
|
}}
|
|
defaultValue={FloatingPopupPositionOptions[0]}
|
|
options={FloatingPopupPositionOptions}></Select>
|
|
</div>
|
|
<div
|
|
className={classNames(
|
|
"mt-4 flex items-center justify-between",
|
|
embedType === "floating-popup" ? "flex" : "hidden"
|
|
)}>
|
|
<div>Button Color</div>
|
|
<div className="w-36">
|
|
<ColorPicker
|
|
defaultValue="#000000"
|
|
onChange={(color) => {
|
|
setPreviewState((previewState) => {
|
|
return {
|
|
...previewState,
|
|
floatingPopup: {
|
|
...previewState.floatingPopup,
|
|
buttonColor: color,
|
|
},
|
|
};
|
|
});
|
|
}}></ColorPicker>
|
|
</div>
|
|
</div>
|
|
<div
|
|
className={classNames(
|
|
"mt-4 flex items-center justify-between",
|
|
embedType === "floating-popup" ? "flex" : "hidden"
|
|
)}>
|
|
<div>Text Color</div>
|
|
<div className="w-36">
|
|
<ColorPicker
|
|
defaultValue="#000000"
|
|
onChange={(color) => {
|
|
setPreviewState((previewState) => {
|
|
return {
|
|
...previewState,
|
|
floatingPopup: {
|
|
...previewState.floatingPopup,
|
|
buttonTextColor: color,
|
|
},
|
|
};
|
|
});
|
|
}}></ColorPicker>
|
|
</div>
|
|
</div>
|
|
{/* <div
|
|
className={classNames(
|
|
"mt-4 items-center justify-between",
|
|
embedType === "floating-popup" ? "flex" : "hidden"
|
|
)}>
|
|
<div>Button Color on Hover</div>
|
|
<div className="w-36">
|
|
<ColorPicker
|
|
defaultValue="#000000"
|
|
onChange={(color) => {
|
|
addToPalette({
|
|
"floating-popup-button-color-hover": color,
|
|
});
|
|
}}></ColorPicker>
|
|
</div>
|
|
</div> */}
|
|
</CollapsibleContent>
|
|
</Collapsible>
|
|
</div>
|
|
<hr className="mt-4"></hr>
|
|
<div className="mt-4 font-medium">
|
|
<Collapsible
|
|
open={isBookingCustomizationOpen}
|
|
onOpenChange={() => setIsBookingCustomizationOpen((val) => !val)}>
|
|
<CollapsibleTrigger className="flex w-full" type="button">
|
|
<div className="text-base font-medium text-neutral-900">Cal Booking Customization</div>
|
|
<ChevronRightIcon
|
|
className={`${
|
|
isBookingCustomizationOpen ? "rotate-90 transform" : ""
|
|
} ml-auto h-5 w-5 text-neutral-500`}
|
|
/>
|
|
</CollapsibleTrigger>
|
|
<CollapsibleContent>
|
|
<div className="mt-6 text-sm">
|
|
<Label className="flex items-center justify-between">
|
|
<div>Theme</div>
|
|
<Select
|
|
className="w-36"
|
|
defaultValue={ThemeOptions[0]}
|
|
components={{
|
|
Control: ThemeSelectControl,
|
|
}}
|
|
onChange={(option) => {
|
|
if (!option) {
|
|
return;
|
|
}
|
|
setPreviewState((previewState) => {
|
|
return {
|
|
...previewState,
|
|
theme: option.value,
|
|
};
|
|
});
|
|
}}
|
|
options={ThemeOptions}></Select>
|
|
</Label>
|
|
{[
|
|
{ name: "brandColor", title: "Brand Color" },
|
|
// { name: "lightColor", title: "Light Color" },
|
|
// { name: "lighterColor", title: "Lighter Color" },
|
|
// { name: "lightestColor", title: "Lightest Color" },
|
|
// { name: "highlightColor", title: "Highlight Color" },
|
|
// { name: "medianColor", title: "Median Color" },
|
|
].map((palette) => (
|
|
<Label key={palette.name} className="flex items-center justify-between">
|
|
<div>{palette.title}</div>
|
|
<div className="w-36">
|
|
<ColorPicker
|
|
defaultValue="#000000"
|
|
onChange={(color) => {
|
|
addToPalette({
|
|
[palette.name as keyof typeof previewState["palette"]]: color,
|
|
});
|
|
}}
|
|
/>
|
|
</div>
|
|
</Label>
|
|
))}
|
|
</div>
|
|
</CollapsibleContent>
|
|
</Collapsible>
|
|
</div>
|
|
</div>
|
|
<div className="w-2/3 bg-gray-50 p-6">
|
|
<EmbedNavBar />
|
|
<div>
|
|
<div
|
|
className={classNames(router.query.tabName === "embed-code" ? "block" : "hidden", "h-[75vh]")}>
|
|
<small className="flex py-4 text-neutral-500">{t("place_where_cal_widget_appear")}</small>
|
|
<TextArea
|
|
data-testid="embed-code"
|
|
ref={embedCode}
|
|
name="embed-code"
|
|
className="h-[36rem]"
|
|
readOnly
|
|
value={
|
|
`<!-- Cal ${embedType} embed code begins -->\n` +
|
|
(embedType === "inline"
|
|
? `<div style="width:${getDimension(previewState.inline.width)};height:${getDimension(
|
|
previewState.inline.height
|
|
)};overflow:scroll" id="my-cal-inline"></div>\n`
|
|
: "") +
|
|
`<script type="text/javascript">
|
|
${getEmbedSnippetString().trim()}
|
|
${getEmbedTypeSpecificString().trim()}
|
|
</script>
|
|
<!-- Cal ${embedType} embed code ends -->`
|
|
}></TextArea>
|
|
<p className="hidden text-sm text-gray-500">
|
|
{t(
|
|
"Need help? See our guides for embedding Cal on Wix, Squarespace, or WordPress, check our common questions, or explore advanced embed options."
|
|
)}
|
|
</p>
|
|
</div>
|
|
<div className={router.query.tabName == "embed-preview" ? "block" : "hidden"}>
|
|
<iframe
|
|
ref={iframeRef}
|
|
data-testid="embed-preview"
|
|
className="border-1 h-[75vh] border"
|
|
width="100%"
|
|
height="100%"
|
|
src={`${WEBAPP_URL}/embed/preview.html?embedType=${embedType}&calLink=${calLink}`}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="mt-8 flex flex-row-reverse gap-x-2">
|
|
<Button
|
|
type="submit"
|
|
onClick={() => {
|
|
if (!embedCode.current) {
|
|
return;
|
|
}
|
|
navigator.clipboard.writeText(embedCode.current.value);
|
|
showToast(t("code_copied"), "success");
|
|
}}>
|
|
{t("copy_code")}
|
|
</Button>
|
|
<DialogClose asChild>
|
|
<Button color="secondary">{t("Close")}</Button>
|
|
</DialogClose>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
);
|
|
};
|
|
|
|
export const EmbedDialog = () => {
|
|
const router = useRouter();
|
|
const eventTypeId: EventType["id"] = +(router.query.eventTypeId as string);
|
|
return (
|
|
<Dialog name="embed" clearQueryParamsOnClose={queryParamsForDialog}>
|
|
{!router.query.embedType ? (
|
|
<ChooseEmbedTypesDialogContent />
|
|
) : (
|
|
<EmbedTypeCodeAndPreviewDialogContent
|
|
eventTypeId={eventTypeId}
|
|
embedType={router.query.embedType as EmbedType}
|
|
/>
|
|
)}
|
|
</Dialog>
|
|
);
|
|
};
|
|
|
|
export const EmbedButton = ({
|
|
eventTypeId,
|
|
className = "",
|
|
dark,
|
|
...props
|
|
}: {
|
|
eventTypeId: EventType["id"];
|
|
className: string;
|
|
dark?: boolean;
|
|
}) => {
|
|
const { t } = useLocale();
|
|
const router = useRouter();
|
|
className = classNames(className, "hidden lg:flex");
|
|
const openEmbedModal = () => {
|
|
const query = {
|
|
...router.query,
|
|
dialog: "embed",
|
|
eventTypeId,
|
|
};
|
|
router.push(
|
|
{
|
|
pathname: router.pathname,
|
|
query,
|
|
},
|
|
undefined,
|
|
{ shallow: true }
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Button
|
|
type="button"
|
|
color="minimal"
|
|
size="sm"
|
|
className={className}
|
|
{...props}
|
|
data-test-eventtype-id={eventTypeId}
|
|
data-testid={"event-type-embed"}
|
|
onClick={() => openEmbedModal()}>
|
|
<CodeIcon
|
|
className={classNames("h-4 w-4 ltr:mr-2 rtl:ml-2", dark ? "" : "text-neutral-500")}></CodeIcon>
|
|
{t("Embed")}
|
|
</Button>
|
|
);
|
|
};
|