+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>
760 lines
23 KiB
TypeScript
760 lines
23 KiB
TypeScript
import { ArrowRightIcon } from "@heroicons/react/outline";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { Prisma } from "@prisma/client";
|
|
import classnames from "classnames";
|
|
import dayjs from "dayjs";
|
|
import localizedFormat from "dayjs/plugin/localizedFormat";
|
|
import timezone from "dayjs/plugin/timezone";
|
|
import utc from "dayjs/plugin/utc";
|
|
import debounce from "lodash/debounce";
|
|
import omit from "lodash/omit";
|
|
import { NextPageContext } from "next";
|
|
import { useSession } from "next-auth/react";
|
|
import Head from "next/head";
|
|
import { useRouter } from "next/router";
|
|
import React, { useEffect, useRef, useState, useCallback } from "react";
|
|
import { useForm } from "react-hook-form";
|
|
import * as z from "zod";
|
|
|
|
import getApps from "@calcom/app-store/utils";
|
|
import { getCalendarCredentials, getConnectedCalendars } from "@calcom/core/CalendarManager";
|
|
import { ResponseUsernameApi } from "@calcom/ee/lib/core/checkPremiumUsername";
|
|
import { Alert } from "@calcom/ui/Alert";
|
|
import Button from "@calcom/ui/Button";
|
|
import { Form } from "@calcom/ui/form/fields";
|
|
|
|
import { getSession } from "@lib/auth";
|
|
import { DEFAULT_SCHEDULE } from "@lib/availability";
|
|
import { useLocale } from "@lib/hooks/useLocale";
|
|
import prisma from "@lib/prisma";
|
|
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
|
|
import { trpc } from "@lib/trpc";
|
|
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
|
import { Schedule as ScheduleType } from "@lib/types/schedule";
|
|
|
|
import { ClientSuspense } from "@components/ClientSuspense";
|
|
import Loader from "@components/Loader";
|
|
import Schedule from "@components/availability/Schedule";
|
|
import { CalendarListContainer } from "@components/integrations/CalendarListContainer";
|
|
import Text from "@components/ui/Text";
|
|
import TimezoneSelect from "@components/ui/form/TimezoneSelect";
|
|
|
|
import getEventTypes from "../lib/queries/event-types/get-event-types";
|
|
|
|
dayjs.extend(utc);
|
|
dayjs.extend(timezone);
|
|
dayjs.extend(localizedFormat);
|
|
|
|
type ScheduleFormValues = {
|
|
schedule: ScheduleType;
|
|
};
|
|
|
|
let mutationComplete: ((err: Error | null) => void) | null;
|
|
|
|
export default function Onboarding(props: inferSSRProps<typeof getServerSideProps>) {
|
|
const { t } = useLocale();
|
|
const router = useRouter();
|
|
const telemetry = useTelemetry();
|
|
|
|
const mutation = trpc.useMutation("viewer.updateProfile", {
|
|
onSuccess: async () => {
|
|
setSubmitting(true);
|
|
setEnteredName(nameRef.current?.value || "");
|
|
if (mutationComplete) {
|
|
mutationComplete(null);
|
|
mutationComplete = null;
|
|
}
|
|
setSubmitting(false);
|
|
},
|
|
onError: (err) => {
|
|
setError(new Error(err.message));
|
|
if (mutationComplete) {
|
|
mutationComplete(new Error(err.message));
|
|
}
|
|
setSubmitting(false);
|
|
},
|
|
});
|
|
|
|
const DEFAULT_EVENT_TYPES = [
|
|
{
|
|
title: t("15min_meeting"),
|
|
slug: "15min",
|
|
length: 15,
|
|
},
|
|
{
|
|
title: t("30min_meeting"),
|
|
slug: "30min",
|
|
length: 30,
|
|
},
|
|
{
|
|
title: t("secret_meeting"),
|
|
slug: "secret",
|
|
length: 15,
|
|
hidden: true,
|
|
},
|
|
];
|
|
|
|
const [isSubmitting, setSubmitting] = React.useState(false);
|
|
const [enteredName, setEnteredName] = React.useState("");
|
|
const { status } = useSession();
|
|
const loading = status === "loading";
|
|
const [ready, setReady] = useState(false);
|
|
const [selectedImport, setSelectedImport] = useState("");
|
|
const [error, setError] = useState<Error | null>(null);
|
|
|
|
const updateUser = useCallback(
|
|
async (data: Prisma.UserUpdateInput) => {
|
|
const res = await fetch(`/api/user/${props.user.id}`, {
|
|
method: "PATCH",
|
|
body: JSON.stringify({ data: { ...data } }),
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error((await res.json()).message);
|
|
}
|
|
const responseData = await res.json();
|
|
return responseData.data;
|
|
},
|
|
[props.user.id]
|
|
);
|
|
|
|
const createEventType = async (data: Prisma.EventTypeCreateInput) => {
|
|
const res = await fetch(`/api/availability/eventtype`, {
|
|
method: "POST",
|
|
body: JSON.stringify(data),
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error((await res.json()).message);
|
|
}
|
|
const responseData = await res.json();
|
|
return responseData.data;
|
|
};
|
|
|
|
const createSchedule = trpc.useMutation("viewer.availability.schedule.create", {
|
|
onError: (err) => {
|
|
throw new Error(err.message);
|
|
},
|
|
});
|
|
|
|
/** Name */
|
|
const nameRef = useRef<HTMLInputElement>(null);
|
|
const bioRef = useRef<HTMLInputElement>(null);
|
|
/** End Name */
|
|
/** TimeZone */
|
|
const [selectedTimeZone, setSelectedTimeZone] = useState(props.user.timeZone ?? dayjs.tz.guess());
|
|
/** End TimeZone */
|
|
|
|
/** Onboarding Steps */
|
|
const [currentStep, setCurrentStep] = useState(0);
|
|
const detectStep = () => {
|
|
// Always set timezone if new user
|
|
let step = 0;
|
|
|
|
const hasConfigureCalendar = props.integrations.some((integration) => integration.credential !== null);
|
|
if (hasConfigureCalendar) {
|
|
step = 2;
|
|
}
|
|
|
|
const hasSchedules = props.schedules && props.schedules.length > 0;
|
|
if (hasSchedules) {
|
|
step = 3;
|
|
}
|
|
|
|
setCurrentStep(step);
|
|
};
|
|
|
|
const handleConfirmStep = async () => {
|
|
try {
|
|
setSubmitting(true);
|
|
if (
|
|
steps[currentStep] &&
|
|
steps[currentStep].onComplete &&
|
|
typeof steps[currentStep].onComplete === "function"
|
|
) {
|
|
await steps[currentStep].onComplete!();
|
|
}
|
|
incrementStep();
|
|
setSubmitting(false);
|
|
} catch (error) {
|
|
setSubmitting(false);
|
|
setError(error as Error);
|
|
}
|
|
};
|
|
|
|
const debouncedHandleConfirmStep = debounce(handleConfirmStep, 850);
|
|
|
|
const handleSkipStep = () => {
|
|
incrementStep();
|
|
};
|
|
|
|
const incrementStep = () => {
|
|
const nextStep = currentStep + 1;
|
|
|
|
if (nextStep >= steps.length) {
|
|
completeOnboarding();
|
|
return;
|
|
}
|
|
setCurrentStep(nextStep);
|
|
};
|
|
|
|
const decrementStep = () => {
|
|
const previous = currentStep - 1;
|
|
|
|
if (previous < 0) {
|
|
return;
|
|
}
|
|
setCurrentStep(previous);
|
|
};
|
|
|
|
const goToStep = (step: number) => {
|
|
setCurrentStep(step);
|
|
};
|
|
|
|
/**
|
|
* Complete Onboarding finalizes the onboarding flow for a new user.
|
|
*
|
|
* Here, 3 event types are pre-created for the user as well.
|
|
* Set to the availability the user enter during the onboarding.
|
|
*
|
|
* If a user skips through the Onboarding flow,
|
|
* then the default availability is applied.
|
|
*/
|
|
const completeOnboarding = async () => {
|
|
setSubmitting(true);
|
|
if (!props.eventTypes || props.eventTypes.length === 0) {
|
|
const eventTypes = await getEventTypes();
|
|
if (eventTypes.length === 0) {
|
|
await Promise.all(
|
|
DEFAULT_EVENT_TYPES.map(async (event) => {
|
|
return await createEventType(event);
|
|
})
|
|
);
|
|
}
|
|
}
|
|
await updateUser({
|
|
completedOnboarding: true,
|
|
});
|
|
|
|
setSubmitting(false);
|
|
router.push("/event-types");
|
|
};
|
|
|
|
const schema = z.object({
|
|
token: z.string(),
|
|
});
|
|
|
|
const formMethods = useForm<{
|
|
token: string;
|
|
}>({ resolver: zodResolver(schema), mode: "onSubmit" });
|
|
|
|
const fetchUsername = async (username: string) => {
|
|
const response = await fetch(`${process.env.NEXT_PUBLIC_WEBSITE_URL}/api/username`, {
|
|
credentials: "include",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ username: username.trim() }),
|
|
method: "POST",
|
|
mode: "cors",
|
|
});
|
|
const data = (await response.json()) as ResponseUsernameApi;
|
|
return { response, data };
|
|
};
|
|
|
|
// Should update username on user when being redirected from sign up and doing google/saml
|
|
useEffect(() => {
|
|
async function validateAndSave(username: string) {
|
|
const { data } = await fetchUsername(username);
|
|
|
|
// Only persist username if its available and not premium
|
|
// premium usernames are saved via stripe webhook
|
|
if (data.available && !data.premium) {
|
|
await updateUser({
|
|
username,
|
|
});
|
|
}
|
|
// Remove it from localStorage
|
|
window.localStorage.removeItem("username");
|
|
return;
|
|
}
|
|
|
|
// Looking for username on localStorage
|
|
const username = window.localStorage.getItem("username");
|
|
if (username) {
|
|
validateAndSave(username);
|
|
}
|
|
}, [updateUser]);
|
|
|
|
const availabilityForm = useForm({ defaultValues: { schedule: DEFAULT_SCHEDULE } });
|
|
const steps = [
|
|
{
|
|
id: t("welcome"),
|
|
title: t("welcome_to_calcom"),
|
|
description: t("welcome_instructions"),
|
|
Component: (
|
|
<>
|
|
{selectedImport == "" && (
|
|
<div className="mb-4 grid grid-cols-2 gap-x-4">
|
|
<Button color="secondary" onClick={() => setSelectedImport("calendly")}>
|
|
{t("import_from")} Calendly
|
|
</Button>
|
|
<Button color="secondary" onClick={() => setSelectedImport("savvycal")}>
|
|
{t("import_from")} SavvyCal
|
|
</Button>
|
|
</div>
|
|
)}
|
|
{selectedImport && (
|
|
<div>
|
|
<h2 className="font-cal text-2xl text-gray-900">
|
|
{t("import_from")} {selectedImport === "calendly" ? "Calendly" : "SavvyCal"}
|
|
</h2>
|
|
<p className="mb-2 text-sm text-gray-500">
|
|
{t("you_will_need_to_generate")}. Find out how to do this{" "}
|
|
<a href="https://docs.cal.com/import">here</a>.
|
|
</p>
|
|
<form
|
|
className="flex"
|
|
onSubmit={formMethods.handleSubmit(async (values) => {
|
|
// track the number of imports. Without personal data/payload
|
|
telemetry.withJitsu((jitsu) =>
|
|
jitsu.track(telemetryEventTypes.importSubmitted, {
|
|
...collectPageParameters(),
|
|
selectedImport,
|
|
})
|
|
);
|
|
setSubmitting(true);
|
|
const response = await fetch(`/api/import/${selectedImport}`, {
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
token: values.token,
|
|
}),
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
if (response.status === 201) {
|
|
setSubmitting(false);
|
|
handleSkipStep();
|
|
} else {
|
|
await response.json().catch((e) => {
|
|
console.log("Error: response.json invalid: " + e);
|
|
setSubmitting(false);
|
|
});
|
|
}
|
|
})}>
|
|
<input
|
|
onChange={async (e) => {
|
|
formMethods.setValue("token", e.target.value);
|
|
}}
|
|
type="text"
|
|
name="token"
|
|
id="token"
|
|
placeholder={t("access_token")}
|
|
required
|
|
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 shadow-sm focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
|
|
/>
|
|
<Button type="submit" className="mt-1 ml-4 h-10">
|
|
{t("import")}
|
|
</Button>
|
|
</form>
|
|
</div>
|
|
)}
|
|
<div className="relative my-4">
|
|
<div className="absolute inset-0 flex items-center" aria-hidden="true">
|
|
<div className="w-full border-t border-gray-300" />
|
|
</div>
|
|
<div className="relative flex justify-center">
|
|
<span className="bg-white px-2 text-sm text-gray-500">or</span>
|
|
</div>
|
|
</div>
|
|
<form className="sm:mx-auto sm:w-full">
|
|
<section className="space-y-8">
|
|
<fieldset>
|
|
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
|
|
{t("full_name")}
|
|
</label>
|
|
<input
|
|
ref={nameRef}
|
|
type="text"
|
|
name="name"
|
|
id="name"
|
|
autoComplete="given-name"
|
|
placeholder={t("your_name")}
|
|
defaultValue={props.user.name ?? enteredName}
|
|
required
|
|
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 shadow-sm focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
|
|
/>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<section className="flex justify-between">
|
|
<label htmlFor="timeZone" className="block text-sm font-medium text-gray-700">
|
|
{t("timezone")}
|
|
</label>
|
|
<Text variant="caption">
|
|
{t("current_time")}:
|
|
<span className="text-black">{dayjs().tz(selectedTimeZone).format("LT")}</span>
|
|
</Text>
|
|
</section>
|
|
<TimezoneSelect
|
|
id="timeZone"
|
|
value={selectedTimeZone}
|
|
onChange={({ value }) => setSelectedTimeZone(value)}
|
|
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"
|
|
/>
|
|
</fieldset>
|
|
</section>
|
|
</form>
|
|
</>
|
|
),
|
|
hideConfirm: false,
|
|
confirmText: t("continue"),
|
|
showCancel: true,
|
|
cancelText: t("set_up_later"),
|
|
onComplete: async () => {
|
|
mutationComplete = null;
|
|
setError(null);
|
|
const mutationAsync = new Promise((resolve, reject) => {
|
|
mutationComplete = (err) => {
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
resolve(null);
|
|
};
|
|
});
|
|
|
|
const userUpdateData = {
|
|
name: nameRef.current?.value,
|
|
timeZone: selectedTimeZone,
|
|
};
|
|
|
|
mutation.mutate(userUpdateData);
|
|
|
|
if (mutationComplete) {
|
|
await mutationAsync;
|
|
}
|
|
},
|
|
},
|
|
{
|
|
id: "connect-calendar",
|
|
title: t("connect_your_calendar"),
|
|
description: t("connect_your_calendar_instructions"),
|
|
Component: (
|
|
<ClientSuspense fallback={<Loader />}>
|
|
<CalendarListContainer heading={false} />
|
|
</ClientSuspense>
|
|
),
|
|
hideConfirm: true,
|
|
confirmText: t("continue"),
|
|
showCancel: true,
|
|
cancelText: t("continue_without_calendar"),
|
|
},
|
|
{
|
|
id: "set-availability",
|
|
title: t("set_availability"),
|
|
description: t("set_availability_instructions"),
|
|
Component: (
|
|
<Form<ScheduleFormValues>
|
|
className="mx-auto max-w-lg bg-white text-black dark:bg-opacity-5 dark:text-white"
|
|
form={availabilityForm}
|
|
handleSubmit={async (values) => {
|
|
try {
|
|
setSubmitting(true);
|
|
await createSchedule.mutate({
|
|
name: t("default_schedule_name"),
|
|
...values,
|
|
});
|
|
debouncedHandleConfirmStep();
|
|
setSubmitting(false);
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
setError(error);
|
|
}
|
|
}
|
|
}}>
|
|
<section>
|
|
<Schedule name="schedule" />
|
|
<footer className="flex flex-col space-y-6 py-6 sm:mx-auto sm:w-full">
|
|
<Button className="justify-center" EndIcon={ArrowRightIcon} type="submit">
|
|
{t("continue")}
|
|
</Button>
|
|
</footer>
|
|
</section>
|
|
</Form>
|
|
),
|
|
hideConfirm: true,
|
|
showCancel: false,
|
|
},
|
|
{
|
|
id: "profile",
|
|
title: t("nearly_there"),
|
|
description: t("nearly_there_instructions"),
|
|
Component: (
|
|
<form className="sm:mx-auto sm:w-full" id="ONBOARDING_STEP_4">
|
|
<section className="space-y-4">
|
|
<fieldset>
|
|
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
|
|
{t("full_name")}
|
|
</label>
|
|
<input
|
|
ref={nameRef}
|
|
type="text"
|
|
name="name"
|
|
id="name"
|
|
autoComplete="given-name"
|
|
placeholder={t("your_name")}
|
|
defaultValue={props.user.name || enteredName}
|
|
required
|
|
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 shadow-sm focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
|
|
/>
|
|
</fieldset>
|
|
<fieldset>
|
|
<label htmlFor="bio" className="block text-sm font-medium text-gray-700">
|
|
{t("about")}
|
|
</label>
|
|
<input
|
|
ref={bioRef}
|
|
type="text"
|
|
name="bio"
|
|
id="bio"
|
|
required
|
|
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 shadow-sm focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
|
|
defaultValue={props.user.bio || undefined}
|
|
/>
|
|
<Text variant="caption" className="mt-2">
|
|
{t("few_sentences_about_yourself")}
|
|
</Text>
|
|
</fieldset>
|
|
</section>
|
|
</form>
|
|
),
|
|
hideConfirm: false,
|
|
confirmText: t("finish"),
|
|
showCancel: true,
|
|
cancelText: t("set_up_later"),
|
|
onComplete: async () => {
|
|
try {
|
|
setSubmitting(true);
|
|
await updateUser({
|
|
bio: bioRef.current?.value,
|
|
});
|
|
setSubmitting(false);
|
|
} catch (error) {
|
|
setError(error as Error);
|
|
setSubmitting(false);
|
|
}
|
|
},
|
|
},
|
|
];
|
|
/** End Onboarding Steps */
|
|
|
|
useEffect(() => {
|
|
detectStep();
|
|
setReady(true);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
|
|
if (loading || !ready) {
|
|
return <div className="loader"></div>;
|
|
}
|
|
|
|
return (
|
|
<div className="bg-brand min-h-screen" data-testid="onboarding">
|
|
<Head>
|
|
<title>Cal.com - {t("getting_started")}</title>
|
|
<link rel="icon" href="/favicon.ico" />
|
|
</Head>
|
|
|
|
{isSubmitting && (
|
|
<div className="fixed z-10 flex h-full w-full flex-col content-center items-center justify-center bg-white bg-opacity-25">
|
|
<Loader />
|
|
</div>
|
|
)}
|
|
<div className="mx-auto px-4 py-24">
|
|
<article className="relative">
|
|
<section className="space-y-4 sm:mx-auto sm:w-full sm:max-w-lg">
|
|
<header>
|
|
<Text className="text-white" variant="largetitle">
|
|
{steps[currentStep].title}
|
|
</Text>
|
|
<Text className="text-white" variant="subtitle">
|
|
{steps[currentStep].description}
|
|
</Text>
|
|
</header>
|
|
<section className="space-y-2 pt-4">
|
|
<Text variant="footnote">
|
|
Step {currentStep + 1} of {steps.length}
|
|
</Text>
|
|
|
|
{error && <Alert severity="error" message={error?.message} />}
|
|
|
|
<section className="flex w-full space-x-2 rtl:space-x-reverse">
|
|
{steps.map((s, index) => {
|
|
return index <= currentStep ? (
|
|
<div
|
|
key={`step-${index}`}
|
|
onClick={() => goToStep(index)}
|
|
className={classnames(
|
|
"h-1 w-1/4 bg-white",
|
|
index < currentStep ? "cursor-pointer" : ""
|
|
)}></div>
|
|
) : (
|
|
<div key={`step-${index}`} className="h-1 w-1/4 bg-white bg-opacity-25"></div>
|
|
);
|
|
})}
|
|
</section>
|
|
</section>
|
|
</section>
|
|
<section className="mx-auto mt-10 max-w-xl rounded-sm bg-white p-10">
|
|
{steps[currentStep].Component}
|
|
|
|
{!steps[currentStep].hideConfirm && (
|
|
<footer className="mt-8 flex flex-col space-y-6 sm:mx-auto sm:w-full">
|
|
<Button
|
|
className="justify-center"
|
|
disabled={isSubmitting}
|
|
onClick={debouncedHandleConfirmStep}
|
|
EndIcon={ArrowRightIcon}
|
|
data-testid={`continue-button-${currentStep}`}>
|
|
{steps[currentStep].confirmText}
|
|
</Button>
|
|
</footer>
|
|
)}
|
|
</section>
|
|
<section className="mx-auto max-w-xl py-8">
|
|
<div className="flex flex-row-reverse justify-between">
|
|
<button
|
|
disabled={isSubmitting}
|
|
onClick={handleSkipStep}
|
|
className="text-sm leading-tight text-gray-500 dark:text-white">
|
|
{t("next_step")}
|
|
</button>
|
|
{currentStep !== 0 && (
|
|
<button
|
|
disabled={isSubmitting}
|
|
onClick={decrementStep}
|
|
className="text-sm leading-tight text-gray-500 dark:text-white">
|
|
{t("prev_step")}
|
|
</button>
|
|
)}
|
|
</div>
|
|
</section>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export async function getServerSideProps(context: NextPageContext) {
|
|
const session = await getSession(context);
|
|
|
|
if (!session?.user?.id) {
|
|
return {
|
|
redirect: {
|
|
permanent: false,
|
|
destination: "/auth/login",
|
|
},
|
|
};
|
|
}
|
|
const user = await prisma.user.findFirst({
|
|
where: {
|
|
id: session.user.id,
|
|
},
|
|
select: {
|
|
id: true,
|
|
startTime: true,
|
|
endTime: true,
|
|
username: true,
|
|
name: true,
|
|
email: true,
|
|
bio: true,
|
|
avatar: true,
|
|
timeZone: true,
|
|
identityProvider: true,
|
|
completedOnboarding: true,
|
|
selectedCalendars: {
|
|
select: {
|
|
externalId: true,
|
|
integration: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
if (!user) {
|
|
throw new Error(`Signed in as ${session.user.id} but cannot be found in db`);
|
|
}
|
|
|
|
if (user.completedOnboarding) {
|
|
return {
|
|
redirect: {
|
|
permanent: false,
|
|
destination: "/event-types",
|
|
},
|
|
};
|
|
}
|
|
|
|
const credentials = await prisma.credential.findMany({
|
|
where: {
|
|
userId: user.id,
|
|
},
|
|
select: {
|
|
id: true,
|
|
type: true,
|
|
key: true,
|
|
userId: true,
|
|
appId: true,
|
|
},
|
|
});
|
|
|
|
const integrations = getApps(credentials)
|
|
.filter((item) => item.type.endsWith("_calendar"))
|
|
.map((item) => omit(item, "key"));
|
|
|
|
// get user's credentials + their connected integrations
|
|
const calendarCredentials = getCalendarCredentials(credentials, user.id);
|
|
// get all the connected integrations' calendars (from third party)
|
|
const connectedCalendars = await getConnectedCalendars(calendarCredentials, user.selectedCalendars);
|
|
|
|
const eventTypes = await prisma.eventType.findMany({
|
|
where: {
|
|
userId: user.id,
|
|
},
|
|
select: {
|
|
id: true,
|
|
title: true,
|
|
slug: true,
|
|
description: true,
|
|
length: true,
|
|
hidden: true,
|
|
},
|
|
});
|
|
|
|
const schedules = await prisma.schedule.findMany({
|
|
where: {
|
|
userId: user.id,
|
|
},
|
|
select: {
|
|
id: true,
|
|
},
|
|
});
|
|
|
|
return {
|
|
props: {
|
|
session,
|
|
user,
|
|
integrations,
|
|
connectedCalendars,
|
|
eventTypes,
|
|
schedules,
|
|
},
|
|
};
|
|
}
|