Compare commits

...
359 Commits
Author SHA1 Message Date
zomars 368d1afe27 v2.0.5 2022-10-10 17:01:53 -06:00
zomars 2069fd7248 Update settings.json 2022-10-10 16:27:31 -06:00
7ed21b692e #4306: Added darkmode support for select by styling via tailwind (#4824)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-10 18:50:43 +00:00
4fcdc7bf5c added badge for unconfirmed bookings (#4912)
* added badge for unconfirmed bookings

* added link to unconfirmed bookings

* added rounded prop to badge, added unconfirmed badge to botto nav

* Supporting recurring events + bug fix

* Tooltip and hover style

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>
2022-10-10 17:00:09 +00:00
2a0a293f8c Add new workflow action: Send email to specific email address (#4929)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-10 13:40:20 +00:00
1b15b71a24 Feat/improve animation on public booking page (#4848)
* Improve responsive layout for timezone select on booking page.

* Close TZ dropdown on booking page when it's hidden for mobile/desktop, this prevents having a dropdown menu floating around.

* Improved animation for showing timeslots on booking page.

* Changed back button on booking page to sm+ to md+ like it was before.

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-10 13:24:06 +00:00
github-actions[bot]andCrowdin Bot 5d9889a90c New Crowdin translations by Github Action (#4927)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-10 13:42:19 +01:00
Peer Richelsen a7ee3366eb Update CONTRIBUTING.md 2022-10-10 09:31:33 +01:00
c68e7595fe New Crowdin translations by Github Action (#4923)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-10 09:28:18 +01:00
Herbert Mühlburger 3f6be3b2c0 correct german spelling (#4925) 2022-10-10 09:27:44 +01:00
Hariom Balhara f1647248a2 PaymentPage: Fix localStorage access issue in incognito (#4922)
* localStorage isnt available at server and in incognito mode

* Do translation server side for Payment Page
2022-10-10 08:11:47 +00:00
Leo Giovanettiandkodiakhq[bot] d7db1ad303 Hiding event type loses recurring config (#4911)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-09 12:36:39 +00:00
Udit Takkar bd48fd01b1 fix: remove hr add spacing, fix link in env file (#4916) 2022-10-09 11:08:47 +00:00
github-actions[bot]andCrowdin Bot fabf692a08 New Crowdin translations by Github Action (#4904)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-08 18:10:55 +01:00
Alex van Andel ab62fc5d47 Bugfix/dst available hour mismatch (#4909)
* Need to use local time, not inviteeDate

* Use selected timeZone to perform localWorkingHours logic

* Adding comment

* More comments
2022-10-07 16:10:48 -06:00
zomars 0c4cb2e2b0 Added denullish utils for zod
Needed for API refactoring
2022-10-07 14:48:41 -06:00
Carina WollendorferandCarinaWolli 7d77132102 Add 'after event ends' trigger to workflows (#4882)
* add after event ends workflow trigger to enum

* add after meetings ends trigger

* fix label for time span

* add deleted label for message template

* add lost changes from merge conflict

* set reminder for already existing bookings

* fix label 'How long before event starts/ends'

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-10-07 12:18:28 -06:00
Joe Au-Yeung 50ac5f2892 Fix MS Teams missing refresh token (#4910) 2022-10-07 13:59:58 -04:00
Peer Richelsen fd12fcc4b8 v2 for help dialog (#4901) 2022-10-07 13:21:49 +01:00
+2 58453d0f2f /event-types/detail wrong empty screen for workflows (#4757)
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>

Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2022-10-07 09:25:30 +00:00
Hariom Balhara 44fd1b9fef Improve default app README.mdex (#4893) 2022-10-07 07:29:09 +00:00
Leo Giovanetti 0098591a56 Applying feedback from a merged PR (#4887) 2022-10-06 17:38:50 -03:00
Leo Giovanetti 41c1e960f8 Adjusting jest and expanding test builders (#4837)
* Adjusting jest and expanding test builders

* Removing unneeded jest config
2022-10-06 13:29:20 -06:00
Leo Giovanetti 4e49d32c61 Fixing unconfirmed/recurring bookings (#4881) 2022-10-06 13:23:22 -06:00
+9 0b29c5063a Wrong css in event configuration page buttons (#4869)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev>
Co-authored-by: Matheus Benini Ferreira <88898100+MatheusBeniniF@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: C000Ldude <coolmagnas@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Eman <emmanuelgatwech@gmail.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Omar López <zomars@me.com>
2022-10-06 12:16:32 -06:00
zomars 95a92567cb Update change-username.e2e.ts 2022-10-06 12:15:14 -06:00
+7 9e183e6ad1 Remove black ring on focus when copying private link (#4883)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: MuriloAmarals <muralha2000@gmail.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev>
Co-authored-by: Matheus Benini Ferreira <88898100+MatheusBeniniF@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: C000Ldude <coolmagnas@gmail.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Eman <emmanuelgatwech@gmail.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-06 15:18:16 +00:00
Peer Richelsen 7900f8bab5 fixes to booking page (#4876) 2022-10-06 14:33:57 +00:00
Hariom Balharaandkodiakhq[bot] 50904d2588 useForm defaultValues should be available initially (#4863)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-06 13:37:52 +00:00
github-actions[bot]andCrowdin Bot 6f811f4f0a New Crowdin translations by Github Action (#4877)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-06 14:14:10 +01:00
sean-brydonandBailey Pumfleet d9000d0cbe Fix height on trash (#4875)
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-10-06 13:33:51 +01:00
sean-brydonandBailey Pumfleet ff78e66051 Add successful booking redirect URL to booking page (#4871)
* Add redirect URL field and switch

* Remove and add new translation for description

Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-10-06 13:32:11 +01:00
Hariom Balharaandkodiakhq[bot] 7c30f8ca4b Routing Forms Type nigration (#4861)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-06 12:21:04 +00:00
Jeroen Reumkens 0ebce77a7b #4851: Always take timeformat on booking page from profile if user is logged in. Also hides timeformat toggle on booking page for logged in users. For non loggedin users we still infer by looking at browser timezone, after that we set a localstorage which we will look at, and which will get updated when the user updates the toggle in the timezone dropdown. (#4865) 2022-10-06 10:25:41 +00:00
sean-brydon 4282adac4b Fix text wrap (#4867) 2022-10-06 10:57:57 +01:00
Nafees Nazikandkodiakhq[bot] ef611e2360 fix: fixes multiple issues in routing form (#4676)
* feat: update forms procedure to match getServerSideProps

* feat: init react query and use prefetched data

* feat: invalidate onSettled while creating a form

* feat: use optimistic updates when deleting

* fix: add optional chaining

* feat: add server-side translation

* feat: add correct translation key

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-06 06:49:03 +00:00
github-actions[bot]andCrowdin Bot 10ab86b332 New Crowdin translations by Github Action (#4772)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-05 20:23:28 +01:00
Peer Richelsen f04cfef0af reduced padding top on mobile onboarding (#4856) 2022-10-05 18:46:20 +00:00
Alex van Andel 50151403c4 v2.0.4 2022-10-05 16:27:01 +01:00
Peer RichelsenandAlex van Andel 7aa131f349 added TimezoneChangeDialog (#4818)
* nit

* added timezoneChangeDialog

* prevent interactOutside

* nit

* fixed title prop clash

* nit

* remind in one day if clicked outside

* Use utcOffset instead of tz string for checking diff

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-05 14:33:29 +00:00
Jeroen Reumkens d88bdc368b Ensure we have to correct tabindexes for addons. Before addons AFTER the input still would get tab focus before the input. (#4849) 2022-10-05 14:22:06 +00:00
Alex van Andel 255938fda0 Remove 'avatar' from query (#4846) 2022-10-05 13:37:42 +00:00
Jeroen ReumkensandPeer Richelsen 5766d6f5f4 Improve responsive layout of timezone select on booking page (#4839)
* Improve responsive layout for timezone select on booking page.

* Close TZ dropdown on booking page when it's hidden for mobile/desktop, this prevents having a dropdown menu floating around.

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-05 13:11:26 +00:00
Peer Richelsen 9d299c6d5f fixed event type z-index (#4844) 2022-10-05 12:58:59 +00:00
f49783b453 Bugfix/reinstate tab name with fixed test (#4842)
* added ?tabName=setup to event-type detail link to give it active status (#4830)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* Use pathname instead of href directly

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-05 11:55:11 +00:00
Jeroen Reumkens 246fce3ab8 #3785: Converted billing settings page to v2. (#4795) 2022-10-04 22:54:15 +00:00
zomars 82f262f60d Quickfix for settings layout on mobile 2022-10-04 15:24:59 -05:00
zomars da8d2da368 v2.0.3 2022-10-04 14:46:51 -05:00
alannncandOmar López f01058e073 fix/error-while-confirm-booking (#4834)
* Add try/catch on workflows while confirming a booking

* Revert "added ?tabName=setup to event-type detail link to give it active status (#4830)"

This reverts commit 11c44db609.

Co-authored-by: Omar López <zomars@me.com>
2022-10-04 14:46:17 -05:00
zomars 8e369d3fb5 Submodule sync 2022-10-04 13:10:16 -05:00
Peer Richelsenandkodiakhq[bot] 11c44db609 added ?tabName=setup to event-type detail link to give it active status (#4830)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-04 17:03:31 +01:00
Peer RichelsenandAlex van Andel 9d911cbb59 fixed dropdown in safari (#4828)
* fixed dropdown in safari

* nit

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-04 15:50:15 +00:00
Alex van Andel 30a887df6f Some tweaks to the ui - use button - use flex justify-between (#4826) 2022-10-04 15:37:20 +00:00
62fea25eb9 fix/connected calendar list different states (#4821)
* Display error if trpc query failed, catch error on getting calendar and cleanup to don't leak credentials to client

* Add calendar connection broke error

* Edit style on alert and disconnecting button

* Ts errors and added translation

* Type fix

* Key and text change for broken calendar connection

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-04 14:56:38 +00:00
Alex van Andel d43d8700d0 Fixes global console error (#4825) 2022-10-04 14:01:32 +00:00
Peer Richelsen e7ed20ea27 fixed border on dark mode success (#4823) 2022-10-04 13:43:49 +01:00
Peer RichelsenandBailey Pumfleet 5e12b5defe new button in logout (#4816)
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-10-04 09:31:13 +00:00
Joe Au-YeungandBailey Pumfleet d241a522fb Add placeholder (#4803)
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-10-04 10:20:21 +01:00
+2 ae929f27b0 /workflows /detail-view - unusual scroll behaviour (#4806)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: C000Ldude <coolmagnas@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-10-04 09:17:30 +00:00
Syed Ali Shahbaz 369d7c4e28 adds custom filter allowing eml format (#4815) 2022-10-04 10:02:28 +01:00
Jeroen Reumkens 4438ca2a63 Improve password peek positioning accross app and website. (#4810) 2022-10-03 19:52:57 +00:00
Hariom Balhara 75589db569 Hotfix: Fix Broken webhooks (#4807) 2022-10-04 00:33:47 +05:30
Alex van Andel 3752ac767a Remove eye for todays release (#4802) 2022-10-03 17:08:17 +01:00
+3 f3e2cf5075 Onboarding, input validation error isn't positioned correctly (#4758)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Eman <emmanuelgatwech@gmail.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-01 21:01:41 +00:00
Peer Richelsen 92a29593aa Update README.md 2022-10-01 21:47:34 +01:00
Peer Richelsen 367c5d89cc removed capitalize from headlines (#4790) 2022-10-01 11:50:27 +01:00
f21f36547c /event-types /team-detail /scheduling type - double divider under team members (#4485)
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: hustle-ps <grace.devolop@gmail.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>

Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: hustle-ps <grace.devolop@gmail.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-01 02:59:20 +00:00
Joe Au-Yeung d3f8d954e7 Update Close.com info (#4777) 2022-09-30 20:22:47 +01:00
27372d624d add skeleton loader for workflows in event type settings (#4774)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-30 19:39:23 +01:00
a5d2f2444a Fixes 'How to use Additional Inputs as Variables' dialog in workflows (#4773)
* add i18n translations and fix dialog width and height

* fix typo

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-09-30 14:41:12 +00:00
6d45e1c7c7 #4533: Show correct time format on booking page (#4771)
* #4533: Store timeformat preferences in localstorage when changing profile. This way you see the correct format on the booking page. Also abstracted the reuse of the localstorage key for timeformat into a small util function, to prevent typos in the future.

* Update apps/web/components/booking/pages/AvailabilityPage.tsx

* 4533: Changed 24hfromlocalstorage values from strings to real booleans for better DX.

* Update apps/web/lib/clock.ts

Co-authored-by: alannnc <alannnc@gmail.com>

* Make code shorter by only handling remaining cases

Co-authored-by: alannnc <alannnc@gmail.com>

* Remove unnecessary boolean check

Co-authored-by: alannnc <alannnc@gmail.com>

Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2022-09-30 14:45:28 +02:00
48419469a6 #4445: Fix to prevent bookings with long titles or a lot of content from overflowing on the page. (#4769)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
2022-09-30 11:47:20 +01:00
Joe Au-Yeung 045ebb6e01 Fix RadixUI dropdown 1.0.0 (#4767)
* Add portal part

* Add portal part to pages

* Add portal part to pages
2022-09-30 12:29:22 +02:00
Jeroen Reumkens c78ea5de0b Added 100vh fix for ios to make sure it always fills the screen height. By default 100vh on ios sometimes will and sometimes won't take into account the address bar height, causing on eg the event types or bookings pages the content to be behind our bottom nav. (#4770) 2022-09-30 10:12:22 +01:00
github-actions[bot]andCrowdin Bot d1a787315c New Crowdin translations by Github Action (#4762)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-09-29 19:23:43 +01:00
ce1f1f3c47 Upgrade to Radix 1.0 (#4746)
* Change package.json version and yarn lock

* Add popover

Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-29 18:22:07 +01:00
2f8bd3c07a add trpc v10 (#4683)
* revert me later

* let's see if this builds

* fix dupe proc

* fix: v10 works

* fix type error

* fix type error

* fix type errors

* fix more

* add example procedure

* spreading not needed

* Update yarn.lock

* Revert "revert me later"

This reverts commit 0c8c15d0577e0c287223039c7b6958b2b0c12c69.

Co-authored-by: Chris Bautista <chrisbautista@netflix.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-29 16:58:29 +00:00
Omar LópezandAlex van Andel d2f27a0463 Fixes for advanced tabs form logic (#4731)
* Fixes for advanced tabs form logic

* Typefix

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-29 16:46:47 +00:00
Jeroen Reumkensandkodiakhq[bot] 9134ad8c5e Improved team layout when blocks have unequal content length. (#4763)
* Improved team layout when blocks have unequal content length.

* Removed unused translation in members component.

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-29 16:34:01 +00:00
Alex van Andel 506036e751 Fixes few i18n related bugs on change password (#4755)
* Fixes few i18n related bugs

* Use PasswordField
2022-09-29 16:19:03 +00:00
5e80f5056d Ensure currentUsername is set before comparing it with username (#4750)
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-29 17:05:01 +01:00
9304ca1415 Fix long url and show password (#4738)
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-29 11:40:37 -03:00
github-actions[bot]andCrowdin Bot fb246fdee6 New Crowdin translations by Github Action (#4754)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-09-29 14:31:30 +01:00
7b9e77f43a Fixing border in installed apps (#4745)
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-29 13:26:36 +00:00
47df21322d Fixes translation issue of dynamic variables in workflows (#4734)
* replace all underscores for variable translations

* use g instead of replaceAll

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2022-09-29 15:07:22 +02:00
6017806763 Fix connected calendar list (#4743)
* Fix connected calendar list

* Fixed invalid params in setDestinationCalendar

* Attempt to fix tests by changing viewport size, bug on /booking pages

* Fixes BookingListItem + another user.id param removed

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-29 12:58:53 +00:00
Alex van Andel b347f29d6c Feature/deprecate title translations (#4682)
* wip

* Aims to deprecate _title keys

* Removed _title translation

* Revert "wip"

This reverts commit 3e33fc6b1ebc064ddfacd8ebc2b63b2ba009d416.
2022-09-29 12:41:40 +00:00
zomars c26cf74aec Revert "Increase timeouts (#4716)"
This reverts commit 145fb09b73.
2022-09-29 14:23:52 +02:00
zomars 760ed74a11 Fixes broken E2E tests
Introduced in #4657
2022-09-29 14:19:04 +02:00
Hariom BalharaandAlex van Andel 145fb09b73 Increase timeouts (#4716)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-29 10:06:57 +00:00
Peer Richelsen dd14e827e1 added cancel button to team creation (#4756)
* added cancel button

* nit

* nit
2022-09-29 10:47:51 +01:00
246c90c10f public page: border and padding (#4730)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Klinger Matheus <klinger.matheus@gitstart.dev>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-29 10:24:28 +01:00
Alexey Pavlov a42f703c1d fix: calendar spacings for mobile on availability page (#4728) 2022-09-29 10:21:52 +01:00
Leo Giovanetti 7256dbf84f Fix updating slug (#4747) 2022-09-28 23:07:55 +01:00
github-actions[bot]andCrowdin Bot f5faebad67 New Crowdin translations by Github Action (#4744)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-09-28 22:19:00 +01:00
+3 be15affa5d fix padding (#4673)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-28 22:02:07 +01:00
58dd5308ac Improved load state (#4696)
* Improved load state

* Don't use editable heading if it is readonly

* Fix ts errors

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-28 19:05:28 +01:00
14a6c5a03f feat: add transition in toasts (#4658)
* feat: add transition in toasts

* feat: toast animation improved

* fix: type check in toast

Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-28 19:01:19 +01:00
5af20f5a7b New Crowdin translations by Github Action (#4742)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-28 18:57:58 +01:00
Peer Richelsen 8478363a23 minor layout fixes (#4741)
* fixed back button, fixed app store detail on mobile

* fixed settings padding and go back button

* Update packages/ui/v2/core/Shell.tsx

* Update packages/ui/v2/core/navigation/tabs/VerticalTabItem.tsx
2022-09-28 18:56:59 +01:00
19b3157c94 New Crowdin translations by Github Action (#4729)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-28 18:51:19 +01:00
Peer Richelsen d4dd451c26 Update packages/ui/v2/core/Shell.tsx 2022-09-28 18:49:23 +01:00
Peer Richelsen 6c046f6507 fixed back button, fixed app store detail on mobile (#4737) 2022-09-28 18:46:14 +01:00
Peer Richelsen a792d31939 Update package.json 2022-09-28 17:13:04 +01:00
zomars fc997739b4 v2.0.2 2022-09-28 11:07:03 +02:00
github-actions[bot]andCrowdin Bot 542d9aa3c6 New Crowdin translations by Github Action (#4726)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-09-28 01:07:04 +01:00
8f3e088db7 New Crowdin translations by Github Action (#4670)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-28 00:53:02 +01:00
699313ac70 fix: position of badges (#4657)
* fix: position of badges

* feat: mobile design added

* fix: minor ui bugs

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-28 00:51:41 +01:00
Carina WollendorferandCarinaWolli fca7087e7d render scheduled email instad of rescheduled email (#4722)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-09-27 20:48:35 +01:00
Carina WollendorferandCarinaWolli a6b16490c0 change zapier_other to zapier_automation (#4719)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-09-27 12:01:54 +00:00
5da735c418 /settings sidebar doesn't match figma designs fix (#4705)
Co-authored-by: Guest <guest@pop-os.localdomain>
Co-authored-by: root <root@pop-os.localdomain>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-09-27 10:24:09 +01:00
Joe Au-Yeung f9ef883f3d Close settings sidebar on window resize (#4662) 2022-09-26 15:09:51 -04:00
Leo Giovanetti 9ddbaf0966 Fixing wrapping of buttons in appstore (#4704) 2022-09-26 14:12:54 +00:00
0695ce7edb redirect to even types after deletion (#4707)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-26 13:31:33 +01:00
Carina WollendorferandCarinaWolli 74f0d39997 fix password reset text (#4708)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-09-26 13:18:11 +01:00
Peer Richelsen 85ab3fb164 fixed z-index and buttons in avatar uploader (#4698) 2022-09-24 17:09:37 +00:00
Hariom BalharaandPeer Richelsen ab417ac267 Hide mobile nav on subpages based on backPath (#4695)
* Hide nav on subpages based on backPath. Add backPath where it was missing

* Fix heading not showing on mobile

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-24 11:55:50 +00:00
Hariom BalharaandAlex van Andel 24635af730 Hotfix: Be more strict with safeRedirectUrl check (#4675)
* Be more strict

* Apply suggestions from code review

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-24 09:40:49 +01:00
sean-brydonandAlex van Andel 682f0c66ff Fix min booking notice (#4679)
* Fix miniumbookig notice

* Fixes minutes field not working properly (#4680)

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-23 14:00:45 +00:00
ace27ca84e Use PremiumTextField in v2 (#4577)
* Use PremiumTextField in v2

* Fix tests

* Fix Lint

* Fix TS error

* Fixes

* Fix username input in self hosted scenario

* Fix type error

* Fix Tests

* Fix username text field test

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
2022-09-22 17:34:17 +00:00
Hariom Balharaandkodiakhq[bot] ce41397517 Routing Forms Improvements - Rename routing_forms to routing-forms (#4546)
* Animate fields list and routes list

* Rename routing_forms slug to routing-forms

* Add comments

* Fixtypo

* Add dropdown separator for consistency

* Fix missing occurences of routing_forms and improve types for webhooks

* Fix weird error about title child is an array

* Fix webhook issues

* Fix webhook tests and issues found during fixing them

* Fix lint errors and warnings

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-22 22:53:43 +05:30
zomars 81aba55710 Website update 2022-09-22 16:49:53 +00:00
Hariom Balhara c5fc3a7e8e Add Billing link (#4668) 2022-09-22 15:33:59 +00:00
Hariom Balhara f628ac4366 Avoid creating dynamic HTML (#4669) 2022-09-22 19:13:45 +05:30
6e2e252c49 feat: new filter added (#4612)
* feat: new filter added

* fix: type error

* Update packages/ui/v2/core/layouts/BookingLayout.tsx

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-22 07:48:27 +00:00
Nafees NazikandPeer Richelsen 10d3a126aa fix: skeleton loader issue in settings/api-keys (#4634)
* fix: skeleton loader issue

* fix: simplify logic

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-22 05:29:15 +00:00
341c39cb6b fix-connect-calendar-minor-issues (#4586)
* Fix displaying both loading and calendar list while returning to page from calendar, added redirect on SSR for installed/calendar page

* Use SSR only for installed/[category] page because of redirect requirement based on cookie

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2022-09-22 04:26:56 +00:00
Alex van Andel 91909281bc Fixes minimumBookingNotice (#4656) 2022-09-21 21:37:31 +00:00
f14b13a1ba feat: optimistically update when deleting an availability (#4648)
* feat: use optimistic updates for deleting

* feat: remove loading props

* feat: remove loading props from v1 component

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-21 21:04:21 +00:00
Joe Au-YeungandAlex van Andel ca859c871c Remove Exchange 2013 and 2016 from seed script (#4651)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-21 16:46:51 +00:00
Peer Richelsen 6e49a3e1c3 fixed <Meta> and fixed dropdown in api keys (#4652)
* fixed dropdown in api keys

* nit

* fixed meta title for settings
2022-09-21 16:35:19 +00:00
Joe Au-YeungandAlex van Andel 830f42bf9a Run available user check if event type does not have checks (#4646)
* Move available users if there are no seats

* Fix adding default seat to event type

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-21 16:30:44 +00:00
Krunal ShahandPeer Richelsen 0830523b84 fix: spacing issue in suffix and prefix (#4623)
* fix: spacing issue in suffix and prefix

* fix input leading color

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-21 16:09:51 +00:00
Peer Richelsen 612b293a70 typo 2022-09-21 16:07:48 +00:00
Peer Richelsen 3ecfa75f86 fixd layout shift in app detail (#4650) 2022-09-21 16:07:11 +00:00
Hariom BalharaandPeer Richelsen 631e974769 Fix flash of untranslated content by adding inline Skeleton Loaders on EventType (#4519)
* Add support for easy use of inline skeleton

* Fix user being taken to /v2 path

* Fixes

* Fix TS

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-21 16:01:47 +00:00
github-actions[bot]andCrowdin Bot 42c128b790 New Crowdin translations by Github Action (#4613)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-09-21 15:44:08 +00:00
7d1065d228 Mobile: hide bottom nav on any sub page (#4626)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: hustle-ps <grace.devolop@gmail.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-21 15:39:54 +00:00
Leo Giovanetti e2747f89d0 Calendar credential key missing fix (#4645) 2022-09-21 13:24:02 +00:00
Alex van Andel a1d4589e66 Icon button instead of text (#4636)
* Icon button instead of text

* Add tooltip on hover
2022-09-21 05:52:14 +00:00
Afzal Sayed 7d2f87a13e Improve new schedule creation flow (#4635) 2022-09-21 05:40:58 +00:00
Carina WollendorferandCarinaWolli 4e7200a44b show avatar placeholder in settings shell (#4639)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-09-21 04:53:31 +00:00
Alex van Andel 52959fe257 Fixes password prompt race condition (#4644) 2022-09-21 10:11:08 +05:30
Peer Richelsen 61b010a60a app store updates (caldav) (#4640)
* added (beta) label to CalDav and list of supported providers

* nit

* fixed app store title on mobile

* i18n for app store
2022-09-20 17:48:20 +00:00
Carina WollendorferandCarinaWolli 4e6e484d8b only allow to add steps of user's workflow (#4637)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-09-20 17:00:43 +00:00
Nafees NazikandAlex van Andel 0a2de203a6 feat: animate event list while reordering (#4611)
* fix: prevent waiting while invalidating

* feat: add auto animate

* static postion is added to animation container because auto animate adding
a inline style of position relative, which breaks arrow buttons
position.

* fix: wait until query get cancelled

* fix: set querydata correctly

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-20 16:55:37 +01:00
Alex van Andelandkodiakhq[bot] 48ee7a956c Fixes #4606 - Add guests cannot be removed (#4609)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-20 12:37:34 +00:00
ae46d615ef fix: positioning of arrows (#4478)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-20 11:37:46 +00:00
Alex van Andel 9b6d5d2ff8 Fixes #4380 (#4615) 2022-09-20 10:56:52 +00:00
Hariom Balhara ec0352da20 Fix wrong team link (#4620) 2022-09-20 10:27:15 +00:00
Joe Au-Yeung 7e65d5d61d Return 401 on null keys (#4616) 2022-09-20 09:21:59 +01:00
f855f24d5c typo in /more (#4610)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-19 22:42:37 +00:00
c2057c4cc2 Fix event type navigation (#4565)
* Fix event type navigation

* Fix dropdown focus styling

* Fix border radius

* Remove stray classname

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2022-09-19 22:27:28 +00:00
github-actions[bot]andCrowdin Bot 1640264e40 New Crowdin translations by Github Action (#4599)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-09-19 16:21:12 +00:00
Peer Richelsen 579fd399c3 Merge branch 'production' into main 2022-09-19 16:13:47 +00:00
Peer Richelsen 1de00d46bf fixed 404 signup page (#4603) 2022-09-19 15:49:04 +00:00
Alex van AndelandPeer Richelsen 01db3ad14e fixed settings teams rewrite (#4600)
Co-authored-by: Peer Richelsen <peer@cal.com>
2022-09-19 14:52:26 +00:00
Peer Richelsen c1cb02a9d1 fixed settings teams rewrite 2022-09-19 14:38:21 +00:00
Hariom Balhara 344710d1f9 Hotfix/ Embed Tabs (#4593) (#4598) 2022-09-19 11:47:57 +00:00
Leo Giovanettiandkodiakhq[bot] 6f0b4f94e6 Hotfix: Fixing installed app navigation (#4597)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-19 17:05:21 +05:30
Hariom Balhara c6ad2de2f9 Embed width increase to allow preview to work in desktop mode (#4547) 2022-09-19 17:04:30 +05:30
Leo Giovanettiandkodiakhq[bot] 9ae3a0eaba Fixing installed app navigation (#4595)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-19 10:03:50 +00:00
Hariom Balhara 2f3d7318a4 Hotfix/ Embed Tabs (#4593) 2022-09-19 10:47:46 +01:00
alannnc 6e71de7ca3 fix isSelectedProp for settings/calendars switches (#4589) 2022-09-19 02:01:34 +00:00
zomars 7d277ad89f Attempt to fix duplicate webhooks 2022-09-19 01:58:36 +01:00
zomars ac276a9654 Fixes v2 in hrefs for webhooks 2022-09-19 01:01:55 +01:00
zomars 588748d78e Simplifies Vertical and Horizontal tabs 2022-09-19 00:58:28 +01:00
zomars 25711ef18e fixes packages mismatch 2022-09-18 23:12:23 +01:00
Jeroen Reumkens 440f53714b fix: Fixed password peek alignment which wasnot correct in all places. (#4582) 2022-09-18 20:23:48 +00:00
Peer Richelsen 22f6d8ab9b added more app store screenshots (#4564)
* added screenshots

* nit

* fixed #4574
2022-09-18 11:58:32 +00:00
Peer Richelsen 57f0d115cf fixed button group in /teams (#4573)
* fixed button group in /teamas

* nit
2022-09-18 11:32:54 +00:00
Hariom Balhara 23bc6aa600 Hotfix: Add back copy Typeform redirect url link and update screenshot (#4569)
* Add back copy Typeform redirect url link and update screenshot

* Delete unused files
2022-09-18 10:47:31 +00:00
Hariom Balhara abdb196f56 Fix typeform how-to-use page (#4567) 2022-09-18 05:03:34 +00:00
zomars 8c0e2b5129 v2.0.1 2022-09-17 23:29:10 +01:00
Joe Au-YeungandOmar López cf32287109 Add password on confirmation when changing email (#4503)
* Add email confirmation

* Confirm password only if email was changed

Co-authored-by: Omar López <zomars@me.com>
2022-09-17 23:27:25 +01:00
Leo GiovanettiandOmar López 2ef8e230c5 Categories strip fix (#4562)
Co-authored-by: Omar López <zomars@me.com>
2022-09-17 23:26:01 +01:00
546d0d50c4 Impersonation fix (#4521)
* Impersonation fix

* Update packages/features/ee/impersonation/lib/ImpersonationProvider.ts

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

* Fix zod schema

* Early returns

Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>
2022-09-17 21:09:06 +00:00
Joe Au-Yeung 33e8198779 Hotfix To Seats on Event Types (#4528)
* Hotfix

* Add seats input to event type page
2022-09-17 21:44:29 +01:00
Leo Giovanettiandzomars e7dd0ff3e9 Removing update table user plan (#4551)
* Removing update table user plan

* All users are PRO now

Co-authored-by: zomars <zomars@me.com>
2022-09-17 20:29:06 +00:00
Leo GiovanettiandPeer Richelsen 1b2707d4f0 Old teams page in v2 (#4553)
* Old teams page in v2

* Tea nav item fix

* Team creation modal converted to v2

* Updated emptyscreen and shell button

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-17 17:53:31 +00:00
Agusti Fernandez Pardo 00071bcdf0 fix: general settings (Save / Update on submit) (#4557) 2022-09-17 17:09:52 +00:00
Jonathan Giardino fc5cc5b8bc Fix: event types skeleton is off #4540 (#4550) 2022-09-17 13:46:11 +00:00
Mackenly Jones 1e79a55825 Update vercel.mdx (#4545) 2022-09-17 11:26:55 +00:00
Alex van Andel 4645bbcb75 Use refs for inputs, full_name label, un-hardcoded WEBSITE_URL (#4542) 2022-09-17 02:40:27 +00:00
95b903110f Fix alignment issues (#4495)
* fix: spacing issue in externalRedirectUrl text input

* fix: remove unnecessary margin from appcard

* fix: center align switch

* fix: alignment in widget text field

* Update packages/app-store/ee/routing_forms/pages/route-builder/[...appPages].tsx

Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2022-09-17 02:00:22 +00:00
Leo Giovanetti 2917455324 Mobile tweaks app store (#4537) 2022-09-16 22:15:47 +00:00
Hariom BalharaandLeo Giovanetti d71765848c Fix workflows (#4518)
Co-authored-by: Leo Giovanetti <hello@leog.me>
2022-09-16 18:49:04 -03:00
Leo GiovanettiandBailey Pumfleet cf7b7d7d3d Fixing forget password link in login (#4526)
* Fixing forget password link in login

* Showing heap info

Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-09-16 15:27:44 +00:00
sean-brydon 96aaa36751 Fix dropdown focus + public dark mode buttons (#4527)
* Fix dropdown focus

* Fix darkmode buttons to use brand
2022-09-16 14:29:40 +00:00
Hariom Balhara 52e74245a4 Settings View Fixes - Theme not working/Nav Items not highlighted (#4522)
* Fix theme not working

* Fix Nav items of teams not highlighted
2022-09-16 12:03:58 +00:00
Bailey Pumfleet cc03b132e6 Update submodules 2022-09-15 21:29:50 +00:00
zomars b787d98502 v2.0.0 2022-09-15 21:55:54 +01:00
+4 d55ffe8d9d Feat: v2 App Store / Installed Apps (#4220)
* First commit

* Additional changes to meet designs

* Fixing app details disconnect button
App Store category filtering fixed

* Further changes for the fix

* Progress on various fronts

* Fixing e2e tests

* second try fixing e2e tests

* Yet again trying to fix e2e tests

* Missing action for non-credential flow

* Adding missing Add button

* Final tweaks and fixes

* Missing button in empty screen

* fix border radius

* EmptyScreen for calendar

* Dynamic installed app return page with highlights + other fixes

* Removing unneeded code

* App Store V2 Improvements over feat/v2-installed-apps (#4379)

* Removing unneeded text

* Fixing app details disconnect button and number of apps (#4305)

* V2 Settings Shell - Add Teams Section & UI fixes (#4347)

* Add teams to sidebar and fix UI

* Clean up

* V2 Multi-select (Team Select) (#4324)

* --init

* design improved

* further fine tuning

* more fixes

* removed extra JSX tag

* added story

* NIT

* revert to use of CheckedTeamSelect

* Removes comments

Co-authored-by: Peer Richelsen <peeroke@gmail.com>

* fix: toggle alligment (#4361)

* fix: add checked tranform for switch (#4357)

* fixed input size on mobile, fixed settings (#4360)

* fixing type errors

* Mobile fixes

* Tests fixes

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: zomars <zomars@me.com>

* Feat/team owner booking (#3999)

* WIP: testing queries

* feat: add badge

* fix: get only id

* refactor: get bookings query

* WIP: display attendees added

* fix: add type

* Adds skeleton loader to workflows (#4402)

* fix event types skeleton loader

* fix margin in event types

* fix skeleton loader in bookings

* add skeleton loader for workflow list

* add skeleton loader for editing page

* fix border radius of skeleton loader

* fix mobile view workflow list

* add mobile view for skeleton list

* make multi select full with in mobile view

* mobile view for edit skeleton loader

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* V2 settings teams (Profil, Members, Appearance View) (#4350)

* add team profile

* first version for team members page

* finish up design of member list item

* fix dialog buttons

* add missing seats and upgrading information

* add v2 dialog for changing role

* finish basic version of member's schedule

* remove modalContainer

* design fixes team profile page

* show only team info to non admins

* allow all member to check availabilities

* make time available heading sticky

* add dropdown for mobile view

* create team appearance view

* finish appearance page

* use settings layout and add danger zone for member

* add fallback logo

* Add teams to sidebar and fix UI

* add team invitations

* Clean up

* code clean up

* add impersontation and disable autofocus on calendar

* improve team info

* refactor teaminvitelist code and fix leaving a team

* add team pages to settings shell

* add link to create new team

* small fixes

* clean up comments

* V2 Multi-select (Team Select) (#4324)

* --init

* design improved

* further fine tuning

* more fixes

* removed extra JSX tag

* added story

* NIT

* revert to use of CheckedTeamSelect

* Removes comments

Co-authored-by: Peer Richelsen <peeroke@gmail.com>

* fix: toggle alligment (#4361)

* fix: add checked tranform for switch (#4357)

* fixed input size on mobile, fixed settings (#4360)

* fix image uploader button in safari

* code clean up

* fixing type errors

* Moved v2 team components to features

Adds deprecation notices

* Update SettingsLayout.tsx

* Migrated to features and build fixes

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>

* add rainbowkit form to v2 event-types (#4349)

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

* v1.9.5

* Fix/daterangepicker css (#4418)

* added styles from external stylesheet

* fixed a border color

* codacy improvements

* more codacy fix

* codacy why u be so dumb

* dynamic import rainbow (only if installed) (#4409)

* Tidyup app cards (#4428)

* Moving API to trpc query

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: hexcowboy <accounts@cowboy.dev>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Abstracting one level the redirectUrl for apps

* Moving enum to a better/working place

* Another try

* Missing code

* Correcting categories look in apps page

* ui tweaks

* Breadcrumb wrong font-weight

* Toast is v1 component and copy updated

* Fix TS error

* Fix TS error

* Update AppsLayout.tsx

* Update InstalledAppsLayout.tsx

* translations and disconnect button with icon

* Reverting file

* Test e2e fix on app-store

* Missing testid in button

* Fix to click categories that are visible

* Going directly to category page as a patch

* Fixing stripe e2e test

* Disconnect is now empty with icon

* Disabled stripe test for now as the navigation is completely changed

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: hexcowboy <accounts@cowboy.dev>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-15 19:53:09 +00:00
sean-brydon 1a776a7320 Add slot interval (#4496) 2022-09-15 18:48:22 +00:00
Joe Au-YeungandPeer Richelsen 9253257699 Hotfix - Add email field to profile settings (#4498)
* Add email field

* Add email hint

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-15 17:45:03 +00:00
fc74b686b6 Hotfix/fix tests (#4494)
* Comment userv2Banner

* Fixed wipemycal test

* Fixed 'Delete my account' and WipeMyCal tests

* Fixed at least one test, need stripe for others.

* Disable embed tests for now

* Fixed console error due to illegal setState

* Hopefully fixed change-password functionality

* Partially implement new team flow

* Fix Change password test by setting a password with both capital and small letters

* recurring event text fix

* Fixed hash-my-url test

* Fixed event-types tests, done?

* fixing event type edit first event e2e

* Temp disable

Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>
2022-09-15 16:59:48 +00:00
github-actions[bot]andCrowdin Bot 400a80f8e2 New Crowdin translations by Github Action (#4396)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-09-15 16:47:23 +00:00
sean-brydon 00c9584ee3 Disable (#4500) 2022-09-15 16:22:50 +00:00
zomars 26e07bc2d8 Fixed account deletion test 2022-09-15 13:10:41 +01:00
zomars 8da31cb293 V2 App Store test fixes 2022-09-15 12:52:52 +01:00
zomars a9b41766f2 Removed cookie requirement for V2 2022-09-15 12:09:06 +01:00
Leo Giovanetti 859bf60ee5 Including cal.com domain (#4492) 2022-09-15 11:53:39 +01:00
96451c5eb9 fix prop in react svg Icon (#4490)
Co-authored-by: hussamkhatib <hussamkhatib@gmail.com>
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-09-15 10:40:56 +00:00
Alex van Andel 4b0f7019a3 Missing translations added (#4491) 2022-09-15 10:28:21 +00:00
sean-brydon 7393ba1d17 Fixing focus visible (#4489) 2022-09-15 11:17:20 +01:00
+15 0c78dbea27 Create New Team (#4450)
* Add link to new teams

* Create new team

* Invite team members

* Bug fix

* Tidy up settings calendars (#4430)

* feat: 4232 - new theme toggle design. (#4371)

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>

* fix: Select dropdown styling (#4358)

* fix: select dropdown styling

* change check icon

* Revert select component, update v2 select

* Updates the background color to match figma design

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Fix input height 36px (#4425)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* Fix crash due to some props being undefined (#4427)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* Improve event type item description visibility (#4404)

* Make event type description more visible

* Delete unneeded import

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* Updating language in settings needs a reload to be translated (#4431)

Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* fix: remove i18n from title and description (#4354)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>

* Fix styles and remove temporarily elements (#4448)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* Fix deletion of route (#4466)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* feat: make toggle button default (#4437)

* feat: make switch default

* fix: refetch invalidated query

* React-query caching the previous isDefault state.

* fix: switch label color

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* Refactor skeleton loader files to v2 folders (#4447)

* create v2 skeleton loaders

* add depreciation notice to old skeleton loader files

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* Adds rescheduling dropdown to past unconfirmed bookings (#3726)

Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* fix: add a consistent key to the day ranges list (#4401)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* remove "Weve changed billing for teams" banner in teams (#4432)

Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* fix: remove name prop from NumberWidget (#4346)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* Fix bug

* Fix bug

* Squashed commit of the following:

commit c3cb839027
Author: Bailey Pumfleet <pumfleet@hey.com>
Date:   Wed Sep 14 17:13:13 2022 +0100

    Remove outline from user dropdown trigger (#4470)

commit 91c39a0306
Author: Sascha Schworm <1959209+saschaschworm@users.noreply.github.com>
Date:   Wed Sep 14 18:09:00 2022 +0200

    feat: Exchange with NTLM support (#4127)

    * add exchange package

    * fix conflicts

    * add setup page for v2

    * refactor setup page for v1

    * return exchange error messages to user if applicable

    Co-authored-by: Peer Richelsen <peeroke@gmail.com>
    Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>

commit f1c15f4e8a
Author: alannnc <alannnc@gmail.com>
Date:   Wed Sep 14 10:57:20 2022 -0500

    4382 onboarding fix the disabled state of the username in step 1 (#4451)

    * Convert username availability premium to v2

    * Update Username Availability to v2 design

    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit 3f29df16ad
Author: Alex van Andel <me@alexvanandel.com>
Date:   Wed Sep 14 16:40:56 2022 +0100

    KBar included @calcom/ui/v2/core -> Shell -> KBar -> Shell -> ... (#4469)

commit 3a06947c8a
Author: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Date:   Wed Sep 14 19:14:42 2022 +0530

    fixes active state of select options (#4456)

    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit f486b0242f
Author: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Date:   Wed Sep 14 17:28:59 2022 +0530

    fix: remove name prop from NumberWidget (#4346)

    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit 9bc2676822
Author: GitStart <1501599+gitstart@users.noreply.github.com>
Date:   Wed Sep 14 12:41:47 2022 +0100

    remove "Weve changed billing for teams" banner in teams (#4432)

    Co-authored-by: gitstart <gitstart@users.noreply.github.com>
    Co-authored-by: gitstart <gitstart@gitstart.com>
    Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
    Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
    Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit 5c72aa3497
Author: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Date:   Wed Sep 14 17:00:25 2022 +0530

    fix: add a consistent key to the day ranges list (#4401)

    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit 09da911bf9
Author: Dallen Pyrah <dallenpyrah@gmail.com>
Date:   Wed Sep 14 05:02:26 2022 -0600

    Adds rescheduling dropdown to past unconfirmed bookings (#3726)

    Co-authored-by: Omar López <zomars@me.com>
    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit 2fbaec41c9
Author: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Date:   Wed Sep 14 06:35:40 2022 -0400

    Refactor skeleton loader files to v2 folders (#4447)

    * create v2 skeleton loaders

    * add depreciation notice to old skeleton loader files

    Co-authored-by: CarinaWolli <wollencarina@gmail.com>
    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit 3bada22900
Author: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Date:   Wed Sep 14 15:54:41 2022 +0530

    feat: make toggle button default (#4437)

    * feat: make switch default

    * fix: refetch invalidated query

    * React-query caching the previous isDefault state.

    * fix: switch label color

    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit 45059554b6
Author: Hariom Balhara <hariombalhara@gmail.com>
Date:   Wed Sep 14 15:40:52 2022 +0530

    Fix deletion of route (#4466)

    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit 8b69359ccb
Author: alannnc <alannnc@gmail.com>
Date:   Wed Sep 14 04:55:49 2022 -0500

    Fix styles and remove temporarily elements (#4448)

    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit b457125915
Author: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Date:   Wed Sep 14 15:12:48 2022 +0530

    fix: remove i18n from title and description (#4354)

    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
    Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>

commit 01e0fed3c5
Author: GitStart <1501599+gitstart@users.noreply.github.com>
Date:   Wed Sep 14 10:28:23 2022 +0100

    Updating language in settings needs a reload to be translated (#4431)

    Co-authored-by: gitstart <gitstart@users.noreply.github.com>
    Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
    Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
    Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
    Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit c09107cec8
Author: Noah <hi@noahflk.com>
Date:   Wed Sep 14 11:08:50 2022 +0200

    Improve event type item description visibility (#4404)

    * Make event type description more visible

    * Delete unneeded import

    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit 2f9905e548
Author: Hariom Balhara <hariombalhara@gmail.com>
Date:   Wed Sep 14 14:16:42 2022 +0530

    Fix crash due to some props being undefined (#4427)

    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit ab4d910564
Author: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Date:   Wed Sep 14 09:18:40 2022 +0100

    Fix input height 36px (#4425)

    Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

commit 34bca4984f
Author: John Afolabi <johnafolabi.da@gmail.com>
Date:   Wed Sep 14 08:27:00 2022 +0100

    fix: Select dropdown styling (#4358)

    * fix: select dropdown styling

    * change check icon

    * Revert select component, update v2 select

    * Updates the background color to match figma design

    Co-authored-by: Peer Richelsen <peeroke@gmail.com>
    Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
    Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

commit 0907d154c7
Author: Jeroen Reumkens <hello@jeroenreumkens.nl>
Date:   Wed Sep 14 09:15:13 2022 +0200

    feat: 4232 - new theme toggle design. (#4371)

    Co-authored-by: Peer Richelsen <peeroke@gmail.com>
    Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>

commit 6f807930f6
Author: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Date:   Tue Sep 13 21:13:24 2022 +0100

    Tidy up settings calendars (#4430)

* Fix types

* Cleanup

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Jeroen Reumkens <hello@jeroenreumkens.nl>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: John Afolabi <johnafolabi.da@gmail.com>
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>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Noah <hi@noahflk.com>
Co-authored-by: GitStart <1501599+gitstart@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Dallen Pyrah <dallenpyrah@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-09-15 09:47:59 +00:00
Leo Giovanetti 7a95e714c2 Latest tweaks (#4482) 2022-09-15 09:33:34 +00:00
0d9d182b56 Settings Improvements - tRPC, Skeletons & Screen Width Issues (#4484)
* Add skeleton and trpc to profile page

* Refactor general page

* Calendars add skeleton loader

* Add translations to calendar empty screen

* Improve conferencing pages

* Add skeleton loader and TRPC to appearance page

* Fixes children being cut off at screen types

* PR feedback fixes

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-15 09:05:26 +00:00
d3b9ebbd34 Add automation category and move zapier and n8n to it (#4410)
* Add automation category

* Move n8n and Zapier to automation category

* Fix n8n category

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-09-15 08:16:56 +00:00
d35ce9c95e Availability default bug (#4345)
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: seunexplicit <seunxplicit@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>

Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Olusanya Timothy <48022904+seunexplicit@users.noreply.github.com>
Co-authored-by: seunexplicit <seunxplicit@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-15 07:20:04 +00:00
Alex van Andelandkodiakhq[bot] 909b37ed44 Implement getDefaultScheduleId to load the default schedule (#4483)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-15 11:37:58 +05:30
33e8b5d227 Availability / Single View (#4467)
* Revamped Schedule before type fixes

* Removed t('copy_all'); fake focus state on CopyButton open

* Added skeletons, availability single view (wip, still a bit glitchy)

* Fixed isDefault switch glitchiness

* Fixes changing the default schedule to the created schedule

* Fix some type errors, on create set availability to DEFAULT_SCHEDULE

* Provided missing translation for copy_times_to

* Fix type errors, this may not be possible until v8 RHF

* Fix lint error

* Some responsiveness fixes

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-15 05:49:59 +00:00
a3462657db feat: v2 embed (#4477)
* Add support for custom tabNameKey and use embedTabName as the key for embed to avoid conflict with event-types tabName property

* Fix tests

* Code cleanup

* feat: v2 embed

* fix: button black default, reuse horitzontalTabs v2

* fix: remove comment, remove linkProps from NavTabs v2

* fix: height: 98% to avoid overflow

* fix: add embed to event type detail page

* fix: add also tabNames embed-code embed-react

* fix: add tabNames w empty divs

* Update Embed component as per V2

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Agusti Fernandez Pardo <git@agusti.me>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-15 05:34:11 +00:00
alannncandkodiakhq[bot] 35c2f9046a fix/4128-reschedulebook-stops-creating-event-calendar (#4348)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-15 01:27:46 +00:00
2281c7cf9f Fix #4457 (remove event type action buttons) (#4472)
* Fix #4457 (remove event type action buttons)

* Configured backPath

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-09-14 22:11:31 +00:00
Alex van Andelandnafees nazik e29d9cd545 Bugfix/hide arrow button (#4481)
* feat: disable arrow buttons based on the position

* Instead of disable, hide

Co-authored-by: nafees nazik <nafeesnazik21@gmail.com>
2022-09-14 22:06:26 +00:00
Alex van Andel f09af2d393 Fixes broken tests due to accidental submit button (#4479) 2022-09-14 21:31:47 +00:00
Bailey Pumfleet 34c8affacd Booking item fixes (#4476) 2022-09-14 18:25:35 +00:00
Bailey Pumfleet d302dc164d Update booking page buttons to match v2 (#4474) 2022-09-14 17:56:54 +00:00
Bailey Pumfleet 34d417515c Remove duplicate unconfirmed label (#4473) 2022-09-14 17:41:46 +00:00
b1a1525471 fix: merge order of calendar freebusy (#4438)
Co-authored-by: chengcheng.frontend <chengcheng.frontend@bytedance.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 17:32:52 +00:00
Bailey Pumfleet 93b59d60c3 Show user avatar in new event type dropdown (#4471) 2022-09-14 17:00:21 +00:00
Bailey Pumfleet c3cb839027 Remove outline from user dropdown trigger (#4470) 2022-09-14 16:13:13 +00:00
91c39a0306 feat: Exchange with NTLM support (#4127)
* add exchange package

* fix conflicts

* add setup page for v2

* refactor setup page for v1

* return exchange error messages to user if applicable

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2022-09-14 16:09:00 +00:00
alannncandkodiakhq[bot] f1c15f4e8a 4382 onboarding fix the disabled state of the username in step 1 (#4451)
* Convert username availability premium to v2

* Update Username Availability to v2 design

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 15:57:20 +00:00
Alex van Andel 3f29df16ad KBar included @calcom/ui/v2/core -> Shell -> KBar -> Shell -> ... (#4469) 2022-09-14 15:40:56 +00:00
Syed Ali Shahbazandkodiakhq[bot] 3a06947c8a fixes active state of select options (#4456)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 13:44:42 +00:00
Nafees Nazikandkodiakhq[bot] f486b0242f fix: remove name prop from NumberWidget (#4346)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 11:58:59 +00:00
9bc2676822 remove "Weve changed billing for teams" banner in teams (#4432)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 11:41:47 +00:00
Nafees Nazikandkodiakhq[bot] 5c72aa3497 fix: add a consistent key to the day ranges list (#4401)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 17:00:25 +05:30
09da911bf9 Adds rescheduling dropdown to past unconfirmed bookings (#3726)
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 16:32:26 +05:30
2fbaec41c9 Refactor skeleton loader files to v2 folders (#4447)
* create v2 skeleton loaders

* add depreciation notice to old skeleton loader files

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 10:35:40 +00:00
Nafees Nazikandkodiakhq[bot] 3bada22900 feat: make toggle button default (#4437)
* feat: make switch default

* fix: refetch invalidated query

* React-query caching the previous isDefault state.

* fix: switch label color

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 10:24:41 +00:00
Hariom Balharaandkodiakhq[bot] 45059554b6 Fix deletion of route (#4466)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 10:10:52 +00:00
alannncandkodiakhq[bot] 8b69359ccb Fix styles and remove temporarily elements (#4448)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 09:55:49 +00:00
b457125915 fix: remove i18n from title and description (#4354)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2022-09-14 09:42:48 +00:00
01e0fed3c5 Updating language in settings needs a reload to be translated (#4431)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 09:28:23 +00:00
Noahandkodiakhq[bot] c09107cec8 Improve event type item description visibility (#4404)
* Make event type description more visible

* Delete unneeded import

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 09:08:50 +00:00
Hariom Balharaandkodiakhq[bot] 2f9905e548 Fix crash due to some props being undefined (#4427)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 08:46:42 +00:00
sean-brydonandkodiakhq[bot] ab4d910564 Fix input height 36px (#4425)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-14 08:18:40 +00:00
34bca4984f fix: Select dropdown styling (#4358)
* fix: select dropdown styling

* change check icon

* Revert select component, update v2 select

* Updates the background color to match figma design

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-09-14 07:27:00 +00:00
0907d154c7 feat: 4232 - new theme toggle design. (#4371)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2022-09-14 08:15:13 +01:00
sean-brydon 6f807930f6 Tidy up settings calendars (#4430) 2022-09-13 20:13:24 +00:00
sean-brydonandkodiakhq[bot] e34405046f Adds radix tailwind + dropdown focus style (#4424)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-13 19:18:29 +00:00
Nafees Nazik cefb46dbdd fix: add translation in getServerSideProps (#4390) 2022-09-13 14:07:10 -05:00
Carina WollendorferandCarinaWolli b22786d7df Fixing design issues /workflows {empty state} (#4446)
* fixes design issues in empty screen for worklfows

* fix font weight and add new text for examples

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-09-13 13:57:23 -05:00
Carina WollendorferandCarinaWolli 735254d48e fixing settings layout for teams (#4444)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-09-13 13:55:39 -04:00
404168fe7d Feat/3796 new UI for signin (#4369)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-13 16:35:14 +00:00
zomars 6c4a372121 Adds v2 launch instructions 2022-09-13 11:15:37 -05:00
zomars 3c60b4f569 V2 Layout fixes 2022-09-13 11:11:37 -05:00
sean-brydon 2292343db6 Tidyup app cards (#4428) 2022-09-13 14:14:27 +00:00
hexcowboy 5db525a62d dynamic import rainbow (only if installed) (#4409) 2022-09-13 12:59:53 +02:00
Syed Ali Shahbaz b2d3432438 Fix/daterangepicker css (#4418)
* added styles from external stylesheet

* fixed a border color

* codacy improvements

* more codacy fix

* codacy why u be so dumb
2022-09-13 09:32:27 +00:00
zomars 140bb3836a v1.9.5 2022-09-12 17:12:38 -05:00
hexcowboyandOmar López 6b2d675043 add rainbowkit form to v2 event-types (#4349)
Co-authored-by: Omar López <zomars@me.com>
2022-09-12 17:11:20 -05:00
7e917cdcbb V2 settings teams (Profil, Members, Appearance View) (#4350)
* add team profile

* first version for team members page

* finish up design of member list item

* fix dialog buttons

* add missing seats and upgrading information

* add v2 dialog for changing role

* finish basic version of member's schedule

* remove modalContainer

* design fixes team profile page

* show only team info to non admins

* allow all member to check availabilities

* make time available heading sticky

* add dropdown for mobile view

* create team appearance view

* finish appearance page

* use settings layout and add danger zone for member

* add fallback logo

* Add teams to sidebar and fix UI

* add team invitations

* Clean up

* code clean up

* add impersontation and disable autofocus on calendar

* improve team info

* refactor teaminvitelist code and fix leaving a team

* add team pages to settings shell

* add link to create new team

* small fixes

* clean up comments

* V2 Multi-select (Team Select) (#4324)

* --init

* design improved

* further fine tuning

* more fixes

* removed extra JSX tag

* added story

* NIT

* revert to use of CheckedTeamSelect

* Removes comments

Co-authored-by: Peer Richelsen <peeroke@gmail.com>

* fix: toggle alligment (#4361)

* fix: add checked tranform for switch (#4357)

* fixed input size on mobile, fixed settings (#4360)

* fix image uploader button in safari

* code clean up

* fixing type errors

* Moved v2 team components to features

Adds deprecation notices

* Update SettingsLayout.tsx

* Migrated to features and build fixes

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2022-09-12 17:04:33 -05:00
50f63ef000 Adds skeleton loader to workflows (#4402)
* fix event types skeleton loader

* fix margin in event types

* fix skeleton loader in bookings

* add skeleton loader for workflow list

* add skeleton loader for editing page

* fix border radius of skeleton loader

* fix mobile view workflow list

* add mobile view for skeleton list

* make multi select full with in mobile view

* mobile view for edit skeleton loader

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-12 16:51:36 -05:00
Udit Takkar 1deca816bd Feat/team owner booking (#3999)
* WIP: testing queries

* feat: add badge

* fix: get only id

* refactor: get bookings query

* WIP: display attendees added

* fix: add type
2022-09-12 15:41:59 -05:00
7e52e3d295 V2 Settings Shell - Add Teams Section & UI fixes (#4347)
* Add teams to sidebar and fix UI

* Clean up

* V2 Multi-select (Team Select) (#4324)

* --init

* design improved

* further fine tuning

* more fixes

* removed extra JSX tag

* added story

* NIT

* revert to use of CheckedTeamSelect

* Removes comments

Co-authored-by: Peer Richelsen <peeroke@gmail.com>

* fix: toggle alligment (#4361)

* fix: add checked tranform for switch (#4357)

* fixed input size on mobile, fixed settings (#4360)

* fixing type errors

* Mobile fixes

* Tests fixes

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2022-09-12 20:26:42 +00:00
+7 ded1fce6fa V2 settings - developer view (#4085)
* Fix breadcrumb colors

* HorizontalTabs

* Team List Item WIP

* Horizontal Tabs

* Cards

* Remove team list item WIP

* Login Page

* Add welcome back i118n

* EventType page work

* Update EventType Icons

* WIP Availability

* Horizontal Tab Work

* Add build command for in root

* Update build DIr/command

* Add Edit Button + change buttons to v2

* Availablitiy page

* Fix IPAD

* Make mobile look a little nicer

* WIP bookingshell

* Remove list items from breaking build

* Mian bulk of Booking Page.

* Few updates to components

* Fix chormatic feedback

* Fix banner

* Fix Empty Screen

* Text area + embded window fixes

* Semi fix avatar

* Troubleshoot container + Active on count

* Improve mobile

* NITS

* Fix padding on input

* Fix icons

* Starting to move event types settings to tabs

* Begin migration to single page form

* Single page tabs

* Limits Page

* Advanced tab

* Add RHF to dependancies

* Most of advanced tab

* Solved RHF mismtach

* Build fixes

* RHF conditionals fixes

* Improved legibility

* Major refactor/organisation into optional V2 UI

* Portal EditLocationModal

* Fix dialoug form

* Update imports

* Auto Animate + custom inputs WIP

* Custom Inputs

* WIP Apps

* Fixing stories imports

* Stripe app

* Remove duplicate dialog

* Remove duplicate dialog

* Fix embed URL

* Fix app toggles + number of active apps

* Fix container padding on disabledBorder prop

* Removes strict

* EventType Team page WIP

* Fix embed

* NIT

* Add Darkmode gray color

* V2 Shell WIP

* Create my account folder

* Add profile section

* Fix headings on shell V2

* Fix mobile layout with V2 shell

* V2 create event type button

* Checked Team Select

* Hidden to happen on save - not on toggle

* Team Attendee Select animation

* WIP

* Fix scheduling type and remove multi select label

* Fix overflow on teams url

* Finish profile fields

* Show toast on success

* General tab WIP

* Even Type move order handles

* Add switching of destination calendar

* List calendar and delete

* Render empty screenwhen no calendars

* Fix Embed TS errors

* Fix TS errors

* Fix Eslint errors

* Fix TS errors for UI

* Fix ESLINT error

* added SidebarCard for promo to v2 and storybook (#3906)

Co-authored-by: Julian Benegas <julianbenegas99@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Tooltip Provider - Wrapper due to dep upgrade

* public event type list darkmode

* V2 Color changes to public booking

* Remove unused component

* Fix typecheck

* Transfer to SSR

* Appearance screen made

* V2 image uploader

* WIP appearance page

* Remove unnecessary data from viewer.me

* Add profile translations

* Add translations to general page

* Add calendar switch

* Add calendar switch

* Add translations to appearance page

* Clean up conferencing page

* Clean up appearance page

* Draft shell on md screen

* fixed slots availability by fixing buffertime (#3756)

Co-authored-by: Peer Richelsen <peeroke@gmail.com>

* Hidding import features from other services (#3970)

* Update PULL_REQUEST_TEMPLATE.md

* Fixes CLS when loading Avatars (#3973)

* Fixes CLS when loading Avatars

* Update packages/ui/v2/core/Avatar.tsx

Co-authored-by: Leo Giovanetti <hello@leog.me>

Co-authored-by: Leo Giovanetti <hello@leog.me>

* New Crowdin translations by Github Action (#3954)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>

* Adding continue button to connectCalendar on getting-started view  (#3971)

* Adding continue button to connectCalendar view getting-started

* Fixing extra space

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>

* 2.0 Settings / My Account {View} (#3874)

* Fix breadcrumb colors

* HorizontalTabs

* Team List Item WIP

* Horizontal Tabs

* Cards

* Remove team list item WIP

* Login Page

* Add welcome back i118n

* EventType page work

* Update EventType Icons

* WIP Availability

* Horizontal Tab Work

* Add build command for in root

* Update build DIr/command

* Add Edit Button + change buttons to v2

* Availablitiy page

* Fix IPAD

* Make mobile look a little nicer

* WIP bookingshell

* Remove list items from breaking build

* Mian bulk of Booking Page.

* Few updates to components

* Fix chormatic feedback

* Fix banner

* Fix Empty Screen

* Text area + embded window fixes

* Semi fix avatar

* Troubleshoot container + Active on count

* Improve mobile

* NITS

* Fix padding on input

* Fix icons

* Starting to move event types settings to tabs

* Begin migration to single page form

* Single page tabs

* Limits Page

* Advanced tab

* Add RHF to dependancies

* Most of advanced tab

* Solved RHF mismtach

* Build fixes

* RHF conditionals fixes

* Improved legibility

* Major refactor/organisation into optional V2 UI

* Portal EditLocationModal

* Fix dialoug form

* Update imports

* Auto Animate + custom inputs WIP

* Custom Inputs

* WIP Apps

* Fixing stories imports

* Stripe app

* Remove duplicate dialog

* Remove duplicate dialog

* Fix embed URL

* Fix app toggles + number of active apps

* Fix container padding on disabledBorder prop

* Removes strict

* EventType Team page WIP

* Fix embed

* NIT

* Add Darkmode gray color

* V2 Shell WIP

* Create my account folder

* Add profile section

* Fix headings on shell V2

* Fix mobile layout with V2 shell

* V2 create event type button

* Checked Team Select

* Hidden to happen on save - not on toggle

* Team Attendee Select animation

* WIP

* Fix scheduling type and remove multi select label

* Fix overflow on teams url

* Finish profile fields

* Show toast on success

* General tab WIP

* Even Type move order handles

* Add switching of destination calendar

* List calendar and delete

* Render empty screenwhen no calendars

* Fix Embed TS errors

* Fix TS errors

* Fix Eslint errors

* Fix TS errors for UI

* Fix ESLINT error

* added SidebarCard for promo to v2 and storybook (#3906)

Co-authored-by: Julian Benegas <julianbenegas99@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Tooltip Provider - Wrapper due to dep upgrade

* public event type list darkmode

* V2 Color changes to public booking

* Remove unused component

* Fix typecheck

* Transfer to SSR

* Appearance screen made

* V2 image uploader

* WIP appearance page

* Remove unnecessary data from viewer.me

* Add profile translations

* Add translations to general page

* Add calendar switch

* Add calendar switch

* Add translations to appearance page

* Clean up conferencing page

* Settings sidebar fixes

* Updates middleware

* Update SettingsLayout.tsx

* Settings layout improvements

* Type fix

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Julian Benegas <julianbenegas99@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* AppStore CLI: Making video app creation a breeze with major cleanup of locations code throughout (#3825)

* Fix breadcrumb colors

* HorizontalTabs

* Team List Item WIP

* Horizontal Tabs

* Cards

* Remove team list item WIP

* Login Page

* Add welcome back i118n

* EventType page work

* Update EventType Icons

* WIP Availability

* Horizontal Tab Work

* Add build command for in root

* Update build DIr/command

* Add Edit Button + change buttons to v2

* Availablitiy page

* Fix IPAD

* Make mobile look a little nicer

* WIP bookingshell

* Remove list items from breaking build

* Add Embed ModalBox for routing forms

* Mian bulk of Booking Page.

* Few updates to components

* Fix chormatic feedback

* Add duplicate form support

* Fix duplication logic

* Change to feathericons everywhere and other fixes

* Dont allow routes for fallback route

* Fix banner

* Fix Empty Screen

* Text area + embded window fixes

* Semi fix avatar

* Fix all TS issues

* Fix tests

* Troubleshoot container + Active on count

* Support routing using query params

* Improve mobile

* NITS

* Fix padding on input

* Support multiselect in router endpoint

* Fix the issue where app goes in embed mode after viewing embed once

* Fix icons

* Add router url tests

* Add Responses download and form toggling tests

* Add required validation test

* Change Icons everywhere

* App typeform app

* Improvements in cli

* Starting to move event types settings to tabs

* Begin migration to single page form

* Single page tabs

* Limits Page

* Advanced tab

* Add RHF to dependancies

* Add typeform how-to-use page

* Add typeform how-to-use page and screenshots

* Most of advanced tab

* Solved RHF mismtach

* Build fixes

* RHF conditionals fixes

* Improved legibility

* Fix TS error

* Add missing image

* Update CliApp.tsx

* Major refactor/organisation into optional V2 UI

* Portal EditLocationModal

* Fix dialoug form

* Update imports

* Auto Animate + custom inputs WIP

* Custom Inputs

* WIP Apps

* Fixing stories imports

* Stripe app

* Remove duplicate dialog

* Remove duplicate dialog

* Major locations cleanup, 10s of bug fixes and app-store improvements

* Fix missing pieces

* More fixes

* Fix embed URL

* Fix app toggles + number of active apps

* Fix container padding on disabledBorder prop

* Removes strict

* more fixes

* EventType Team page WIP

* Fix embed

* NIT

* Add Darkmode gray color

* V2 Shell WIP

* Fix headings on shell V2

* Fix mobile layout with V2 shell

* V2 create event type button

* Checked Team Select

* Hidden to happen on save - not on toggle

* Team Attendee Select animation

* Fix scheduling type and remove multi select label

* Fix overflow on teams url

* Revert console

* Revert api

* Fix Embed TS errors

* Fix TS errors

* Fix Eslint errors

* Fix TS errors for UI

* Fix ESLINT error

* Fix TS errors

* Add missing import

* Fix CLI

* Add a default placeholder

* Remove hardcoded daily:integrations

* Fix message for payment page

* Revert api and console to main

* Update README

* Fix TS errors

* Fix Lint warnings

* Fix Tests

* Fix conflict issues

* Fix conflict issues

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>

* Button to test a workflow action (#3873)

* add Test action button + UI improvements

* add test action functionality

* add confirmation dialog before sending SMS

* code clean up

* show error message if test action fails

* disable test action button in edit mode

* fixes SMS testing

* use updated values

* fix wrongly updated data in useEffect

* fix typo

* code clean up

* fix UI issue in mobile view

* small design fix

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>

* Improve CLI App Help Text (#3982)

* Adds deprecation notice to QueryCell (#3977)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* added campfire.to app (#3984)

* fixed file size campfire (#3985)

* added campfire.to app

* fixed file size

* Draft shell on md screen

* Draft small settings bar

* Add padding to dropdown menu

* WIP

* Create mobile settings nav

* Fix conferencing render

* Only display mobile settings nav

* Clean up my account section

* Clean up

* Remove old draft shell

* Remove old draft shell

* Implement changes from #3817

* WIP

* Create webhooks page

* WIP Webhook form

* WIP

* WIP Webhook

* WIP

* Create edit webhook page

* Edit webhooks

* Edit API keys

* Update API key name

* Create embeds page

* Fix type errors

* Remove embeds page

* Improves Zapier response and payload (#3892)

* improve listBookings API call

* improve payload for zapier call

* use more readable location for integrations

* remove cancellation reason

* convert location to readbale string

* code clean up

* code clean up

* updated expected webhook response for e2e tests

* fix failing e2e

* revert webhook response

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* fixed dark mode team page (#4315)

* Checkly formatting

* Move hidden label to toggle on events page (#4320)

* Add events for routing forms

* Bring shell changes from main

* Clean up

* WIP

* V2 Multi-select (Team Select) (#4324)

* --init

* design improved

* further fine tuning

* more fixes

* removed extra JSX tag

* added story

* NIT

* revert to use of CheckedTeamSelect

* Removes comments

Co-authored-by: Peer Richelsen <peeroke@gmail.com>

* fix: toggle alligment (#4361)

* fix: add checked tranform for switch (#4357)

* fixed input size on mobile, fixed settings (#4360)

* fixed input size on mobile, fixed settings (#4360)

* Fix merge errors

* Fix API key page

* Fix type error

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Julian Benegas <julianbenegas99@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: Kszemi <mkrzemien11@gmail.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>
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: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Noah <hi@noahflk.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2022-09-12 14:07:52 -05:00
sean-brydon 27f58c70d3 Fix px on suffix/prefix (#4395) 2022-09-12 13:43:09 +00:00
Udit Takkar 869ebb092b fix: add fit to height (#4372) 2022-09-12 13:30:05 +00:00
sean-brydon 5f3e9efa6d Oooops (#4386) 2022-09-12 10:55:41 +00:00
Carina Wollendorfer 078ca46a28 Fixes design issues in settings/appearance (#4375) 2022-09-12 10:27:35 +00:00
sean-brydon ef7863ad0b Simple animations (#4378) 2022-09-12 10:15:30 +00:00
Syed Ali Shahbazandkodiakhq[bot] 9557c1818b Fixes Dynamic Link description Logic (#4351)
* fixes logic

* fixes description

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-12 15:34:42 +05:30
20fd45a3fd V2 password reset design (#4327)
* using new v2 buttons in password reset

* Forgot password UI

* Logo updates

* change error copy

* Fix tests

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[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>
2022-09-12 09:25:54 +00:00
Carina Wollendorfer e6849174c1 Fixes security vulnerability in workflows (#4376) 2022-09-12 09:15:58 +02:00
sean-brydon a9b79eafb6 Add automanimate page transitions (#4368) 2022-09-11 21:54:05 +02:00
Peer Richelsen e50bc0f198 fixed input size on mobile, fixed settings (#4360) 2022-09-11 09:09:23 +00:00
Udit Takkar 63aae60af4 fix: add checked tranform for switch (#4357) 2022-09-10 17:52:35 +00:00
Udit Takkar c215f7de03 fix: toggle alligment (#4361) 2022-09-10 19:34:38 +02:00
Syed Ali ShahbazandPeer Richelsen 3ce687c0a4 V2 Multi-select (Team Select) (#4324)
* --init

* design improved

* further fine tuning

* more fixes

* removed extra JSX tag

* added story

* NIT

* revert to use of CheckedTeamSelect

* Removes comments

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-10 09:57:55 +00:00
20e9e9763a HOTFIX: Removes disabled state from reminder phone input (reschedule) (#4322)
* removes disabled state from reminder phone

* prefill phone number for sms reminder

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2022-09-09 15:01:37 -04:00
Peer RichelsenandAlex van Andel 5cd2ce9246 a ton of mobile improvements to save space (#4336)
* made /booking more responsive friendly

* hiding headline and description on mobile, moving CTA to bottom right fixed on mobile

* more padding on full screen mode on iPhone

* nit fixes to wipemycal

* nit fixes to wipemycal

* lint fix

* Update apps/web/styles/globals.css

Massive nit, lol.

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-09 15:02:31 +00:00
770c35039d Feature/add availability tab (#4314)
* Add Availability Tab

* Some i18n fixups

* Update apps/web/components/v2/eventtype/AvailabilityTab.tsx

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

* Update apps/web/components/v2/eventtype/AvailabilityTab.tsx

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

* Update apps/web/components/v2/eventtype/AvailabilityTab.tsx

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

* Update apps/web/components/v2/eventtype/AvailabilityTab.tsx

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

* Fixed lint

* Oops

* Use classnames for SkeletonText instead of width,height

* Ditches QueryCell

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-09 15:47:17 +01:00
0cab371db4 Replace AdminLayout with SettingsLayout (#4311)
* Replace AdminLayout with SettingsLayout

* Revert "Replace AdminLayout with SettingsLayout"

This reverts commit cc30ddcb6aa7a61157d53e46d60054f1cc565796.

* Revert to changes

* Add back changes

* Few nits + translate language to each local language

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-09 12:57:31 +00:00
Alex van Andel 15a0216cc5 Removed deprecations (#4335) 2022-09-09 12:24:26 +00:00
Peer Richelsenandsean-brydon a385a4bb38 minor changes to routing forms (#4333)
* added new combined buttons on all pages

* fixed required label in routing forms

* i18n for routing forms, minor changes to mobile routing forms action items

* full width for routing forms descriptin on mobile

* i18n fixes

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-09-09 11:45:20 +00:00
Alex van Andel 5965129851 Checking if serial fixes the issue (not conflicting with any others) (#4331)
* Checking if serial fixes the issue (not conflicting with any others)

* Remove redundant check
2022-09-09 11:31:55 +00:00
sean-brydonandPeer Richelsen 3e92571078 Add rounded-none to combined buttons by default (#4330)
* Add rounded-none to combined buttons by default

* Remove combined

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-09 11:18:15 +00:00
Peer Richelsenandsean-brydon 83f0ba7ba4 added new combined buttons on all pages (#4328)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-09-09 10:53:02 +00:00
b0de653166 fix/onboarding-no-more-flaky-tests (#4254)
* Change expects to don't wait for plain text

* Fix change password test flakiness

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-09 08:54:10 +00:00
Syed Ali Shahbazandkodiakhq[bot] 1c46370572 dynamic link description on availability page (#4323)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-09 08:40:58 +00:00
Omar Lópezandsean-brydon 99f11dcc5c Removes dynamic dimensions for V2 Skeletons in favor of classnames (#4263)
* WIP

* Removes dynamic width in favor of classnames

* Update useLocale.ts

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-09-09 08:22:39 +00:00
d7e523d365 Change flex to inline-flex (#4317)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-09 08:00:54 +00:00
Peer Richelsen 342c281f3c fix color for calcom logo on /video (#4326)
* fixed daily video logo

* nit
2022-09-09 09:44:25 +02:00
d0225824fb Hotfix Lark Get Times (#4321)
* Removed disconnected calendar notice

* fixes dark mode piblic page (#4201) (#4203)

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

* fix cookie name (#4295)

* fix: lark get free busy param error (#4285)

Co-authored-by: chengcheng.frontend <chengcheng.frontend@bytedance.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>

* Revert "Removed disconnected calendar notice"

This reverts commit 638600a5b716aa989549ed8b103b35b185ecbdd6.

* Revert "fix cookie name (#4295)"

This reverts commit 1aefa0efb3986c44bc5faaf6e0765580cdaeb564.

Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Cheng CHENG <c.cheng@live.cn>
Co-authored-by: chengcheng.frontend <chengcheng.frontend@bytedance.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-09 00:43:53 +00:00
Noah 79fd1bd76f Move hidden label to toggle on events page (#4320) 2022-09-08 16:26:51 -06:00
zomars 293c2ffb86 Checkly formatting 2022-09-08 16:04:40 -06:00
Peer Richelsen 4162d403e1 fixed dark mode team page (#4315) 2022-09-08 21:33:19 +00:00
bb24539731 Improves Zapier response and payload (#3892)
* improve listBookings API call

* improve payload for zapier call

* use more readable location for integrations

* remove cancellation reason

* convert location to readbale string

* code clean up

* code clean up

* updated expected webhook response for e2e tests

* fix failing e2e

* revert webhook response

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-08 13:18:00 -06:00
sean-brydonandPeer Richelsen 0b8eb3b8ec Modal updates (#4289)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-08 19:40:09 +02:00
38ccd7b159 fix: lark get free busy param error (#4285)
Co-authored-by: chengcheng.frontend <chengcheng.frontend@bytedance.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-08 19:39:23 +02:00
4aa9c830c1 feat: add a link tag (#4292)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-08 19:38:36 +02:00
0c2a25fb78 fix: toggle alignment, spacing, square btn (#4282)
* fix: toggle alignment, spacing, square btn

* fix: toggle alignment

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-08 19:38:11 +02:00
dce7973825 Fix UI issues (#4300)
* fix: increase font size to 14 px

* fix: double border issue

* fix: destructive button looks off from figma

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-08 19:37:26 +02:00
Peer Richelsenandkodiakhq[bot] 4771a1a64a first iteration of fixing dark mode, <Select> is missing (#4304)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-08 19:35:32 +02:00
Peer Richelsenandkodiakhq[bot] bb7087640d removed <hr> from event type apps (#4297)
* removed <hr> from event type apps, app settings need to be extracted into /packages/app-store/

* nit

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-08 19:34:49 +02:00
Peer Richelsenandkodiakhq[bot] d7ec335a34 added padding top for settings on desktop (#4280)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-08 19:34:13 +02:00
zomars 54f1eef308 Adds concurrency to workflows 2022-09-08 11:01:47 -06:00
Peer Richelsenandkodiakhq[bot] f1b95cd28d added n8n to app store (#4291)
* added n8n app to app store, gave padding to sticky app store header

* nit

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-08 18:34:01 +02:00
alannnc 6ee878aab0 fix cookie name (#4294) 2022-09-08 10:12:56 -06:00
1bb987556d Impersonate UI (#4243)
* Impersonate UI

* Depracates Old admin required

* Move permission container to features

* Remove logic from dumb components

* Fix export from v2

* Fix type cast - add tab filter

* Redirect on authed pages

* Add key back

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2022-09-08 15:35:31 +00:00
github-actions[bot]andCrowdin Bot 306a1212cf New Crowdin translations by Github Action (#4279)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-09-08 16:58:08 +02:00
Peer Richelsen f7119849db addded padding to sticky nav, temp removed TODAY from /booking (#4278) 2022-09-08 14:34:37 +00:00
Alex van AndelandPeer Richelsen a8c1851c04 Fixes button styles on /event-types and /availability (#4281)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-08 14:21:46 +00:00
3c8cc4e624 fix: use flex (#4276)
* fix: use flex

* Update packages/ui/v2/core/Dialog.tsx

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

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peer@cal.com>
2022-09-08 13:22:09 +02:00
github-actions[bot]andCrowdin Bot 6958c710cf New Crowdin translations by Github Action (#4264)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-09-08 11:46:52 +02:00
38bd111260 fixes toggle (#4253)
* fixes toggle

* fix: remove absolute from recurring event div

Co-authored-by: Agusti Fernandez Pardo <git@agusti.me>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-08 11:42:46 +02:00
github-actions[bot]andCrowdin Bot 290fb8db55 New Crowdin translations by Github Action (#4235)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-09-08 10:53:11 +02:00
zomars 3e0f14ea29 Removed disconnected calendar notice 2022-09-07 19:10:24 -06:00
zomars 984281963e v1.9.4 2022-09-07 19:03:31 -06:00
zomars ec8be2994c Submodule sync 2022-09-07 19:03:14 -06:00
zomars 8996a2aa8e Adds opt-in banner to mobile "more" menu 2022-09-07 19:02:24 -06:00
4856ed9977 Signup Flow improvements (#4012)
* Get login working

* Update website

* Fixes

* Save

* svae

* Save

* Change translation key

* Various fixes after testing

* Update website

* Add TS Tests

* Upate website

* Fix tests

* Fix linting and other issues

* Fix linting and other issues

* Fix bugs found during recording of demos

* Revert default coookie change

* Self review fixe

* Link fixes

* Removed inline styles, cleanup

* Various fixes

* Added new envs to e2e

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2022-09-07 18:38:37 -06:00
Alex van Andel d36d925788 Feature/goal reduce flaky tests (#4247)
* Wait for current_password input to be visible

* Login flow remove duplication + small fix change-password

* Made hash-my-url more robust

* Wait until locale is ready to resolve test flackiness

* Added waitForSelector before trying to fill inputs
2022-09-07 17:29:24 -06:00
NoahandAlex van Andel de54d75cd3 Add delete tooltip to event detail (#4259)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-07 23:20:08 +00:00
NoahandAlex van Andel 70ac949bea Move hidden label to toggle on events page (#4261)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-09-07 22:40:50 +00:00
34408c5593 Add calendar source to busy blocks (#3074)
* Add calendar source to busy blocks

* Update troubleshoot.tsx

* Type fixes

* Adds sources to availablity

# Conflicts:
#	apps/web/pages/availability/troubleshoot.tsx

* Update troubleshoot v1 and v2 with source element

* Type fixes

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Alan <alannnc@gmail.com>
2022-09-07 13:28:43 -06:00
Joe Au-Yeung 7da272f67a Prevent saving error responses from Zoom to user credentials (#3743) 2022-09-07 18:18:11 +00:00
sean-brydonandPeer Richelsen 46e46bc1de Fixing spacing on event details (#4238)
* Fixing spacing on event details

* Shell pb 40px

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-07 16:28:58 +00:00
Peer Richelsen 7b36df9eb3 added raycast to app store (#4231) 2022-09-07 15:33:27 +00:00
Peer Richelsen b4d1cb0988 added desktop app layout for v2.0, changed 2.0 banner (#4242) 2022-09-07 09:01:33 -06:00
Carina WollendorferandCarinaWolli e52af8d387 fix design (#4226)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-09-07 14:05:25 +00:00
Peer Richelsen e9b6e5144a temp disabled onboarding test due to flakiness (#4236) 2022-09-07 13:25:11 +00:00
0c6c23e1b0 New Crowdin translations by Github Action (#4225)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-07 14:49:53 +02:00
Peer Richelsen 458406feaf fixed booking navbar, fixed bookings buttons, fixed /success dark mode (#4230) 2022-09-07 14:49:30 +02:00
Peer Richelsen b9fc3ac076 Update README.md 2022-09-07 13:08:06 +02:00
sean-brydon 00f5ba2ac5 Rounded divider + Fixing button group (#4233) 2022-09-07 11:04:12 +00:00
Syed Ali Shahbaz f1436da96c fixes overflow rounded border (#4227) 2022-09-07 09:26:22 +02:00
30f8d12563 added /more page for mobile (#4219)
* added /more page for mobile

* i18n

* Navigation improvements

* Reverting unused exports

* Improved performance for getting mobile/desktop items

* I take your forEach and up it with reduce

Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-06 22:13:31 -06:00
zomars 1561224dbb Type fixes 2022-09-06 21:25:17 -06:00
f3bedabf92 Fix/styles getting started v2 (#4224)
* Fixes spacings style

* Fix spacings

* Update test

* Fix router push when image upload is success, also typo

* Fix styles for mobile

* Sumodule sync

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2022-09-06 19:34:33 -06:00
58b3067b2b Hotfix Settings Shell (#4222)
* Add settings section prop to shell

* Clean up

* Adjustments

* Update Shell.tsx

* Update SettingsLayout.tsx

* Mobile drawer fixes

* Update VerticalTabItem.tsx

Co-authored-by: zomars <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-09-06 19:33:50 -06:00
zomars 6a6adf1c3c Submodule sync 2022-09-06 19:28:00 -06:00
zomars 2027447eab Takes you back to referer page on v2 opt-in 2022-09-06 19:18:47 -06:00
13c2dc24dc Feature/new onboarding page (#3377)
* [WIP] New design and components for onboarding page

* saving work in progress

* new fonts

* [WIP] new onboarding page, initial page, components

* WIP calendar connect

* WIP availability new design

* WIP onboarding page

* WIP onboarding, working new availability form

* WIP AvailabilitySchedule componente v2

* WIP availability with defaultSchedule

* User profile view

* Relocate new onboarding/getting-started page components

* Steps test for onboarding v2

* Remove logs and unused code

* remove any as types

* Adding translations

* Fixes translation text and css for step 4

* Deprecation note for old-getting-started

* Added defaul events and refetch user query when finishing getting-started

* Fix button text translation

* Undo schedule v1 changes

* Fix calendar switches state

* Add cookie to save return-to when connecting calendar

* Change useTranslation for useLocale instead

* Change test to work with data-testid instead of hardcoded plain text due to translation

* Fix skeleton containers for calendars

* Style fixes

* fix styles to match v2

* Fix styles and props types to match v2 design

* Bugfix/router and console errors (#4206)

* The loading={boolean} parameter is required, so this must be <Button />

* Fixes duplicate key error

* Use zod and router.query.step directly to power state machine

* use ul>li & divide for borders

* Update apps/web/components/getting-started/steps-views/ConnectCalendars.tsx

Co-authored-by: alannnc <alannnc@gmail.com>

* Linting

* Deprecation notices and type fixes

* Update CreateEventsOnCalendarSelect.tsx

* Type fixes

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: zomars <zomars@me.com>
2022-09-06 16:58:16 -06:00
7f2db86a83 updated workflows empty screen (#4218)
* updated workflows empty screen

* fix missing title

* small design fixes for cards

* add ... if workflow example exceeds 2 lines

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2022-09-06 21:30:03 +00:00
91bc6ae820 V1->V2 connect calendar (#3961)
* V1->V2 connect calendar

* Fix imports

* Fix types

* Implementing v1 NoCalendarConnectedAlert changes on v2 component

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
2022-09-06 20:45:46 +00:00
Peer Richelsen 008706e5ac fixed i18n 2022-09-06 21:42:34 +02:00
zomars dad2fd098e Partial-Revert "V2/settings shell (#3987)"
This reverts commit 3a35eb413f.
2022-09-06 13:34:58 -06:00
Peer Richelsen c0b83c1df6 changed all new buttons, added i18n for routing forms (#4217) 2022-09-06 13:13:20 -06:00
Peer Richelsen 851fb25430 fixed height of recurring event select (#4213) 2022-09-06 13:01:05 -06:00
github-actions[bot]andCrowdin Bot 00d50f0285 New Crowdin translations by Github Action (#4214)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-09-06 20:55:34 +02:00
Peer Richelsen efc0bf6a5b event types new dropdown misaligned (#4216)
* fixed event type creation dropdown

* nit
2022-09-06 12:51:11 -06:00
Alex van AndelandPeer Richelsen dd9d7ab485 Added timeZone to v2, bugfix availabilityAsString (#4210)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-06 12:34:29 -06:00
+4 3a35eb413f V2/settings shell (#3987)
* Fix breadcrumb colors

* HorizontalTabs

* Team List Item WIP

* Horizontal Tabs

* Cards

* Remove team list item WIP

* Login Page

* Add welcome back i118n

* EventType page work

* Update EventType Icons

* WIP Availability

* Horizontal Tab Work

* Add build command for in root

* Update build DIr/command

* Add Edit Button + change buttons to v2

* Availablitiy page

* Fix IPAD

* Make mobile look a little nicer

* WIP bookingshell

* Remove list items from breaking build

* Mian bulk of Booking Page.

* Few updates to components

* Fix chormatic feedback

* Fix banner

* Fix Empty Screen

* Text area + embded window fixes

* Semi fix avatar

* Troubleshoot container + Active on count

* Improve mobile

* NITS

* Fix padding on input

* Fix icons

* Starting to move event types settings to tabs

* Begin migration to single page form

* Single page tabs

* Limits Page

* Advanced tab

* Add RHF to dependancies

* Most of advanced tab

* Solved RHF mismtach

* Build fixes

* RHF conditionals fixes

* Improved legibility

* Major refactor/organisation into optional V2 UI

* Portal EditLocationModal

* Fix dialoug form

* Update imports

* Auto Animate + custom inputs WIP

* Custom Inputs

* WIP Apps

* Fixing stories imports

* Stripe app

* Remove duplicate dialog

* Remove duplicate dialog

* Fix embed URL

* Fix app toggles + number of active apps

* Fix container padding on disabledBorder prop

* Removes strict

* EventType Team page WIP

* Fix embed

* NIT

* Add Darkmode gray color

* V2 Shell WIP

* Create my account folder

* Add profile section

* Fix headings on shell V2

* Fix mobile layout with V2 shell

* V2 create event type button

* Checked Team Select

* Hidden to happen on save - not on toggle

* Team Attendee Select animation

* WIP

* Fix scheduling type and remove multi select label

* Fix overflow on teams url

* Finish profile fields

* Show toast on success

* General tab WIP

* Even Type move order handles

* Add switching of destination calendar

* List calendar and delete

* Render empty screenwhen no calendars

* Fix Embed TS errors

* Fix TS errors

* Fix Eslint errors

* Fix TS errors for UI

* Fix ESLINT error

* added SidebarCard for promo to v2 and storybook (#3906)

Co-authored-by: Julian Benegas <julianbenegas99@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Tooltip Provider - Wrapper due to dep upgrade

* public event type list darkmode

* V2 Color changes to public booking

* Remove unused component

* Fix typecheck

* Transfer to SSR

* Appearance screen made

* V2 image uploader

* WIP appearance page

* Remove unnecessary data from viewer.me

* Add profile translations

* Add translations to general page

* Add calendar switch

* Add calendar switch

* Add translations to appearance page

* Clean up conferencing page

* Clean up appearance page

* Draft shell on md screen

* fixed slots availability by fixing buffertime (#3756)

Co-authored-by: Peer Richelsen <peeroke@gmail.com>

* Hidding import features from other services (#3970)

* Update PULL_REQUEST_TEMPLATE.md

* Fixes CLS when loading Avatars (#3973)

* Fixes CLS when loading Avatars

* Update packages/ui/v2/core/Avatar.tsx

Co-authored-by: Leo Giovanetti <hello@leog.me>

Co-authored-by: Leo Giovanetti <hello@leog.me>

* New Crowdin translations by Github Action (#3954)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>

* Adding continue button to connectCalendar on getting-started view  (#3971)

* Adding continue button to connectCalendar view getting-started

* Fixing extra space

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>

* 2.0 Settings / My Account {View} (#3874)

* Fix breadcrumb colors

* HorizontalTabs

* Team List Item WIP

* Horizontal Tabs

* Cards

* Remove team list item WIP

* Login Page

* Add welcome back i118n

* EventType page work

* Update EventType Icons

* WIP Availability

* Horizontal Tab Work

* Add build command for in root

* Update build DIr/command

* Add Edit Button + change buttons to v2

* Availablitiy page

* Fix IPAD

* Make mobile look a little nicer

* WIP bookingshell

* Remove list items from breaking build

* Mian bulk of Booking Page.

* Few updates to components

* Fix chormatic feedback

* Fix banner

* Fix Empty Screen

* Text area + embded window fixes

* Semi fix avatar

* Troubleshoot container + Active on count

* Improve mobile

* NITS

* Fix padding on input

* Fix icons

* Starting to move event types settings to tabs

* Begin migration to single page form

* Single page tabs

* Limits Page

* Advanced tab

* Add RHF to dependancies

* Most of advanced tab

* Solved RHF mismtach

* Build fixes

* RHF conditionals fixes

* Improved legibility

* Major refactor/organisation into optional V2 UI

* Portal EditLocationModal

* Fix dialoug form

* Update imports

* Auto Animate + custom inputs WIP

* Custom Inputs

* WIP Apps

* Fixing stories imports

* Stripe app

* Remove duplicate dialog

* Remove duplicate dialog

* Fix embed URL

* Fix app toggles + number of active apps

* Fix container padding on disabledBorder prop

* Removes strict

* EventType Team page WIP

* Fix embed

* NIT

* Add Darkmode gray color

* V2 Shell WIP

* Create my account folder

* Add profile section

* Fix headings on shell V2

* Fix mobile layout with V2 shell

* V2 create event type button

* Checked Team Select

* Hidden to happen on save - not on toggle

* Team Attendee Select animation

* WIP

* Fix scheduling type and remove multi select label

* Fix overflow on teams url

* Finish profile fields

* Show toast on success

* General tab WIP

* Even Type move order handles

* Add switching of destination calendar

* List calendar and delete

* Render empty screenwhen no calendars

* Fix Embed TS errors

* Fix TS errors

* Fix Eslint errors

* Fix TS errors for UI

* Fix ESLINT error

* added SidebarCard for promo to v2 and storybook (#3906)

Co-authored-by: Julian Benegas <julianbenegas99@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* Tooltip Provider - Wrapper due to dep upgrade

* public event type list darkmode

* V2 Color changes to public booking

* Remove unused component

* Fix typecheck

* Transfer to SSR

* Appearance screen made

* V2 image uploader

* WIP appearance page

* Remove unnecessary data from viewer.me

* Add profile translations

* Add translations to general page

* Add calendar switch

* Add calendar switch

* Add translations to appearance page

* Clean up conferencing page

* Settings sidebar fixes

* Updates middleware

* Update SettingsLayout.tsx

* Settings layout improvements

* Type fix

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Julian Benegas <julianbenegas99@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* AppStore CLI: Making video app creation a breeze with major cleanup of locations code throughout (#3825)

* Fix breadcrumb colors

* HorizontalTabs

* Team List Item WIP

* Horizontal Tabs

* Cards

* Remove team list item WIP

* Login Page

* Add welcome back i118n

* EventType page work

* Update EventType Icons

* WIP Availability

* Horizontal Tab Work

* Add build command for in root

* Update build DIr/command

* Add Edit Button + change buttons to v2

* Availablitiy page

* Fix IPAD

* Make mobile look a little nicer

* WIP bookingshell

* Remove list items from breaking build

* Add Embed ModalBox for routing forms

* Mian bulk of Booking Page.

* Few updates to components

* Fix chormatic feedback

* Add duplicate form support

* Fix duplication logic

* Change to feathericons everywhere and other fixes

* Dont allow routes for fallback route

* Fix banner

* Fix Empty Screen

* Text area + embded window fixes

* Semi fix avatar

* Fix all TS issues

* Fix tests

* Troubleshoot container + Active on count

* Support routing using query params

* Improve mobile

* NITS

* Fix padding on input

* Support multiselect in router endpoint

* Fix the issue where app goes in embed mode after viewing embed once

* Fix icons

* Add router url tests

* Add Responses download and form toggling tests

* Add required validation test

* Change Icons everywhere

* App typeform app

* Improvements in cli

* Starting to move event types settings to tabs

* Begin migration to single page form

* Single page tabs

* Limits Page

* Advanced tab

* Add RHF to dependancies

* Add typeform how-to-use page

* Add typeform how-to-use page and screenshots

* Most of advanced tab

* Solved RHF mismtach

* Build fixes

* RHF conditionals fixes

* Improved legibility

* Fix TS error

* Add missing image

* Update CliApp.tsx

* Major refactor/organisation into optional V2 UI

* Portal EditLocationModal

* Fix dialoug form

* Update imports

* Auto Animate + custom inputs WIP

* Custom Inputs

* WIP Apps

* Fixing stories imports

* Stripe app

* Remove duplicate dialog

* Remove duplicate dialog

* Major locations cleanup, 10s of bug fixes and app-store improvements

* Fix missing pieces

* More fixes

* Fix embed URL

* Fix app toggles + number of active apps

* Fix container padding on disabledBorder prop

* Removes strict

* more fixes

* EventType Team page WIP

* Fix embed

* NIT

* Add Darkmode gray color

* V2 Shell WIP

* Fix headings on shell V2

* Fix mobile layout with V2 shell

* V2 create event type button

* Checked Team Select

* Hidden to happen on save - not on toggle

* Team Attendee Select animation

* Fix scheduling type and remove multi select label

* Fix overflow on teams url

* Revert console

* Revert api

* Fix Embed TS errors

* Fix TS errors

* Fix Eslint errors

* Fix TS errors for UI

* Fix ESLINT error

* Fix TS errors

* Add missing import

* Fix CLI

* Add a default placeholder

* Remove hardcoded daily:integrations

* Fix message for payment page

* Revert api and console to main

* Update README

* Fix TS errors

* Fix Lint warnings

* Fix Tests

* Fix conflict issues

* Fix conflict issues

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>

* Button to test a workflow action (#3873)

* add Test action button + UI improvements

* add test action functionality

* add confirmation dialog before sending SMS

* code clean up

* show error message if test action fails

* disable test action button in edit mode

* fixes SMS testing

* use updated values

* fix wrongly updated data in useEffect

* fix typo

* code clean up

* fix UI issue in mobile view

* small design fix

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>

* Improve CLI App Help Text (#3982)

* Adds deprecation notice to QueryCell (#3977)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* added campfire.to app (#3984)

* fixed file size campfire (#3985)

* added campfire.to app

* fixed file size

* Draft shell on md screen

* Draft small settings bar

* Add padding to dropdown menu

* WIP

* Create mobile settings nav

* Fix conferencing render

* Only display mobile settings nav

* Clean up my account section

* Clean up

* Remove old draft shell

* Remove old draft shell

* Implement changes from #3817

* Revise sidebar

* WIP

* Side navigation drawer

* Fix type error

* Fix lint problem

* Get rid of main top padding on main shell

* Fix e2e test

* Fix type error

* Remove unused imports

* Add back button function

* Update SettingsSidebarContainer.tsx

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Julian Benegas <julianbenegas99@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: Kszemi <mkrzemien11@gmail.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>
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: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-09-06 12:23:17 -06:00
Peer Richelsen 62ab3056a5 fixed dropdown component styling, added new shadow-dropdown (#4211) 2022-09-06 17:56:13 +00:00
Peer Richelsen f9d639b157 fixed app bar on mobile (#4209) 2022-09-06 17:05:16 +00:00
hexcowboy b30ab45612 fix mobile styling for rainbowkit gate (#4207) 2022-09-06 18:40:28 +02:00
Peer Richelsen 143279699a fixed subpage pills (#4204)
* fixed subpage pills

* nit
2022-09-06 11:06:39 +02:00
bbf22a29b0 New Crowdin translations by Github Action (#4202)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-06 10:54:21 +02:00
5ca2c0561c New Crowdin translations by Github Action (#4188)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-09-06 10:53:25 +02:00
Syed Ali Shahbaz addd58cca6 fixes dark mode piblic page (#4201) 2022-09-06 10:50:36 +02:00
Carina Wollendorfer dc6f8d1f63 V2 workflow improvements (#4070) 2022-09-05 23:29:00 -04:00
zomars dc68b608a0 Adds apps/installed to blacklist v2 2022-09-05 19:09:04 -06:00
zomars 6fdbadc94b Removed disconnected calendar notice 2022-09-05 19:09:04 -06:00
Alex van Andel 046dadbae2 Fixes labels for v2 nav being blank (#4190) 2022-09-05 18:40:27 -06:00
612 changed files with 22245 additions and 6970 deletions
+1 -1
View File
@@ -97,7 +97,7 @@ LARK_OPEN_VERIFICATION_TOKEN=""
# - WEB3
# Used for the Web3 plugin
# @see https://github.com/calcom/cal.com/blob/main/packages/app-store/web3/README.md
# @see https://github.com/calcom/cal.com/blob/main/packages/app-store/rainbow/README.md
ALCHEMY_API_KEY=""
INFURA_API_KEY=""
# *********************************************************************************************************
+2
View File
@@ -94,6 +94,8 @@ NEXT_PUBLIC_IS_E2E=
# Used for internal billing system
NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE=
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE=
NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN=0
NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE=
NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRO_PLAN_PRODUCT_ID=
@@ -12,6 +12,9 @@ on:
- apps/storybook/**
- packages/ui/**
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
# List of jobs
jobs:
+10
View File
@@ -5,6 +5,11 @@ on:
- main
paths:
- "**.tsx?"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
NODE_OPTIONS: "--max-old-space-size=8192"
jobs:
check-types:
runs-on: ubuntu-latest
@@ -17,8 +22,13 @@ jobs:
- name: Use Node 16.x
uses: actions/setup-node@v3
with:
node-version: 16.x
cache: "yarn"
- name: Show info
run: node -e "console.log(require('v8').getHeapStatistics())"
- run: yarn --frozen-lockfile
- run: yarn type-check
+14 -12
View File
@@ -4,22 +4,24 @@ on:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
synchronize-with-crowdin:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v2
- name: crowdin action
uses: crowdin/github-action@1.4.9
with:
upload_translations: true
download_translations: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
- name: crowdin action
uses: crowdin/github-action@1.4.9
with:
upload_translations: true
download_translations: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
+3 -1
View File
@@ -12,6 +12,9 @@ on:
- apps/swagger/**
- apps/website/**
- apps/web/public/**
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test:
timeout-minutes: 20
@@ -97,4 +100,3 @@ jobs:
with:
name: test-results
path: test-results
+5
View File
@@ -7,6 +7,9 @@ on:
paths:
- apps/web/**
- packages/embeds/**
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
e2e-embed:
timeout-minutes: 20
@@ -25,6 +28,8 @@ jobs:
NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE: ${{ secrets.NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE }}
NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN: 1
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
+5
View File
@@ -8,6 +8,9 @@ on:
paths:
- apps/web/**
- packages/**
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
e2e:
timeout-minutes: 20
@@ -26,6 +29,8 @@ jobs:
NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE: ${{ secrets.NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE }}
NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN: 1
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
+3 -1
View File
@@ -1,7 +1,9 @@
name: "Pull Request Labeler"
on:
- pull_request_target
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
labeler:
permissions:
+3
View File
@@ -6,6 +6,9 @@ on:
paths:
- "**.tsx?"
- "**.jsx?"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
+3
View File
@@ -5,6 +5,9 @@ on:
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
required-checks:
runs-on: ubuntu-latest
+3
View File
@@ -8,6 +8,9 @@ on:
paths:
- apps/web/**
- packages/**
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test:
timeout-minutes: 20
+1 -1
View File
@@ -6,5 +6,5 @@
},
"typescript.preferences.importModuleSpecifier": "non-relative",
"spellright.language": ["en"],
"spellright.documentTypes": ["markdown"]
"spellright.documentTypes": ["markdown", "typescript"]
}
-17
View File
@@ -4,23 +4,6 @@ Contributions are what make the open source community such an amazing place to l
- Before jumping into a PR be sure to search [existing PRs](https://github.com/calcom/cal.com/pulls) or [issues](https://github.com/calcom/cal.com/issues) for an open or closed item that relates to your submission.
## Areas of expertise
### Legend
✅ = has knowledge
🥇 = is their main priority
⚠️ = is the only one with knowledge
👀 = has no knowledge but wants to be onboarded
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://dynamic-svgs.vercel.app/image.svg?dark">
<img alt="Areas of expertise table" src="https://dynamic-svgs.vercel.app/image.svg">
</picture>
## Developing
The development branch is `main`. This is the branch that all pull
+3 -3
View File
@@ -31,7 +31,7 @@
<a href="https://news.ycombinator.com/item?id=26817795"><img src="https://img.shields.io/badge/Hacker%20News-311-%23FF6600" alt="Hacker News"></a>
<a href="https://github.com/calcom/cal.com/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-AGPLv3-purple" alt="License"></a>
<a href="https://github.com/calcom/cal.com/pulse"><img src="https://img.shields.io/github/commit-activity/m/calcom/cal.com" alt="Commits-per-month"></a>
<a href="https://cal.com/pricing"><img src="https://img.shields.io/badge/Pricing-%2412%2Fmonth-brightgreen" alt="Pricing"></a>
<a href="https://cal.com/pricing"><img src="https://img.shields.io/badge/Pricing-Free-brightgreen" alt="Pricing"></a>
<a href="https://jitsu.com?utm_source=github/calcom/cal.com"><img src="https://img.shields.io/badge/Metrics_tracked_by-JITSU-AA00FF?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACKSURBVHgBrZDRCYAwDEQv6gCOoKO4hOCXI9QVnEZwiY5iF5GaVClaBNtioCSUvCR3tMJaxIfZgW4AGUoEPVwgPZoS0Dmgg3NBVDFNbMIsmYCak3J1jDk9iCQvsKJvkzr71N81Gj6vDT/LU2P6RhY63jcafk3YJEbgeZpiFyc/5HJKv8Ef273NSfABGbQfUZhnOSAAAAAASUVORK5CYII=" alt="Jitsu Tracked"></a>
<img src="https://api.checklyhq.com/v1/badges/checks/5e048048-1b51-47ba-9209-60607507622e?responseTime=true" alt="Checkly Availability" />
<a href="https://hub.docker.com/r/calendso/calendso"><img src="https://img.shields.io/docker/pulls/calendso/calendso"></a>
@@ -58,7 +58,7 @@ Calendly and other scheduling tools are awesome. It made our lives massively eas
That's where Cal.com comes in. Self-hosted or hosted by us. White-label by design. API-driven and ready to be deployed on your own domain. Full control of your events and data.
## Product of the Month: April
## Product of the Month: April 2021
#### Support us on [Product Hunt](https://www.producthunt.com/posts/calendso?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-calendso)
@@ -207,7 +207,7 @@ Be sure to set the environment variable `NEXTAUTH_URL` to the correct value. If
yarn test-e2e
# To open last HTML report run:
yarn playwright show-report test-results/reports/playwright-html-report
yarn playwright show-report test-results/reports/playwright-html-report
```
### Upgrading from earlier versions
+11 -11
View File
@@ -4,31 +4,31 @@
*/
// Create a Chromium browser
const { chromium } = require('playwright')
const { chromium } = require("playwright");
// Checkly supports top level await, but we wrap your code in an async function so you can run it locally too.
async function run () {
const browser = await chromium.launch()
const page = await browser.newPage()
async function run() {
const browser = await chromium.launch();
const page = await browser.newPage();
// If available, we set the target URL to a preview deployment URL provided by the ENVIRONMENT_URL created by Vercel.
// Otherwise, we use the Production URL.
const targetUrl = process.env.ENVIRONMENT_URL || 'https://app.cal.com'
const targetUrl = process.env.ENVIRONMENT_URL || "https://app.cal.com";
// We visit the page. This waits for the "load" event by default.
const response = await page.goto(targetUrl)
const response = await page.goto(targetUrl);
// If the page doesn't return a successful response code, we fail the check
if (response.status() > 399) {
throw new Error(`Failed with response code ${response.status()}`)
throw new Error(`Failed with response code ${response.status()}`);
}
// We snap a screenshot.
await page.screenshot({ path: 'screenshot.jpg' })
await page.screenshot({ path: "screenshot.jpg" });
// We close the page and browser. This is needed for collecting accurate web vitals.
await page.close()
await browser.close()
await page.close();
await browser.close();
}
run()
run();
+1 -1
View File
@@ -6,7 +6,7 @@ title: Vercel
## Requirements
You need a PostgresDB database hosted somewhere. [Heroku](https://www.heroku.com) and [Supabase](https://supabase.com/) offer great free options.
You need a PostgresDB database hosted somewhere. [Supabase](https://supabase.com/) offers a great free option.
## Getting Started
+9 -8
View File
@@ -15,14 +15,15 @@
"dependencies": {
"@calcom/dayjs": "*",
"@calcom/ui": "*",
"@radix-ui/react-avatar": "^0.1.4",
"@radix-ui/react-collapsible": "^0.1.0",
"@radix-ui/react-dialog": "^0.1.0",
"@radix-ui/react-dropdown-menu": "^0.1.1",
"@radix-ui/react-id": "^0.1.0",
"@radix-ui/react-radio-group": "^0.1.1",
"@radix-ui/react-slider": "^0.1.1",
"@radix-ui/react-switch": "^0.1.1",
"@radix-ui/react-avatar": "^1.0.0",
"@radix-ui/react-collapsible": "^1.0.0",
"@radix-ui/react-dialog": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^1.0.0",
"@radix-ui/react-id": "^1.0.0",
"@radix-ui/react-popover": "^1.0.0",
"@radix-ui/react-radio-group": "^1.0.0",
"@radix-ui/react-slider": "^1.0.0",
"@radix-ui/react-switch": "^1.0.0",
"@radix-ui/react-tooltip": "^1.0.0",
"next": "^12.2.5",
"next-transpile-modules": "^9.0.0",
@@ -28,13 +28,14 @@ export const Default = () => (
export const Combined = () => (
<div className="flex flex-col space-y-2">
<ButtonGroup combined>
<Button StartIcon={Trash2} size="icon" color="secondary" combined />
<Button StartIcon={Navigation2} size="icon" color="secondary" combined />
<Button StartIcon={Clipboard} size="icon" color="secondary" combined />
<Button StartIcon={Trash2} size="icon" color="secondary" />
<Button StartIcon={Navigation2} size="icon" color="secondary" />
<Button StartIcon={Clipboard} size="icon" color="secondary" />
</ButtonGroup>
<ButtonGroup combined>
<Button StartIcon={ArrowLeft} size="icon" color="secondary" combined />
<Button StartIcon={ArrowRight} size="icon" color="secondary" combined />
<Button StartIcon={ArrowLeft} size="icon" color="secondary" />
<Button StartIcon={ArrowRight} size="icon" color="secondary" />
<Button StartIcon={ArrowRight} href="/" size="icon" color="secondary" />
</ButtonGroup>
</div>
);
+8 -1
View File
@@ -1,7 +1,8 @@
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Copy } from "react-feather";
import { TextAreaField, TextField } from "@calcom/ui/v2/core/form/fields";
import { TextAreaField, TextField, PasswordField } from "@calcom/ui/v2/core/form/fields";
import DatePicker from "@calcom/ui/v2/modules/booker/DatePicker";
export default {
@@ -66,3 +67,9 @@ export const TextAreaInput: ComponentStory<typeof TextAreaField> = () => (
);
export const DatePickerInput: ComponentStory<typeof DatePicker> = () => <DatePicker date={new Date()} />;
export const PasswordInput: ComponentStory<typeof PasswordField> = () => (
<TooltipProvider>
<PasswordField />
</TooltipProvider>
);
+14 -2
View File
@@ -1,13 +1,24 @@
import { ComponentMeta } from "@storybook/react";
import { Select } from "@calcom/ui/v2";
import MultiDropdownSelect from "@calcom/ui/v2/modules/event-types/MultiDropdownSelect";
export default {
title: "Form/Select",
component: Select,
} as ComponentMeta<typeof Select>;
const testOptions = [
const singleOptions = [
{
label: "Select",
value: 0,
},
...[5, 10, 15, 20, 30, 45, 60, 90, 120].map((minutes) => ({
label: minutes + " " + "minutes",
value: minutes,
})),
];
const multiOptions = [
{
label: "Select",
value: 0,
@@ -18,4 +29,5 @@ const testOptions = [
})),
];
export const Single = () => <Select options={testOptions} />;
export const Single = () => <Select options={singleOptions} />;
export const Multi = () => <MultiDropdownSelect options={multiOptions} />;
+1 -1
View File
@@ -426,7 +426,7 @@ paths:
- installed: true
type: caldav_calendar
credential: null
title: CalDav Server
title: CalDav (Beta)
imageSrc: integrations/caldav.svg
description: For personal and business calendars
__N_SSP: true
+35 -43
View File
@@ -1,5 +1,6 @@
import Link from "next/link";
import React, { useEffect, useState } from "react";
import { useRouter } from "next/router";
import React, { useState } from "react";
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
import { InstallAppButton } from "@calcom/app-store/components";
@@ -13,6 +14,8 @@ import { Icon } from "@calcom/ui/Icon";
import Shell from "@calcom/ui/Shell";
import Badge from "@calcom/ui/v2/core/Badge";
import DisconnectIntegration from "@components/integrations/DisconnectIntegration";
const Component = ({
name,
type,
@@ -32,8 +35,15 @@ const Component = ({
isProOnly,
}: Parameters<typeof App>[0]) => {
const { t } = useLocale();
const router = useRouter();
const { data: user } = trpc.useQuery(["viewer.me"]);
const utils = trpc.useContext();
const handleOpenChange = () => {
utils.invalidateQueries(["viewer.integrations"]);
router.replace("/apps/installed");
};
const mutation = useAddAppMutation(null, {
onSuccess: () => {
showToast("App successfully installed", "success");
@@ -48,34 +58,14 @@ const Component = ({
currency: "USD",
useGrouping: false,
}).format(price);
const [installedAppCount, setInstalledAppCount] = useState(0);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
async function getInstalledApp(appCredentialType: string) {
const queryParam = new URLSearchParams();
queryParam.set("app-credential-type", appCredentialType);
try {
const result = await fetch(`/api/app-store/installed?${queryParam.toString()}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
}).then((data) => {
setIsLoading(false);
return data;
});
if (result.status === 200) {
const res = await result.json();
setInstalledAppCount(res.count);
}
} catch (error) {
if (error instanceof Error) {
console.log(error.message);
}
}
}
getInstalledApp(type);
}, [type]);
const [existingCredentials, setExistingCredentials] = useState<number[]>([]);
const appCredentials = trpc.useQuery(["viewer.appCredentialsByType", { appType: type }], {
onSuccess(data) {
setExistingCredentials(data);
},
});
const allowedMultipleInstalls = categories.indexOf("calendar") > -1;
return (
@@ -92,11 +82,6 @@ const Component = ({
<header className="px-4 py-2">
<div className="flex items-center">
<h1 className="font-cal text-xl text-gray-900">{name}</h1>
{isProOnly && user?.plan === "FREE" ? (
<Badge className="ml-2" variant="default">
PRO
</Badge>
) : null}
</div>
<h2 className="text-sm text-gray-500">
<span className="capitalize">{categories[0]}</span> • {t("published_by", { author })}
@@ -105,12 +90,13 @@ const Component = ({
</div>
<div className="mt-4 sm:mt-0 sm:text-right">
{!isLoading ? (
isGlobal || (installedAppCount > 0 && allowedMultipleInstalls) ? (
{!appCredentials.isLoading ? (
isGlobal ||
(existingCredentials.length > 0 && allowedMultipleInstalls ? (
<div className="flex space-x-3">
<Button StartIcon={Icon.FiCheck} color="secondary" disabled>
{installedAppCount > 0
? t("active_install", { count: installedAppCount })
{existingCredentials.length > 0
? t("active_install", { count: existingCredentials.length })
: t("globally_install")}
</Button>
{!isGlobal && (
@@ -135,10 +121,16 @@ const Component = ({
/>
)}
</div>
) : installedAppCount > 0 ? (
<Button color="secondary" disabled title="App already installed">
{t("installed")}
</Button>
) : existingCredentials.length > 0 ? (
<DisconnectIntegration
id={existingCredentials[0]}
render={(btnProps) => (
<Button {...btnProps} color="warn" data-testid={type + "-integration-disconnect-button"}>
{t("disconnect")}
</Button>
)}
onOpenChange={handleOpenChange}
/>
) : (
<InstallAppButton
type={type}
@@ -159,7 +151,7 @@ const Component = ({
);
}}
/>
)
))
) : (
<SkeletonButton width="24" height="10" />
)}
@@ -107,9 +107,10 @@ const DestinationCalendarSelector = ({
control: (defaultStyles) => {
return {
...defaultStyles,
borderRadius: "2px",
borderRadius: "6px",
"@media only screen and (min-width: 640px)": {
...(defaultStyles["@media only screen and (min-width: 640px)"] as object),
width: "100%",
},
};
},
@@ -135,6 +136,9 @@ const DestinationCalendarSelector = ({
}}
isLoading={isLoading}
value={selectedOption}
components={{
IndicatorSeparator: () => null,
}}
/>
</div>
);
+86 -106
View File
@@ -1,19 +1,17 @@
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
import classNames from "classnames";
import { useRouter } from "next/router";
import { NextRouter, useRouter } from "next/router";
import { createRef, forwardRef, MutableRefObject, RefObject, useRef, useState } from "react";
import { components, ControlProps } from "react-select";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { Dialog, DialogClose, DialogContent } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields";
import { Button, Switch } from "@calcom/ui/v2";
import { Button, HorizontalTabs, showToast, Switch } from "@calcom/ui/v2";
import { Dialog, DialogClose, DialogContent } from "@calcom/ui/v2/core/Dialog";
import { EMBED_LIB_URL, WEBAPP_URL } from "@lib/config/constants";
import NavTabs from "@components/NavTabs";
import ColorPicker from "@components/ui/colorpicker";
import Select from "@components/ui/form/Select";
@@ -38,7 +36,7 @@ type PreviewState = {
brandColor: string;
};
};
const queryParamsForDialog = ["embedType", "tabName", "embedUrl"];
const queryParamsForDialog = ["embedType", "embedTabName", "embedUrl"];
const getDimension = (dimension: string) => {
if (dimension.match(/^\d+$/)) {
@@ -47,6 +45,23 @@ const getDimension = (dimension: string) => {
return dimension;
};
const goto = (router: NextRouter, searchParams: Record<string, string>) => {
const newQuery = new URLSearchParams(router.asPath.split("?")[1]);
Object.keys(searchParams).forEach((key) => {
newQuery.set(key, searchParams[key]);
});
router.push(`${router.asPath.split("?")[0]}?${newQuery.toString()}`, undefined, {
shallow: true,
});
};
const removeQueryParams = (router: NextRouter, queryParams: string[]) => {
queryParams.forEach((param) => {
delete router.query[param];
});
router.push(`${router.asPath.split("?")[0]}?${router.query.toString()}`);
};
/**
* It allows us to show code with certain reusable blocks indented according to the block variable placement
* So, if you add a variable ${abc} with indentation of 4 spaces, it will automatically indent all newlines in `abc` with the same indent before constructing the final string
@@ -453,7 +468,7 @@ const embeds: {
const tabs = [
{
name: "HTML",
tabName: "embed-code",
href: "embedTabName=embed-code",
icon: Icon.FiCode,
type: "code",
Component: forwardRef<
@@ -504,7 +519,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previ
},
{
name: "React",
tabName: "embed-react",
href: "embedTabName=embed-react",
icon: Icon.FiCode,
type: "code",
Component: forwardRef<
@@ -544,8 +559,8 @@ ${getEmbedTypeSpecificString({ embedFramework: "react", embedType, calLink, prev
},
{
name: "Preview",
tabName: "embed-preview",
icon: Icon.FiEye,
href: "embedTabName=embed-preview",
icon: Icon.FiTrello,
type: "iframe",
Component: forwardRef<
HTMLIFrameElement | HTMLTextAreaElement | null,
@@ -561,7 +576,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "react", embedType, calLink, prev
<iframe
ref={ref as typeof ref & MutableRefObject<HTMLIFrameElement>}
data-testid="embed-preview"
className="border-1 h-[75vh] border"
className="border-1 h-[100vh] border"
width="100%"
height="100%"
src={`${WEBAPP_URL}/embed/preview.html?embedType=${embedType}&calLink=${calLink}`}
@@ -591,7 +606,7 @@ const ChooseEmbedTypesDialogContent = () => {
const { t } = useLocale();
const router = useRouter();
return (
<DialogContent size="lg">
<DialogContent type="creation" useOwnActionButtons 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")}
@@ -607,11 +622,8 @@ const ChooseEmbedTypesDialogContent = () => {
key={index}
data-testid={embed.type}
onClick={() => {
router.push({
query: {
...router.query,
embedType: embed.type,
},
goto(router, {
embedType: embed.type,
});
}}>
<div className="order-none box-border flex-none rounded-sm border border-solid bg-white">
@@ -636,6 +648,13 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
const { t } = useLocale();
const router = useRouter();
const iframeRef = useRef<HTMLIFrameElement>(null);
const s = (href: string) => {
const searchParams = new URLSearchParams(router.asPath.split("?")[1] || "");
const [a, b] = href.split("=");
searchParams.set(a, b);
return `${router.asPath.split("?")[0]}?${searchParams.toString()}`;
};
const parsedTabs = tabs.map((t) => ({ ...t, href: s(t.href) }));
const embedCodeRefs: Record<typeof tabs[0]["name"], RefObject<HTMLTextAreaElement>> = {};
tabs
.filter((tab) => tab.type === "code")
@@ -662,28 +681,13 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
});
const close = () => {
const noPopupQuery = {
...router.query,
};
delete noPopupQuery.dialog;
queryParamsForDialog.forEach((queryParam) => {
delete noPopupQuery[queryParam];
});
router.push({
query: noPopupQuery,
});
removeQueryParams(router, ["dialog", ...queryParamsForDialog]);
};
// Use embed-code as default tab
if (!router.query.tabName) {
router.query.tabName = "embed-code";
router.push({
query: {
...router.query,
},
if (!router.query.embedTabName) {
goto(router, {
embedTabName: "embed-code",
});
}
@@ -780,20 +784,13 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
];
return (
<DialogContent size="xl">
<DialogContent size="xl" className="p-0.5" type="creation" useOwnActionButtons>
<div className="flex">
<div className="flex w-1/3 flex-col bg-white p-6">
<div className="flex w-1/3 flex-col bg-gray-50 p-8">
<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,
},
});
removeQueryParams(router, ["embedType", "embedTabName"]);
}}>
<Icon.FiArrowLeft className="mr-4 w-4" />
</button>
@@ -871,9 +868,9 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
<div
className={classNames(
"mt-4 items-center justify-between",
embedType === "floating-popup" ? "flex" : "hidden"
embedType === "floating-popup" ? "" : "hidden"
)}>
<div className="text-sm">Button Text</div>
<div className="mb-2 text-sm">Button Text</div>
{/* Default Values should come from preview iframe */}
<TextField
name="buttonText"
@@ -895,10 +892,9 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
</div>
<div
className={classNames(
"mt-4 flex items-center justify-between",
embedType === "floating-popup" ? "flex" : "hidden"
"mt-4 flex items-center justify-start",
embedType === "floating-popup" ? "space-x-2" : "hidden"
)}>
<div className="text-sm">Display Calendar Icon Button</div>
<Switch
defaultChecked={true}
onCheckedChange={(checked) => {
@@ -913,13 +909,14 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
});
}}
/>
<div className="text-sm">Display Calendar Icon Button</div>
</div>
<div
className={classNames(
"mt-4 flex items-center justify-between",
embedType === "floating-popup" ? "flex" : "hidden"
"mt-4 items-center justify-between",
embedType === "floating-popup" ? "" : "hidden"
)}>
<div>Position of Button</div>
<div className="mb-2">Position of Button</div>
<Select
onChange={(position) => {
setPreviewState((previewState) => {
@@ -936,13 +933,9 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
options={FloatingPopupPositionOptions}
/>
</div>
<div
className={classNames(
"mt-4 flex items-center justify-between",
embedType === "floating-popup" ? "flex" : "hidden"
)}>
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
<div>Button Color</div>
<div className="w-36">
<div className="w-full">
<ColorPicker
defaultValue="#000000"
onChange={(color) => {
@@ -959,13 +952,9 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
/>
</div>
</div>
<div
className={classNames(
"mt-4 flex items-center justify-between",
embedType === "floating-popup" ? "flex" : "hidden"
)}>
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
<div>Text Color</div>
<div className="w-36">
<div className="w-full">
<ColorPicker
defaultValue="#000000"
onChange={(color) => {
@@ -1000,10 +989,10 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
</CollapsibleTrigger>
<CollapsibleContent>
<div className="mt-6 text-sm">
<Label className="flex items-center justify-between">
<div>Theme</div>
<Label className="">
<div className="mb-2">Theme</div>
<Select
className="w-36"
className="w-full"
defaultValue={ThemeOptions[0]}
components={{
Control: ThemeSelectControl,
@@ -1030,9 +1019,9 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
// { 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">
<Label key={palette.name} className="pb-4">
<div className="mb-2 pt-2">{palette.title}</div>
<div className="w-full">
<ColorPicker
defaultValue="#000000"
onChange={(color) => {
@@ -1049,33 +1038,33 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
</Collapsible>
</div>
</div>
<div className="w-2/3 bg-gray-50 p-6">
<NavTabs data-testid="embed-tabs" tabs={tabs} linkProps={{ shallow: true }} />
<div className="flex w-2/3 flex-col p-8">
<HorizontalTabs data-testid="embed-tabs" tabs={parsedTabs} linkProps={{ shallow: true }} />
{tabs.map((tab) => {
return (
<div
key={tab.tabName}
className={classNames(router.query.tabName === tab.tabName ? "block" : "hidden")}>
<div>
<div className={classNames(tab.type === "code" ? "h-[75vh]" : "")}>
{tab.type === "code" ? (
<tab.Component
embedType={embedType}
calLink={calLink}
previewState={previewState}
ref={refOfEmbedCodesRefs.current[tab.name]}
/>
) : (
<tab.Component
embedType={embedType}
calLink={calLink}
previewState={previewState}
ref={iframeRef}
/>
)}
</div>
<div className={router.query.tabName == "embed-preview" ? "block" : "hidden"} />
key={tab.href}
className={classNames(
router.query.embedTabName === tab.href.split("=")[1] ? "flex flex-grow flex-col" : "hidden"
)}>
<div className="flex h-[55vh] flex-grow flex-col">
{tab.type === "code" ? (
<tab.Component
embedType={embedType}
calLink={calLink}
previewState={previewState}
ref={refOfEmbedCodesRefs.current[tab.name]}
/>
) : (
<tab.Component
embedType={embedType}
calLink={calLink}
previewState={previewState}
ref={iframeRef}
/>
)}
</div>
<div className={router.query.embedTabName == "embed-preview" ? "block" : "hidden"} />
<div className="mt-8 flex flex-row-reverse gap-x-2">
{tab.type === "code" ? (
<Button
@@ -1142,19 +1131,10 @@ export const EmbedButton = <T extends React.ElementType>({
const router = useRouter();
className = classNames(className, "hidden lg:inline-flex");
const openEmbedModal = () => {
const query = {
...router.query,
goto(router, {
dialog: "embed",
embedUrl,
};
router.push(
{
pathname: router.pathname,
query,
},
undefined,
{ shallow: true }
);
});
};
const Component = as ?? Button;
+3 -2
View File
@@ -1,8 +1,7 @@
import dynamic from "next/dynamic";
import { Dispatch, useState, useEffect } from "react";
import { JSONObject } from "superjson/dist/types";
import RainbowGate from "@calcom/app-store/rainbow/components/RainbowKit";
export type Gate = undefined | "rainbow"; // Add more like ` | "geolocation" | "payment"`
export type GateState = {
@@ -16,6 +15,8 @@ type GateProps = {
dispatch: Dispatch<Partial<GateState>>;
};
const RainbowGate = dynamic(() => import("@calcom/app-store/rainbow/components/RainbowKit"));
// To add a new Gate just add the gate logic to the switch statement
const Gates: React.FC<GateProps> = ({ children, gates, metadata, dispatch }) => {
const [rainbowToken, setRainbowToken] = useState<string>();
+3 -1
View File
@@ -10,7 +10,9 @@ export function useViewerI18n() {
* i18n should never be clubbed with other queries, so that it's caching can be managed independently.
* We intend to not cache i18n query
**/
context: { skipBatch: true },
trpc: {
context: { skipBatch: true },
},
});
}
+1
View File
@@ -63,6 +63,7 @@ function CropContainer({
);
}
/** @deprecated Use `packages/ui/v2/core/ImageUploader.tsx` */
export default function ImageUploader({
target,
id,
-5
View File
@@ -32,11 +32,6 @@ export default function AppCard(props: AppCardProps) {
</div>
<div className="flex items-center">
<h3 className="font-medium">{props.app.name}</h3>
{props.app.isProOnly && user?.plan === "FREE" ? (
<Badge className="ml-2" variant="default">
PRO
</Badge>
) : null}
</div>
{/* TODO: add reviews <div className="flex text-sm text-gray-800">
<span>{props.rating} stars</span> <StarIcon className="ml-1 mt-0.5 h-4 w-4 text-yellow-600" />
@@ -1,3 +1,7 @@
/**
* @deprecated
* use Component in "/packages/features/schedules/components/Schedule";
**/
import classNames from "classnames";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
@@ -192,6 +196,10 @@ const CopyTimes = ({ disabled, onApply }: { disabled: number[]; onApply: (select
);
};
/**
* @deprecated
* use Component in "/packages/features/schedules/components/Schedule";
**/
export const DayRanges = ({
name,
defaultValue = [defaultDayRange],
@@ -324,6 +332,10 @@ const ScheduleBlock = ({ name, day, weekday }: ScheduleBlockProps) => {
);
};
/**
* @deprecated
* use Component in "/packages/features/schedules/components/Schedule";
**/
const Schedule = ({ name }: { name: string }) => {
const { i18n } = useLocale();
return (
@@ -1,3 +1,7 @@
/**
* @deprecated modifications to this file should be v2 only
* Use `/packages/features/schedules/components/ScheduleListItem.tsx` instead
*/
import Link from "next/link";
import { Fragment } from "react";
@@ -9,6 +13,10 @@ import { Button } from "@calcom/ui";
import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
/**
* @deprecated modifications to this file should be v2 only
* Use `/packages/features/schedules/components/ScheduleListItem.tsx` instead
*/
export function ScheduleListItem({
schedule,
deleteFunction,
@@ -37,7 +45,7 @@ export function ScheduleListItem({
<p className="mt-1 text-xs text-neutral-500">
{schedule.availability.map((availability: Availability) => (
<Fragment key={availability.id}>
{availabilityAsString(availability, i18n.language)}
{availabilityAsString(availability, { locale: i18n.language })}
<br />
</Fragment>
))}
@@ -45,6 +53,7 @@ export function ScheduleListItem({
</a>
</Link>
</div>
<Dropdown>
<DropdownMenuTrigger className="group mr-5 h-10 w-10 border border-transparent p-0 text-neutral-500 hover:border-gray-200">
<Icon.FiMoreHorizontal className="h-5 w-5 group-hover:text-gray-800" />
@@ -2,9 +2,10 @@ import React from "react";
import { SkeletonText } from "@calcom/ui";
/** @deprecated Use `apps/web/components/v2/availability/SkeletonLoader.tsx` */
function SkeletonLoader() {
return (
<ul className="animate-pulse divide-y divide-neutral-200 border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden">
<ul className="animate-pulse divide-y divide-neutral-200 rounded-md border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden">
<SkeletonItem />
<SkeletonItem />
<SkeletonItem />
@@ -16,7 +16,7 @@ type AvailableTimesProps = {
eventTypeId: number;
recurringCount: number | undefined;
eventTypeSlug: string;
date: Dayjs;
date?: Dayjs;
seatsPerTimeSlot?: number | null;
slots?: Slot[];
isLoading: boolean;
@@ -44,8 +44,10 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
setBrand(getComputedStyle(document.documentElement).getPropertyValue("--brand-color").trim());
}, []);
if (!date) return null;
return (
<div className="dark:bg-darkgray-100 mt-8 flex flex-col px-4 text-center sm:mt-0 sm:w-1/3 sm:p-5 md:-mb-5">
<div className="dark:bg-darkgray-100 mt-8 flex h-full w-full flex-col px-4 text-center sm:mt-0 sm:p-5 md:-mb-5 md:min-w-[200px] lg:min-w-[300px]">
<div className="mb-4 text-left text-base">
<span className="text-bookingdarker dark:text-darkgray-800 mb-8 w-1/2 break-words font-semibold text-gray-900">
{nameOfDay(i18n.language, Number(date.format("d")))}
@@ -150,4 +152,6 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
);
};
AvailableTimes.displayName = "AvailableTimes";
export default AvailableTimes;
+237 -93
View File
@@ -13,6 +13,7 @@ import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
import { TextArea } from "@calcom/ui/form/fields";
import Badge from "@calcom/ui/v2/core/Badge";
import Button from "@calcom/ui/v2/core/Button";
import useMeQuery from "@lib/hooks/useMeQuery";
@@ -28,7 +29,7 @@ type BookingItem = inferQueryOutput<"viewer.bookings">["bookings"][number];
type BookingItemProps = BookingItem & {
listingStatus: BookingListingStatus;
recurringBookings?: BookingItem[];
recurringBookings: inferQueryOutput<"viewer.bookings">["recurringInfo"];
};
function BookingListItem(booking: BookingItemProps) {
@@ -43,10 +44,25 @@ function BookingListItem(booking: BookingItemProps) {
const mutation = trpc.useMutation(["viewer.bookings.confirm"], {
onSuccess: () => {
setRejectionDialogIsOpen(false);
showToast(t("booking_confirmation_success"), "success");
utils.invalidateQueries("viewer.bookings");
},
onError: () => {
showToast(t("booking_confirmation_failed"), "error");
utils.invalidateQueries("viewer.bookings");
},
});
const isUpcoming = new Date(booking.endTime) >= new Date();
const isPast = new Date(booking.endTime) < new Date();
const isCancelled = booking.status === BookingStatus.CANCELLED;
const isConfirmed = booking.status === BookingStatus.ACCEPTED;
const isRejected = booking.status === BookingStatus.REJECTED;
const isPending = booking.status === BookingStatus.PENDING;
const isRecurring = booking.recurringEventId !== null;
const isTabRecurring = booking.listingStatus === "recurring";
const isTabUnconfirmed = booking.listingStatus === "unconfirmed";
const bookingConfirm = async (confirm: boolean) => {
let body = {
bookingId: booking.id,
@@ -55,27 +71,18 @@ function BookingListItem(booking: BookingItemProps) {
};
/**
* Only pass down the recurring event id when we need to confirm the entire series, which happens in
* the "Recurring" tab, to support confirming discretionally in the "Recurring" tab.
* the "Recurring" tab and "Unconfirmed" tab, to support confirming discretionally in the "Recurring" tab.
*/
if (booking.listingStatus === "recurring" && booking.recurringEventId !== null) {
if ((isTabRecurring || isTabUnconfirmed) && isRecurring) {
body = Object.assign({}, body, { recurringEventId: booking.recurringEventId });
}
mutation.mutate(body);
};
const isUpcoming = new Date(booking.endTime) >= new Date();
const isCancelled = booking.status === BookingStatus.CANCELLED;
const isConfirmed = booking.status === BookingStatus.ACCEPTED;
const isRejected = booking.status === BookingStatus.REJECTED;
const isPending = booking.status === BookingStatus.PENDING;
const pendingActions: ActionType[] = [
{
id: "reject",
label:
booking.listingStatus === "recurring" && booking.recurringEventId !== null
? t("reject_all")
: t("reject"),
label: (isTabRecurring || isTabUnconfirmed) && isRecurring ? t("reject_all") : t("reject"),
onClick: () => {
setRejectionDialogIsOpen(true);
},
@@ -84,10 +91,7 @@ function BookingListItem(booking: BookingItemProps) {
},
{
id: "confirm",
label:
booking.listingStatus === "recurring" && booking.recurringEventId !== null
? t("confirm_all")
: t("confirm"),
label: (isTabRecurring || isTabUnconfirmed) && isRecurring ? t("confirm_all") : t("confirm"),
onClick: () => {
bookingConfirm(true);
},
@@ -100,23 +104,15 @@ function BookingListItem(booking: BookingItemProps) {
let bookedActions: ActionType[] = [
{
id: "cancel",
label:
booking.listingStatus === "recurring" && booking.recurringEventId !== null
? t("cancel_all_remaining")
: t("cancel"),
label: isTabRecurring && isRecurring ? t("cancel_all_remaining") : t("cancel"),
/* When cancelling we need to let the UI and the API know if the intention is to
cancel all remaining bookings or just that booking instance. */
href: `/cancel/${booking.uid}${
booking.listingStatus === "recurring" && booking.recurringEventId !== null
? "?allRemainingBookings=true"
: ""
}`,
href: `/cancel/${booking.uid}${isTabRecurring && isRecurring ? "?allRemainingBookings=true" : ""}`,
icon: Icon.FiX,
},
{
id: "edit_booking",
label: t("edit_booking"),
icon: Icon.FiEdit,
label: t("edit"),
actions: [
{
id: "reschedule",
@@ -145,7 +141,7 @@ function BookingListItem(booking: BookingItemProps) {
},
];
if (booking.listingStatus === "recurring" && booking.recurringEventId !== null) {
if (isTabRecurring && isRecurring) {
bookedActions = bookedActions.filter((action) => action.id !== "edit_booking");
}
@@ -182,12 +178,8 @@ function BookingListItem(booking: BookingItemProps) {
let recurringStrings: string[] = [];
let recurringDates: Date[] = [];
if (booking.recurringBookings && booking.eventType.recurringEvent?.freq !== undefined) {
[recurringStrings, recurringDates] = extractRecurringDates(
booking.recurringBookings,
user?.timeZone,
i18n
);
if (booking.recurringBookings !== undefined && booking.eventType.recurringEvent?.freq !== undefined) {
[recurringStrings, recurringDates] = extractRecurringDates(booking, user?.timeZone, i18n);
}
const location = booking.location || "";
@@ -260,90 +252,103 @@ function BookingListItem(booking: BookingItemProps) {
</DialogContent>
</Dialog>
<tr className="flex hover:bg-neutral-50">
<td className="hidden align-top ltr:pl-6 rtl:pr-6 sm:table-cell sm:w-44" onClick={onClick}>
<tr className="flex flex-col hover:bg-neutral-50 sm:flex-row">
<td className="hidden align-top ltr:pl-6 rtl:pr-6 sm:table-cell sm:min-w-[10rem]" onClick={onClick}>
<div className="cursor-pointer py-4">
<div className="text-sm leading-6 text-gray-900">{startTime}</div>
<div className="text-sm text-gray-500">
{dayjs(booking.startTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")} -{" "}
{dayjs(booking.endTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")}
</div>
<div className="text-sm text-gray-400">
{booking.recurringBookings &&
booking.eventType?.recurringEvent?.freq &&
(booking.listingStatus === "recurring" || booking.listingStatus === "cancelled") && (
<div className="underline decoration-gray-400 decoration-dashed underline-offset-2">
<div className="flex">
<Tooltip
content={recurringStrings.map((aDate, key) => (
<p key={key}>{aDate}</p>
))}>
<div className="text-gray-600 dark:text-white">
<Icon.FiRefreshCcw
stroke-width="3"
className="float-left mr-1 mt-1.5 inline-block h-3 w-3 text-gray-400"
/>
<p className="mt-1 pl-5 text-xs">
{booking.status === BookingStatus.ACCEPTED
? `${t("event_remaining", {
count: recurringDates.length,
})}`
: getEveryFreqFor({
t,
recurringEvent: booking.eventType.recurringEvent,
recurringCount: booking.recurringBookings.length,
})}
</p>
</div>
</Tooltip>
</div>
</div>
)}
{isPending && (
<Badge className="ltr:mr-2 rtl:ml-2" variant="orange">
{t("unconfirmed")}
</Badge>
)}
{booking.eventType?.team && (
<Badge className="ltr:mr-2 rtl:ml-2" variant="gray">
{booking.eventType.team.name}
</Badge>
)}
{!!booking?.eventType?.price && !booking.paid && (
<Badge className="ltr:mr-2 rtl:ml-2" variant="orange">
{t("pending_payment")}
</Badge>
)}
<div className="mt-2 text-sm text-gray-400">
<RecurringBookingsTooltip
booking={booking}
recurringStrings={recurringStrings}
recurringDates={recurringDates}
/>
</div>
</div>
</td>
<td className={"flex-1 px-4" + (isRejected ? " line-through" : "")} onClick={onClick}>
<div className="cursor-pointer py-4">
<div className="sm:hidden">
{isPending && <Tag className="mb-2 ltr:mr-2 rtl:ml-2">{t("unconfirmed")}</Tag>}
{!!booking?.eventType?.price && !booking.paid && (
<Tag className="mb-2 ltr:mr-2 rtl:ml-2">Pending payment</Tag>
)}
<div className="text-sm font-medium text-gray-900">
{startTime}:{" "}
<small className="text-sm text-gray-500">
{dayjs(booking.startTime).format("HH:mm")} - {dayjs(booking.endTime).format("HH:mm")}
</small>
<td className={"w-full px-4" + (isRejected ? " line-through" : "")} onClick={onClick}>
{/* Time and Badges for mobile */}
<div className="w-full pt-4 pb-2 sm:hidden">
<div className="flex w-full items-center justify-between sm:hidden">
<div className="text-sm leading-6 text-gray-900">{startTime}</div>
<div className="pr-2 text-sm text-gray-500">
{dayjs(booking.startTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")} -{" "}
{dayjs(booking.endTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")}
</div>
</div>
{isPending && (
<Badge className="ltr:mr-2 rtl:ml-2 sm:hidden" variant="orange">
{t("unconfirmed")}
</Badge>
)}
{booking.eventType?.team && (
<Badge className="ltr:mr-2 rtl:ml-2 sm:hidden" variant="gray">
{booking.eventType.team.name}
</Badge>
)}
{!!booking?.eventType?.price && !booking.paid && (
<Badge className="ltr:mr-2 rtl:ml-2 sm:hidden" variant="orange">
{t("pending_payment")}
</Badge>
)}
<div className="text-sm text-gray-400 sm:hidden">
<RecurringBookingsTooltip
booking={booking}
recurringStrings={recurringStrings}
recurringDates={recurringDates}
/>
</div>
</div>
<div className="cursor-pointer py-4">
<div
title={booking.title}
className={classNames(
"max-w-56 truncate text-sm font-medium leading-6 text-neutral-900 md:max-w-max",
"max-w-10/12 sm:max-w-56 text-sm font-medium leading-6 text-neutral-900 md:max-w-full",
isCancelled ? "line-through" : ""
)}>
{booking.eventType?.team && <strong>{booking.eventType.team.name}: </strong>}
{booking.title}
<span> </span>
{!!booking?.eventType?.price && !booking.paid && (
<Tag className="hidden ltr:ml-2 rtl:mr-2 sm:inline-flex">Pending payment</Tag>
)}
{isPending && <Tag className="hidden ltr:ml-2 rtl:mr-2 sm:inline-flex">{t("unconfirmed")}</Tag>}
</div>
{booking.description && (
<div
className="max-w-52 md:max-w-96 truncate text-sm text-gray-500"
className="max-w-52 md:max-w-96 truncate text-sm text-gray-600"
title={booking.description}>
&quot;{booking.description}&quot;
</div>
)}
{booking.attendees.length !== 0 && (
<a
className="text-sm text-gray-900 hover:text-blue-500"
href={"mailto:" + booking.attendees[0].email}
onClick={(e) => e.stopPropagation()}>
{booking.attendees[0].email}
</a>
<DisplayAttendees
attendees={booking.attendees}
user={booking.user}
currentEmail={user?.email}
/>
)}
{isCancelled && booking.rescheduled && (
<div className="mt-2 inline-block text-left text-sm md:hidden">
@@ -352,8 +357,7 @@ function BookingListItem(booking: BookingItemProps) {
)}
</div>
</td>
<td className="whitespace-nowrap py-4 text-right text-sm font-medium ltr:pr-4 rtl:pl-4">
<td className="py-4 pl-4 text-right text-sm font-medium ltr:pr-4 rtl:pl-4 sm:pl-0">
{isUpcoming && !isCancelled ? (
<>
{isPending && user?.id === booking.user?.id && <TableActions actions={pendingActions} />}
@@ -361,6 +365,7 @@ function BookingListItem(booking: BookingItemProps) {
{isRejected && <div className="text-sm text-gray-500">{t("rejected")}</div>}
</>
) : null}
{isPast && isPending && !isConfirmed ? <TableActions actions={bookedActions} /> : null}
{isCancelled && booking.rescheduled && (
<div className="hidden h-full items-center md:flex">
<RequestSentMessage />
@@ -372,6 +377,145 @@ function BookingListItem(booking: BookingItemProps) {
);
}
interface RecurringBookingsTooltipProps {
booking: BookingItemProps;
recurringStrings: string[];
recurringDates: Date[];
}
const RecurringBookingsTooltip = ({
booking,
recurringStrings,
recurringDates,
}: RecurringBookingsTooltipProps) => {
const { t } = useLocale();
const now = new Date();
return (
(booking.recurringBookings &&
booking.eventType?.recurringEvent?.freq &&
(booking.listingStatus === "recurring" ||
booking.listingStatus === "unconfirmed" ||
booking.listingStatus === "cancelled") && (
<div className="underline decoration-gray-400 decoration-dashed underline-offset-2">
<div className="flex">
<Tooltip
content={recurringStrings.map((aDate, key) => (
<p key={key} className={classNames(recurringDates[key] < now && "line-through")}>
{aDate}
</p>
))}>
<div className="text-gray-600 dark:text-white">
<Icon.FiRefreshCcw
strokeWidth="3"
className="float-left mr-1 mt-1.5 inline-block h-3 w-3 text-gray-400"
/>
<p className="mt-1 pl-5 text-xs">
{booking.status === BookingStatus.ACCEPTED
? `${t("event_remaining", {
count: recurringDates.length,
})}`
: getEveryFreqFor({
t,
recurringEvent: booking.eventType.recurringEvent,
recurringCount: recurringDates.filter((date) => {
return date >= now;
}).length,
})}
</p>
</div>
</Tooltip>
</div>
</div>
)) ||
null
);
};
interface UserProps {
id: number;
name: string | null;
email: string;
}
const FirstAttendee = ({
user,
currentEmail,
}: {
user: UserProps;
currentEmail: string | null | undefined;
}) => {
return user.email === currentEmail ? (
<div className="inline-block">You</div>
) : (
<a
key={user.email}
className=" hover:text-blue-500"
href={"mailto:" + user.email}
onClick={(e) => e.stopPropagation()}>
{user.name}
</a>
);
};
const Attendee: React.FC<{ email: string; children: React.ReactNode }> = ({ email, children }) => {
return (
<a className=" hover:text-blue-500" href={"mailto:" + email} onClick={(e) => e.stopPropagation()}>
{children}
</a>
);
};
interface AttendeeProps {
name: string;
email: string;
}
const DisplayAttendees = ({
attendees,
user,
currentEmail,
}: {
attendees: AttendeeProps[];
user: UserProps | null;
currentEmail: string | null | undefined;
}) => {
if (attendees.length === 1) {
return (
<div className="text-sm text-gray-900">
{user && <FirstAttendee user={user} currentEmail={currentEmail} />}
<span>&nbsp;and&nbsp;</span>
<Attendee email={attendees[0].email}>{attendees[0].name}</Attendee>
</div>
);
} else if (attendees.length === 2) {
return (
<div className="text-sm text-gray-900">
{user && <FirstAttendee user={user} currentEmail={currentEmail} />}
<span>,&nbsp;</span>
<Attendee email={attendees[0].email}>{attendees[0].name}</Attendee>
<div className="inline-block text-sm text-gray-900">&nbsp;and&nbsp;</div>
<Attendee email={attendees[1].email}>{attendees[1].name}</Attendee>
</div>
);
} else {
return (
<div className="text-sm text-gray-900">
{user && <FirstAttendee user={user} currentEmail={currentEmail} />}
<span>,&nbsp;</span>
<Attendee email={attendees[0].email}>{attendees[0].name}</Attendee>
<span>&nbsp;&&nbsp;</span>
<Tooltip
content={attendees.slice(1).map((attendee, key) => (
<p key={key}>{attendee.name}</p>
))}>
<div className="inline-block">{attendees.length - 1} more</div>
</Tooltip>
</div>
);
}
};
const Tag = ({ children, className = "" }: React.PropsWithChildren<{ className?: string }>) => {
return (
<span
@@ -2,9 +2,10 @@ import React from "react";
import { SkeletonText } from "@calcom/ui";
/** @deprecated Use `apps/web/components/v2/bookings/SkeletonLoader.tsx` */
function SkeletonLoader() {
return (
<ul className="mx-6 mt-6 animate-pulse divide-y divide-neutral-200 border border-gray-200 bg-white sm:overflow-hidden">
<ul className="animate-pulse divide-y divide-neutral-200 rounded-md border border-gray-200 bg-white sm:overflow-hidden">
<SkeletonItem />
<SkeletonItem />
<SkeletonItem />
@@ -16,7 +17,7 @@ export default SkeletonLoader;
function SkeletonItem() {
return (
<li className="group flex w-full items-center justify-between px-2 py-4 sm:px-6">
<li className="group flex w-full items-center justify-between px-4 py-4 sm:px-6">
<div className="flex-grow truncate text-sm">
<div className="flex">
<div className="flex flex-col space-y-2">
+18 -15
View File
@@ -1,8 +1,8 @@
import { FC, useEffect, useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import Switch from "@calcom/ui/Switch";
import TimezoneSelect, { ITimezoneOption } from "@calcom/ui/form/TimezoneSelect";
import { Switch } from "@calcom/ui/v2";
import TimezoneSelect, { ITimezoneOption } from "@calcom/ui/v2/core/TimezoneSelect";
import { is24h, timeZone } from "../../lib/clock";
@@ -10,9 +10,10 @@ type Props = {
onSelectTimeZone: (selectedTimeZone: string) => void;
onToggle24hClock: (is24hClock: boolean) => void;
timeFormat: string;
hideTimeFormatToggle?: boolean;
};
const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone, timeFormat }) => {
const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone, timeFormat, hideTimeFormatToggle }) => {
const [selectedTimeZone, setSelectedTimeZone] = useState("");
const [is24hClock, setIs24hClock] = useState(timeFormat === "HH:mm" && true);
const { t } = useLocale();
@@ -33,20 +34,22 @@ const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone, timeFormat
};
return selectedTimeZone !== "" ? (
<div className="max-w-80 dark:border-darkgray-300 dark:bg-darkgray-200 absolute z-10 w-full rounded-sm border border-gray-200 bg-white px-4 pt-4 pb-3 shadow-sm">
<div className="dark:border-darkgray-300 dark:bg-darkgray-200 rounded-sm border border-gray-200 bg-white px-4 pt-4 pb-3 shadow-sm">
<div className="mb-4 flex">
<div className="text-sm font-medium text-gray-600 dark:text-white">{t("time_options")}</div>
<div className="ml-auto flex items-center">
<label className="ltl:mr-3 mr-2 align-text-top text-sm font-medium text-neutral-700 ltr:ml-3 rtl:mr-3 dark:text-white">
{t("am_pm")}
</label>
<Switch
name="24hClock"
label={t("24_h")}
defaultChecked={is24hClock}
onCheckedChange={handle24hClockToggle}
/>
</div>
{!hideTimeFormatToggle && (
<div className="ml-auto flex items-center">
<label className="ltl:mr-3 mr-2 align-text-top text-sm font-medium text-neutral-700 ltr:ml-3 rtl:mr-3 dark:text-white">
{t("am_pm")}
</label>
<Switch
name="24hClock"
label={t("24_h")}
defaultChecked={is24hClock}
onCheckedChange={handle24hClockToggle}
/>
</div>
)}
</div>
<TimezoneSelect
id="timeZone"
@@ -1,10 +1,11 @@
// Get router variables
import autoAnimate from "@formkit/auto-animate";
import { EventType } from "@prisma/client";
import { SchedulingType } from "@prisma/client";
import * as Collapsible from "@radix-ui/react-collapsible";
import * as Popover from "@radix-ui/react-popover";
import { TFunction } from "next-i18next";
import { useRouter } from "next/router";
import { useReducer, useEffect, useMemo, useState } from "react";
import { useReducer, useEffect, useMemo, useState, useRef } from "react";
import { Toaster } from "react-hot-toast";
import { FormattedNumber, IntlProvider } from "react-intl";
import { z } from "zod";
@@ -24,8 +25,7 @@ import useTheme from "@calcom/lib/hooks/useTheme";
import notEmpty from "@calcom/lib/notEmpty";
import { getRecurringFreq } from "@calcom/lib/recurringStrings";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
import { localStorage } from "@calcom/lib/webstorage";
import { detectBrowserTimeFormat, getIs24hClockFromLocalStorage } from "@calcom/lib/timeFormat";
import { trpc } from "@calcom/trpc/react";
import { Icon } from "@calcom/ui/Icon";
import DatePicker from "@calcom/ui/v2/modules/booker/DatePicker";
@@ -79,7 +79,7 @@ const useSlots = ({
usernameList: string[];
timeZone?: string;
}) => {
const { data, isLoading, isIdle } = trpc.useQuery(
const { data, isLoading, isPaused } = trpc.useQuery(
[
"viewer.public.slots.getSchedule",
{
@@ -101,8 +101,8 @@ const useSlots = ({
}
}, [data]);
// The very first time isIdle is set if auto-fetch is disabled, so isIdle should also be considered a loading state.
return { slots: cachedSlots, isLoading: isLoading || isIdle };
// The very first time isPaused is set if auto-fetch is disabled, so isPaused should also be considered a loading state.
return { slots: cachedSlots, isLoading: isLoading || isPaused };
};
const SlotPicker = ({
@@ -129,6 +129,11 @@ const SlotPicker = ({
const { date, setQuery: setDate } = useRouterQuery("date");
const { month, setQuery: setMonth } = useRouterQuery("month");
const router = useRouter();
const slotPickerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
slotPickerRef.current && autoAnimate(slotPickerRef.current);
}, [slotPickerRef]);
useEffect(() => {
if (!router.isReady) return;
@@ -169,17 +174,15 @@ const SlotPicker = ({
timeZone,
});
const slots = useMemo(() => ({ ..._1, ..._2 }), [_1, _2]);
const slots = useMemo(() => ({ ..._2, ..._1 }), [_1, _2]);
return (
<>
<DatePicker
isLoading={isLoading}
className={classNames(
"mt-8 w-full px-4 sm:mt-0 sm:min-w-[455px] md:px-5",
selectedDate
? "sm:dark:border-darkgray-200 border-gray-200 sm:w-1/2 sm:border-r sm:p-4 sm:pr-6 md:w-1/3 "
: "sm:p-4"
"mt-8 px-4 pb-4 sm:mt-0 md:min-w-[300px] md:px-5 lg:min-w-[455px]",
selectedDate ? "sm:dark:border-darkgray-200 border-gray-200 sm:border-r sm:p-4 sm:pr-6" : "sm:p-4"
)}
includedDates={Object.keys(slots).filter((k) => slots[k].length > 0)}
locale={isLocaleReady ? i18n.language : "en"}
@@ -194,10 +197,10 @@ const SlotPicker = ({
weekStart={weekStart}
/>
{selectedDate && (
<div ref={slotPickerRef}>
<AvailableTimes
isLoading={isLoading}
slots={slots[selectedDate.format("YYYY-MM-DD")]}
slots={selectedDate && slots[selectedDate.format("YYYY-MM-DD")]}
date={selectedDate}
timeFormat={timeFormat}
eventTypeId={eventType.id}
@@ -206,7 +209,7 @@ const SlotPicker = ({
recurringCount={recurringEventCount}
ethSignature={ethSignature}
/>
)}
</div>
</>
);
};
@@ -216,16 +219,18 @@ function TimezoneDropdown({
onChangeTimeZone,
timeZone,
timeFormat,
hideTimeFormatToggle,
}: {
onChangeTimeFormat: (newTimeFormat: string) => void;
onChangeTimeZone: (newTimeZone: string) => void;
timeZone?: string;
timeFormat: string;
hideTimeFormatToggle?: boolean;
}) {
const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false);
useEffect(() => {
handleToggle24hClock(localStorage.getItem("timeOption.is24hClock") === "true");
handleToggle24hClock(!!getIs24hClockFromLocalStorage());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -241,26 +246,32 @@ function TimezoneDropdown({
};
return (
<Collapsible.Root open={isTimeOptionsOpen} onOpenChange={setIsTimeOptionsOpen} className="flex">
<Collapsible.Trigger className="min-w-32 dark:text-darkgray-600 mb-2 -ml-2 px-2 text-left text-gray-600">
<Popover.Root open={isTimeOptionsOpen} onOpenChange={setIsTimeOptionsOpen}>
<Popover.Trigger className="min-w-32 dark:text-darkgray-600 radix-state-open:bg-gray-200 dark:radix-state-open:bg-darkgray-200 group relative mb-2 -ml-2 inline-block rounded-md px-2 py-2 text-left text-gray-600">
<p className="text-sm font-medium">
<Icon.FiGlobe className="mr-[10px] ml-[2px] -mt-[2px] inline-block h-4 w-4" />
{timeZone}
{isTimeOptionsOpen ? (
<Icon.FiChevronUp className="ml-1 inline-block h-4 w-4 " />
<Icon.FiChevronUp className="ml-1 inline-block h-4 w-4" />
) : (
<Icon.FiChevronDown className="ml-1 inline-block h-4 w-4 " />
<Icon.FiChevronDown className="ml-1 inline-block h-4 w-4" />
)}
</p>
</Collapsible.Trigger>
<Collapsible.Content>
<TimeOptions
onSelectTimeZone={handleSelectTimeZone}
onToggle24hClock={handleToggle24hClock}
timeFormat={timeFormat}
/>
</Collapsible.Content>
</Collapsible.Root>
</Popover.Trigger>
<Popover.Portal>
<Popover.Content
hideWhenDetached
align="start"
className="animate-fade-in-up absolute left-0 top-2 w-80 max-w-[calc(100vw_-_1.5rem)]">
<TimeOptions
onSelectTimeZone={handleSelectTimeZone}
onToggle24hClock={handleToggle24hClock}
timeFormat={timeFormat}
hideTimeFormatToggle={hideTimeFormatToggle}
/>
</Popover.Content>
</Popover.Portal>
</Popover.Root>
);
}
@@ -289,7 +300,14 @@ const useRouterQuery = <T extends string>(name: T) => {
export type Props = AvailabilityTeamPageProps | AvailabilityPageProps | DynamicAvailabilityPageProps;
const timeFormatTotimeFormatString = (timeFormat?: number | null) => {
if (!timeFormat) return null;
return timeFormat === 24 ? "HH:mm" : "h:mma";
};
const AvailabilityPage = ({ profile, eventType }: Props) => {
const { data: user } = trpc.useQuery(["viewer.me"]);
const timeFormatFromProfile = timeFormatTotimeFormatString(user?.timeFormat);
const router = useRouter();
const isEmbed = useIsEmbed();
const query = dateQuerySchema.parse(router.query);
@@ -302,8 +320,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
const isBackgroundTransparent = useIsBackgroundTransparent();
const [timeZone, setTimeZone] = useState<string>();
const [timeFormat, setTimeFormat] = useState(detectBrowserTimeFormat);
const [isAvailableTimesVisible, setIsAvailableTimesVisible] = useState<boolean>();
const [timeFormat, setTimeFormat] = useState(timeFormatFromProfile || detectBrowserTimeFormat);
const [gateState, gateDispatcher] = useReducer(
(state: GateState, newState: Partial<GateState>) => ({
...state,
@@ -316,10 +333,6 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
setTimeZone(localStorageTimeZone() || dayjs.tz.guess());
}, []);
useEffect(() => {
setIsAvailableTimesVisible(!!query.date);
}, [query.date]);
// TODO: Improve this;
useExposePlanGlobally(eventType.users.length === 1 ? eventType.users[0].plan : "PRO");
@@ -338,14 +351,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
// get dynamic user list here
const userList = eventType.users ? eventType.users.map((user) => user.username).filter(notEmpty) : [];
// Recurring event sidebar requires more space
const maxWidth = isAvailableTimesVisible
? recurringEventCount
? "max-w-6xl"
: "max-w-5xl"
: recurringEventCount
? "max-w-4xl"
: "max-w-3xl";
const timezoneDropdown = useMemo(
() => (
<TimezoneDropdown
@@ -353,9 +359,12 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
onChangeTimeFormat={setTimeFormat}
timeZone={timeZone}
onChangeTimeZone={setTimeZone}
// Currently we don't allow the user to change the timeformat when they're logged in,
// the only way to change it is if they go to their profile.
hideTimeFormatToggle={!!timeFormatFromProfile}
/>
),
[timeZone, timeFormat]
[timeZone, timeFormat, timeFormatFromProfile]
);
const rawSlug = profile.slug ? profile.slug.split("/") : [];
if (rawSlug.length > 1) rawSlug.pop(); //team events have team name as slug, but user events have [user]/[type] as slug.
@@ -385,201 +394,109 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
<div>
<main
className={classNames(
shouldAlignCentrally ? "mx-auto" : "",
isEmbed
? classNames(maxWidth)
: classNames("transition-max-width mx-auto my-0 duration-500 ease-in-out md:my-24", maxWidth)
"flex flex-col",
shouldAlignCentrally ? "items-center" : "items-start",
!isEmbed && classNames("mx-auto my-0 ease-in-out md:my-24")
)}>
<div
style={availabilityDatePickerEmbedStyles}
className={classNames(
isBackgroundTransparent ? "" : "dark:bg-darkgray-100 sm:dark:border-darkgray-300 bg-white",
"border-bookinglightest rounded-md md:border",
isEmbed ? "mx-auto" : maxWidth
)}>
{/* mobile: details */}
<div className="block px-4 pt-4 sm:p-8 md:hidden">
<div>
<UserAvatars
profile={profile}
users={eventType.users}
showMembers={eventType.schedulingType !== SchedulingType.ROUND_ROBIN}
size={9}
truncateAfter={5}
/>
<div className="mt-4">
<div className="dark:text-darkgray-900 mt-2 gap-2">
<p className="break-words text-sm font-medium text-gray-600 dark:text-gray-300">
{profile.name}
</p>
<h1 className="text-bookingdark dark:text-darkgray-900 mb-4 break-words text-xl font-semibold">
{eventType.title}
</h1>
<div className="flex flex-col space-y-3">
{eventType?.description && (
<div className="dark:text-darkgray-600 flex py-1 text-sm font-medium text-gray-600">
<div>
<Icon.FiInfo className="mr-[10px] ml-[2px] inline-block h-4 w-4" />
</div>
<EventTypeDescriptionSafeHTML eventType={eventType} />
<div>
<div
style={availabilityDatePickerEmbedStyles}
className={classNames(
isBackgroundTransparent
? ""
: "dark:bg-darkgray-100 sm:dark:border-darkgray-300 bg-white pb-4 md:pb-0",
"border-bookinglightest overflow-hidden rounded-md md:border",
isEmbed && "mx-auto"
)}>
<div className="overflow-hidden md:flex">
<div
className={classNames(
"sm:dark:border-darkgray-200 flex flex-col border-gray-200 p-5 sm:border-r",
"min-w-full md:w-[280px] md:min-w-[280px]",
recurringEventCount && "xl:w-[380px] xl:min-w-[380px]"
)}>
<UserAvatars
profile={profile}
users={eventType.users}
showMembers={eventType.schedulingType !== SchedulingType.ROUND_ROBIN}
size={10}
truncateAfter={3}
/>
<h2 className="mt-2 break-words text-sm font-medium text-gray-600 dark:text-gray-300">
{profile.name}
</h2>
<h1 className="font-cal dark:text-darkgray-900 mb-6 break-words text-2xl font-semibold text-gray-900">
{eventType.title}
</h1>
<div className="dark:text-darkgray-600 flex flex-col space-y-3 text-sm font-medium text-gray-600">
{eventType?.description && (
<div className="flex">
<div>
<Icon.FiInfo className="mr-[10px] ml-[2px] inline-block h-4 w-4" />
</div>
)}
{eventType?.requiresConfirmation && (
<p className="dark:text-darkgray-600 dark:text-darkgray-600 text-gray-600">
<Icon.FiCheckSquare className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
{t("requires_confirmation")}
</p>
)}
<AvailableEventLocations locations={eventType.locations} />
<p className="dark:text-darkgray-600 text-gray-600">
<Icon.FiClock className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4" />
{eventType.length} {t("minutes")}
<EventTypeDescriptionSafeHTML eventType={eventType} />
</div>
)}
{eventType?.requiresConfirmation && (
<div className="flex items-center">
<div>
<Icon.FiCheckSquare className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 " />
</div>
{t("requires_confirmation")}
</div>
)}
<AvailableEventLocations locations={eventType.locations} />
<p className="text-sm font-medium">
<Icon.FiClock className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4" />
{eventType.length} {t("minutes")}
</p>
{!rescheduleUid && eventType.recurringEvent && (
<div className="flex items-start text-sm font-medium">
<Icon.FiRefreshCcw className="float-left mr-[10px] mt-[7px] ml-[2px] inline-block h-4 w-4 " />
<div>
<p className="mb-1 -ml-2 inline px-2 py-1">
{getRecurringFreq({ t, recurringEvent: eventType.recurringEvent })}
</p>
<input
type="number"
min="1"
max={eventType.recurringEvent.count}
className="w-15 dark:bg-darkgray-200 h-7 rounded-sm border-gray-300 bg-white text-sm font-medium [appearance:textfield] ltr:mr-2 rtl:ml-2 dark:border-gray-500"
defaultValue={eventType.recurringEvent.count}
onChange={(event) => {
setRecurringEventCount(parseInt(event?.target.value));
}}
/>
<p className="inline">
{t("occurrence", {
count: recurringEventCount,
})}
</p>
</div>
</div>
)}
{eventType.price > 0 && (
<p className="-ml-2 px-2 text-sm font-medium">
<Icon.FiCreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
<IntlProvider locale="en">
<FormattedNumber
value={eventType.price / 100.0}
style="currency"
currency={eventType.currency.toUpperCase()}
/>
</IntlProvider>
</p>
{eventType.price > 0 && (
<div className="dark:text-darkgray-600 text-gray-600">
<Icon.FiCreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
<IntlProvider locale="en">
<FormattedNumber
value={eventType.price / 100.0}
style="currency"
currency={eventType.currency.toUpperCase()}
/>
</IntlProvider>
</div>
)}
{!rescheduleUid && eventType.recurringEvent && (
<div className="dark:text-darkgray-600 flex items-center text-gray-600">
<Icon.FiRefreshCcw className="float-left mr-[10px] mt-1 ml-[2px] inline-block h-4 w-4 shrink-0 text-gray-500" />
<div>
<p className="mb-1 -ml-2 inline px-2 py-1">
{getRecurringFreq({ t, recurringEvent: eventType.recurringEvent })}
</p>
<input
type="number"
min="1"
max={eventType.recurringEvent.count}
className="w-15 dark:text-darkgray-600 h-7 rounded-sm border-gray-300 bg-white text-sm text-gray-600 [appearance:textfield] ltr:mr-2 rtl:ml-2 dark:border-gray-500 dark:bg-gray-600"
defaultValue={eventType.recurringEvent.count}
onChange={(event) => {
setRecurringEventCount(parseInt(event?.target.value));
}}
/>
<p className="dark:text-darkgray-600 inline text-gray-600 ">
{t("occurrence", {
count: recurringEventCount,
})}
</p>
</div>
</div>
)}
{timezoneDropdown}
<div className="md:hidden">
{/* Temp disabled booking?.startTime && rescheduleUid && (
<div>
<p
className="mt-8 text-gray-600 dark:text-darkgray-600"
data-testid="former_time_p_mobile">
{t("former_time")}
</p>
<p className="text-gray-500 line-through dark:text-darkgray-600">
<CalendarIcon className="mr-[10px] -mt-1 inline-block h-4 w-4 text-gray-500" />
{typeof booking.startTime === "string" &&
parseDate(dayjs(booking.startTime), i18n)}
</p>
</div>
)*/}
</div>
</div>
)}
{timezoneDropdown}
</div>
</div>
</div>
</div>
<div className="overflow-hidden sm:flex">
<div
className={
"sm:dark:border-darkgray-200 hidden overflow-hidden border-gray-200 p-5 sm:border-r md:flex md:flex-col " +
(isAvailableTimesVisible ? "sm:w-1/3" : recurringEventCount ? "sm:w-2/3" : "sm:w-1/2")
}>
<UserAvatars
profile={profile}
users={eventType.users}
showMembers={eventType.schedulingType !== SchedulingType.ROUND_ROBIN}
size={10}
truncateAfter={3}
/>
<h2 className="break-words text-sm font-medium text-gray-600 dark:text-gray-300 lg:mt-2">
{profile.name}
</h2>
<h1 className="font-cal dark:text-darkgray-900 mb-6 break-words text-2xl text-gray-900 ">
{eventType.title}
</h1>
<div className="dark:text-darkgray-600 flex flex-col space-y-3 text-sm font-medium text-gray-600">
{eventType?.description && (
<div className="flex ">
<div>
<Icon.FiInfo className="mr-[10px] ml-[2px] inline-block h-4 w-4" />
</div>
<EventTypeDescriptionSafeHTML eventType={eventType} />
{!isEmbed && (
<div className="mt-auto hidden md:block">
<GoBackToPreviousPage t={t} />
</div>
)}
{eventType?.requiresConfirmation && (
<div className="flex items-center">
<div>
<Icon.FiCheckSquare className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 " />
</div>
{t("requires_confirmation")}
</div>
)}
<AvailableEventLocations locations={eventType.locations} />
<p className="text-sm font-medium">
<Icon.FiClock className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4" />
{eventType.length} {t("minutes")}
</p>
{!rescheduleUid && eventType.recurringEvent && (
<div className="flex items-start text-sm font-medium">
<Icon.FiRefreshCcw className="float-left mr-[10px] mt-[7px] ml-[2px] inline-block h-4 w-4 " />
<div>
<p className="mb-1 -ml-2 inline px-2 py-1">
{getRecurringFreq({ t, recurringEvent: eventType.recurringEvent })}
</p>
<input
type="number"
min="1"
max={eventType.recurringEvent.count}
className="w-15 dark:bg-darkgray-200 h-7 rounded-sm border-gray-300 bg-white text-sm font-medium [appearance:textfield] ltr:mr-2 rtl:ml-2 dark:border-gray-500"
defaultValue={eventType.recurringEvent.count}
onChange={(event) => {
setRecurringEventCount(parseInt(event?.target.value));
}}
/>
<p className="inline">
{t("occurrence", {
count: recurringEventCount,
})}
</p>
</div>
</div>
)}
{eventType.price > 0 && (
<p className="-ml-2 px-2 text-sm font-medium ">
<Icon.FiCreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
<IntlProvider locale="en">
<FormattedNumber
value={eventType.price / 100.0}
style="currency"
currency={eventType.currency.toUpperCase()}
/>
</IntlProvider>
</p>
)}
{timezoneDropdown}
</div>
{!isEmbed && <GoBackToPreviousPage t={t} />}
{/* Temporarily disabled - booking?.startTime && rescheduleUid && (
{/* Temporarily disabled - booking?.startTime && rescheduleUid && (
<div>
<p
className="mt-4 mb-3 text-gray-600 dark:text-darkgray-600"
@@ -592,26 +509,33 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
</p>
</div>
)*/}
</div>
<SlotPicker
weekStart={
typeof profile.weekStart === "string"
? ([
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
].indexOf(profile.weekStart) as 0 | 1 | 2 | 3 | 4 | 5 | 6)
: profile.weekStart /* Allows providing weekStart as number */
}
eventType={eventType}
timeFormat={timeFormat}
timeZone={timeZone}
users={userList}
seatsPerTimeSlot={eventType.seatsPerTimeSlot || undefined}
recurringEventCount={recurringEventCount}
ethSignature={gateState.rainbowToken}
/>
</div>
<SlotPicker
weekStart={
typeof profile.weekStart === "string"
? (["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"].indexOf(
profile.weekStart
) as 0 | 1 | 2 | 3 | 4 | 5 | 6)
: profile.weekStart /* Allows providing weekStart as number */
}
eventType={eventType}
timeFormat={timeFormat}
timeZone={timeZone}
users={userList}
seatsPerTimeSlot={eventType.seatsPerTimeSlot || undefined}
recurringEventCount={recurringEventCount}
ethSignature={gateState.rainbowToken}
/>
</div>
{(!eventType.users[0] || !isBrandingHidden(eventType.users[0])) && !isEmbed && <PoweredByCal />}
</div>
{(!eventType.users[0] || !isBrandingHidden(eventType.users[0])) && !isEmbed && <PoweredByCal />}
</main>
</div>
<Toaster position="bottom-right" />
@@ -1,6 +1,7 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { EventTypeCustomInputType, WorkflowActions } from "@prisma/client";
import { SchedulingType } from "@prisma/client";
import { useMutation } from "@tanstack/react-query";
import { isValidPhoneNumber } from "libphonenumber-js";
import { useSession } from "next-auth/react";
import Head from "next/head";
@@ -9,7 +10,6 @@ import { useEffect, useMemo, useState, useReducer } from "react";
import { Controller, useForm, useWatch } from "react-hook-form";
import { FormattedNumber, IntlProvider } from "react-intl";
import { ReactMultiEmail } from "react-multi-email";
import { useMutation } from "react-query";
import { v4 as uuidv4 } from "uuid";
import { z } from "zod";
@@ -34,11 +34,11 @@ import useTheme from "@calcom/lib/hooks/useTheme";
import { HttpError } from "@calcom/lib/http-error";
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { Button } from "@calcom/ui/Button";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
import PhoneInput from "@calcom/ui/form/PhoneInputLazy";
import { EmailInput, Form } from "@calcom/ui/form/fields";
import { Button } from "@calcom/ui/v2";
import { asStringOrNull } from "@lib/asStringOrNull";
import { timeZone } from "@lib/clock";
@@ -199,16 +199,12 @@ const BookingPage = ({
// There should only exists one default userData variable for primaryAttendee.
const defaultUserValues = {
email: booking?.attendees[0].email
? booking.attendees[0].email
email: rescheduleUid
? booking?.attendees[0].email
: router.query.email
? (router.query.email as string)
: "",
name: booking?.attendees[0].name
? booking.attendees[0].name
: router.query.name
? (router.query.name as string)
: "",
name: rescheduleUid ? booking?.attendees[0].name : router.query.name ? (router.query.name as string) : "",
};
const defaultValues = () => {
@@ -242,6 +238,7 @@ const BookingPage = ({
guests: guestListEmails,
notes: booking.description || "",
rescheduleReason: "",
smsReminderNumber: booking.smsReminderNumber || undefined,
customInputs: eventType.customInputs.reduce(
(customInputs, input) => ({
...customInputs,
@@ -448,7 +445,6 @@ const BookingPage = ({
<div
className={classNames(
"main overflow-hidden",
isEmbed ? "" : "border border-gray-200",
isBackgroundTransparent ? "" : "dark:border-1 dark:bg-darkgray-100 bg-white",
"dark:border-darkgray-300 rounded-md sm:border"
)}>
@@ -526,7 +522,7 @@ const BookingPage = ({
content={recurringStrings.slice(5).map((aDate, key) => (
<p key={key}>{aDate}</p>
))}>
<p className="dark:text-darkgray-600 text-sm">
<p className="dark:text-darkgray-600 text-sm">
{t("plus_more", { count: recurringStrings.length - 5 })}
</p>
</Tooltip>
@@ -594,9 +590,7 @@ const BookingPage = ({
required
className={classNames(
inputClassName,
bookingForm.formState.errors.email
? "border-red-700 focus:ring-red-700"
: " border-gray-300 dark:border-gray-900"
bookingForm.formState.errors.email && "!focus:ring-red-700 !border-red-700"
)}
placeholder="you@example.com"
type="search" // Disables annoying 1password intrusive popup (non-optimal, I know I know...)
@@ -718,90 +712,81 @@ const BookingPage = ({
/>
)}
{input.type === EventTypeCustomInputType.BOOL && (
<div className="flex h-5 items-center">
<input
type="checkbox"
{...bookingForm.register(`customInputs.${input.id}`, {
required: input.required,
})}
required={input.required}
id={"custom_" + input.id}
className="h-4 w-4 rounded border-gray-300 text-black focus:ring-black disabled:bg-gray-200 ltr:mr-2 rtl:ml-2 disabled:dark:text-gray-500"
placeholder=""
disabled={disabledExceptForOwner}
/>
<label
htmlFor={"custom_" + input.id}
className="mb-1 block text-sm font-medium text-gray-700 dark:text-white">
{input.label}
</label>
<div className="my-6">
<div className="flex">
<input
type="checkbox"
{...bookingForm.register(`customInputs.${input.id}`, {
required: input.required,
})}
required={input.required}
id={"custom_" + input.id}
className="h-4 w-4 rounded border-gray-300 text-black focus:ring-black disabled:bg-gray-200 ltr:mr-2 rtl:ml-2 disabled:dark:text-gray-500"
placeholder=""
disabled={disabledExceptForOwner}
/>
<label
htmlFor={"custom_" + input.id}
className="-mt-px block text-sm font-medium text-gray-700 dark:text-white">
{input.label}
</label>
</div>
</div>
)}
</div>
))}
{!eventType.disableGuests && (
{!eventType.disableGuests && guestToggle && (
<div className="mb-4">
{!guestToggle && (
<div>
<label
onClick={() => setGuestToggle(!guestToggle)}
htmlFor="guests"
className="mb-1 block text-sm font-medium hover:cursor-pointer dark:text-white">
{/*<UserAddIcon className="inline-block w-5 h-5 mr-1 -mt-1" />*/}
{t("additional_guests")}
className="mb-1 block text-sm font-medium text-gray-700 dark:text-white">
{t("guests")}
</label>
)}
{guestToggle && (
<div>
<label
htmlFor="guests"
className="mb-1 block text-sm font-medium text-gray-700 dark:text-white">
{t("guests")}
</label>
{!disableInput && (
<Controller
control={bookingForm.control}
name="guests"
render={({ field: { onChange, value } }) => (
<ReactMultiEmail
className="relative"
placeholder="guest@example.com"
emails={value}
onChange={onChange}
getLabel={(
email: string,
index: number,
removeEmail: (index: number) => void
) => {
return (
<div data-tag key={index} className="cursor-pointer">
{email}
{!disableInput && (
<span data-tag-handle onClick={() => removeEmail(index)}>
×
</span>
)}
</div>
);
}}
/>
)}
/>
)}
{/* Custom code when guest emails should not be editable */}
{disableInput && guestListEmails && guestListEmails.length > 0 && (
<div data-tag className="react-multi-email">
{/* // @TODO: user owners are appearing as guest here when should be only user input */}
{guestListEmails.map((email, index) => {
return (
<div key={index} className="cursor-pointer">
<span data-tag>{email}</span>
</div>
);
})}
</div>
)}
</div>
)}
{!disableInput && (
<Controller
control={bookingForm.control}
name="guests"
render={({ field: { onChange, value } }) => (
<ReactMultiEmail
className="relative"
placeholder={<span className="dark:text-darkgray-600">guest@example.com</span>}
emails={value}
onChange={onChange}
getLabel={(
email: string,
index: number,
removeEmail: (index: number) => void
) => {
return (
<div data-tag key={index} className="cursor-pointer">
{email}
{!disableInput && (
<span data-tag-handle onClick={() => removeEmail(index)}>
×
</span>
)}
</div>
);
}}
/>
)}
/>
)}
{/* Custom code when guest emails should not be editable */}
{disableInput && guestListEmails && guestListEmails.length > 0 && (
<div data-tag className="react-multi-email">
{/* // @TODO: user owners are appearing as guest here when should be only user input */}
{guestListEmails.map((email, index) => {
return (
<div key={index} className="cursor-pointer">
<span data-tag>{email}</span>
</div>
);
})}
</div>
)}
</div>
</div>
)}
{isSmsReminderNumberNeeded && selectedLocationType !== LocationType.Phone && (
@@ -818,7 +803,6 @@ const BookingPage = ({
placeholder={t("enter_phone_number")}
id="smsReminderNumber"
required
disabled={disableInput}
/>
</div>
{bookingForm.formState.errors.smsReminderNumber && (
@@ -858,17 +842,28 @@ const BookingPage = ({
</div>
<div className="flex justify-end space-x-2 rtl:space-x-reverse">
{!eventType.disableGuests && !guestToggle && (
<Button
type="button"
color="minimalSecondary"
size="icon"
tooltip={t("additional_guests")}
StartIcon={Icon.FiUserPlus}
onClick={() => setGuestToggle(!guestToggle)}
className="mr-auto"
/>
)}
<Button
color="secondary"
color="minimal"
type="button"
onClick={() => router.back()}
// We override this for this component only for now - as we don't support darkmode everywhere in the app
className="dark:border-none">
className="dark:hover:bg-darkgray-200 dark:border-none dark:text-white">
{t("cancel")}
</Button>
<Button
type="submit"
className="dark:bg-darkmodebrand dark:text-darkmodebrandcontrast rounded-md"
className="dark:bg-darkmodebrand dark:text-darkmodebrandcontrast dark:hover:border-darkmodebrandcontrast mr-auto dark:border-transparent"
data-testid={rescheduleUid ? "confirm-reschedule-button" : "confirm-book-button"}
loading={mutation.isLoading || recurringMutation.isLoading}>
{rescheduleUid ? t("reschedule") : t("confirm")}
@@ -1,6 +1,6 @@
import { useMutation } from "@tanstack/react-query";
import { RescheduleResponse } from "pages/api/book/request-reschedule";
import React, { useState, Dispatch, SetStateAction } from "react";
import { useMutation } from "react-query";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
@@ -7,6 +7,7 @@ import { useForm } from "react-hook-form";
import type { z } from "zod";
import classNames from "@calcom/lib/classNames";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { createEventTypeInput } from "@calcom/prisma/zod/custom/eventtype";
@@ -338,7 +339,7 @@ function CreateEventTeamsItem(props: {
onSelect={() => props.openModal(props.option)}>
<Avatar
alt={props.option.name || ""}
imageSrc={props.option.image}
imageSrc={props.option.image || WEBAPP_URL + "/" + props.option.slug + "/avatar.png"}
size={6}
className="inline ltr:mr-2 rtl:ml-2"
/>
@@ -32,7 +32,7 @@ export const EventTypeDescription = ({ eventType, className }: EventTypeDescript
<>
<div className={classNames("text-gray-600 dark:text-white", className)}>
{eventType.description && (
<h2 className="max-w-[280px] overflow-hidden text-ellipsis opacity-60 sm:max-w-[500px]">
<h2 className="max-w-[280px] overflow-hidden text-ellipsis sm:max-w-[500px]">
{eventType.description.substring(0, 100)}
{eventType.description.length > 100 && "..."}
</h2>
@@ -47,8 +47,8 @@ export default function RecurringEventController({
<Alert severity="warning" title={t("warning_payment_recurring_event")} />
) : (
<>
<div className="relative flex items-start">
<div className="flex h-5 items-center">
<div className="flex items-start">
<div className="flex h-5 items-start">
<input
onChange={(event) => {
onRecurringEventDefined(event?.target.checked);
@@ -1,19 +1,18 @@
import React from "react";
import { SkeletonAvatar, SkeletonContainer, SkeletonText } from "@calcom/ui";
import { Icon } from "@calcom/ui/Icon";
/** @deprecated Use `apps/web/components/v2/eventtype/SkeletonLoader.tsx` */
function SkeletonLoader() {
return (
<SkeletonContainer>
<div className="mb-4 flex items-center">
<SkeletonAvatar width="8" height="8" />
<div className="space-y-1">
<SkeletonText height="4" width="16" />
<SkeletonText height="4" width="24" />
<SkeletonText className="h-4 w-16" />
<SkeletonText className="h-4 w-24" />
</div>
</div>
<ul className="divide-y divide-neutral-200 border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden">
<ul className="divide-y divide-neutral-200 rounded-md border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden">
<SkeletonItem />
<SkeletonItem />
<SkeletonItem />
@@ -44,19 +43,6 @@ function SkeletonItem() {
</ul>
</div>
</div>
<div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 sm:flex">
<div className="flex justify-between rtl:space-x-reverse">
<div className="cursor-pointer appearance-none rounded-sm border border-transparent p-2 text-neutral-500 hover:border-gray-300 hover:text-neutral-900">
<Icon.FiExternalLink className="h-5 w-5" />
</div>
<div className="cursor-pointer appearance-none rounded-sm border border-transparent p-2 text-neutral-500 hover:border-gray-300 hover:text-neutral-900">
<Icon.FiLink className="h-5 w-5" />
</div>
<div className="cursor-pointer appearance-none rounded-sm border border-transparent p-2 text-neutral-500 hover:border-gray-300 hover:text-neutral-900">
<Icon.FiMoreHorizontal className="h-5 w-5" />
</div>
</div>
</div>
</li>
);
}
@@ -0,0 +1,42 @@
import { InstallAppButtonWithoutPlanCheck } from "@calcom/app-store/components";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { App } from "@calcom/types/App";
import Button from "@calcom/ui/v2/core/Button";
interface ICalendarItem {
title: string;
description?: string;
imageSrc: string;
type: App["type"];
}
const CalendarItem = (props: ICalendarItem) => {
const { title, imageSrc, type } = props;
const { t } = useLocale();
return (
<div className="flex flex-row items-center justify-between p-5">
<div className="flex items-center space-x-3">
<img src={imageSrc} alt={title} className="h-8 w-8" />
<p className="text-sm font-bold">{title}</p>
</div>
<InstallAppButtonWithoutPlanCheck
type={type}
render={(buttonProps) => (
<Button
{...buttonProps}
color="secondary"
type="button"
onClick={(event) => {
// Save cookie key to return url step
document.cookie = `return-to=${window.location.href};path=/;max-age=3600;SameSite=Lax`;
buttonProps && buttonProps.onClick && buttonProps?.onClick(event);
}}>
{t("connect")}
</Button>
)}
/>
</div>
);
};
export { CalendarItem };
@@ -0,0 +1,86 @@
import { useMutation } from "@tanstack/react-query";
import showToast from "@calcom/lib/notification";
import { trpc } from "@calcom/trpc/react";
import { Switch } from "@calcom/ui/v2";
import classNames from "@lib/classNames";
interface ICalendarSwitchProps {
title: string;
externalId: string;
type: string;
isChecked: boolean;
name: string;
isLastItemInList?: boolean;
}
const CalendarSwitch = (props: ICalendarSwitchProps) => {
const { title, externalId, type, isChecked, name, isLastItemInList = false } = props;
const utils = trpc.useContext();
const mutation = useMutation<
unknown,
unknown,
{
isOn: boolean;
}
>(
async ({ isOn }: { isOn: boolean }) => {
const body = {
integration: type,
externalId: externalId,
};
if (isOn) {
const res = await fetch("/api/availability/calendar", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) {
throw new Error("Something went wrong");
}
} else {
const res = await fetch("/api/availability/calendar", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) {
throw new Error("Something went wrong");
}
}
},
{
async onSettled() {
await utils.invalidateQueries(["viewer.integrations"]);
},
onError() {
showToast(`Something went wrong when toggling "${title}""`, "error");
},
}
);
return (
<div className={classNames("flex flex-row items-center", !isLastItemInList ? "mb-4" : "")}>
<div className="flex pl-2">
<Switch
id={externalId}
defaultChecked={isChecked}
onCheckedChange={(isOn: boolean) => {
mutation.mutate({ isOn });
}}
/>
</div>
<label className="ml-3 text-sm font-medium leading-5" htmlFor={externalId}>
{name}
</label>
</div>
);
};
export { CalendarSwitch };
@@ -0,0 +1,76 @@
import { DotsHorizontalIcon } from "@heroicons/react/solid";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button } from "@calcom/ui/v2";
import { CalendarSwitch } from "./CalendarSwitch";
interface IConnectedCalendarItem {
name: string;
logo: string;
externalId?: string;
integrationType: string;
calendars?: {
primary: true | null;
isSelected: boolean;
credentialId: number;
name?: string | undefined;
readOnly?: boolean | undefined;
userId?: number | undefined;
integration?: string | undefined;
externalId: string;
}[];
}
const ConnectedCalendarItem = (prop: IConnectedCalendarItem) => {
const { name, logo, externalId, calendars, integrationType } = prop;
const { t } = useLocale();
return (
<>
<div className="flex flex-row items-center p-4">
<img src={logo} alt={name} className="m-1 h-8 w-8" />
<div className="mx-4">
<p className="font-sans text-sm font-bold leading-5">
{name}
{/* Temporarily removed till we use it on another place */}
{/* <span className="mx-1 rounded-[4px] bg-green-100 py-[2px] px-[6px] font-sans text-xs font-medium text-green-600">
{t("default")}
</span> */}
</p>
<div className="fle-row flex">
<span
title={externalId}
className="max-w-44 mt-1 overflow-hidden text-ellipsis whitespace-nowrap font-sans text-sm text-gray-500">
{externalId}{" "}
</span>
</div>
</div>
{/* Temporarily removed */}
{/* <Button
color="minimal"
type="button"
className="ml-auto flex rounded-md border border-gray-200 py-[10x] px-4 font-sans text-sm">
{t("edit")}
</Button> */}
</div>
<div className="h-[1px] w-full border-b border-gray-200" />
<div>
<ul className="p-4">
{calendars?.map((calendar, i) => (
<CalendarSwitch
key={calendar.externalId}
externalId={calendar.externalId}
title={calendar.name || "Nameless Calendar"}
name={calendar.name || "Nameless Calendar"}
type={integrationType}
isChecked={calendar.isSelected}
isLastItemInList={i === calendars.length - 1}
/>
))}
</ul>
</div>
</>
);
};
export { ConnectedCalendarItem };
@@ -0,0 +1,37 @@
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { inferMutationInput, trpc } from "@calcom/trpc/react";
import DestinationCalendarSelector from "@components/DestinationCalendarSelector";
interface ICreateEventsOnCalendarSelectProps {
calendar?: inferMutationInput<"viewer.setDestinationCalendar"> | null;
}
const CreateEventsOnCalendarSelect = (props: ICreateEventsOnCalendarSelectProps) => {
const { calendar } = props;
const { t } = useLocale();
const mutation = trpc.useMutation(["viewer.setDestinationCalendar"]);
return (
<>
<div className="mt-6 flex flex-row">
<div className="w-full">
<label htmlFor="createEventsOn" className="flex text-sm font-medium text-neutral-700">
{t("create_events_on")}
</label>
<div className="mt-2">
<DestinationCalendarSelector
value={calendar ? calendar.externalId : undefined}
onChange={(calendar) => {
mutation.mutate(calendar);
}}
hidePlaceholder
/>
</div>
</div>
</div>
</>
);
};
export { CreateEventsOnCalendarSelect };
@@ -0,0 +1,9 @@
const StepCard: React.FC<{ children: React.ReactNode }> = (props) => {
return (
<div className="mt-10 rounded-md border border-gray-200 bg-white p-4 dark:bg-black sm:p-8">
{props.children}
</div>
);
};
export { StepCard };
@@ -0,0 +1,37 @@
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
interface ISteps {
maxSteps: number;
currentStep: number;
navigateToStep: (step: number) => void;
}
const Steps = (props: ISteps) => {
const { maxSteps, currentStep, navigateToStep } = props;
const { t } = useLocale();
return (
<div className="mt-6 space-y-2">
<p className="text-xs font-medium text-gray-500 dark:text-white">
{t("current_step_of_total", { currentStep: currentStep + 1, maxSteps })}
</p>
<div className="flex w-full space-x-2 rtl:space-x-reverse">
{new Array(maxSteps).fill(0).map((_s, index) => {
return index <= currentStep ? (
<div
key={`step-${index}`}
onClick={() => navigateToStep(index)}
className={classNames(
"h-1 w-1/4 rounded-[1px] bg-black dark:bg-white",
index < currentStep ? "cursor-pointer" : ""
)}
/>
) : (
<div key={`step-${index}`} className="h-1 w-1/4 rounded-[1px] bg-black bg-opacity-25" />
);
})}
</div>
</div>
);
};
export { Steps };
@@ -0,0 +1,108 @@
import { ArrowRightIcon } from "@heroicons/react/solid";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { List } from "@calcom/ui/List";
import { SkeletonAvatar, SkeletonText, SkeletonButton } from "@calcom/ui/v2";
import { CalendarItem } from "../components/CalendarItem";
import { ConnectedCalendarItem } from "../components/ConnectedCalendarItem";
import { CreateEventsOnCalendarSelect } from "../components/CreateEventsOnCalendarSelect";
interface IConnectCalendarsProps {
nextStep: () => void;
}
const ConnectedCalendars = (props: IConnectCalendarsProps) => {
const { nextStep } = props;
const queryConnectedCalendars = trpc.useQuery(["viewer.connectedCalendars"]);
const { t } = useLocale();
const queryIntegrations = trpc.useQuery([
"viewer.integrations",
{ variant: "calendar", onlyInstalled: false },
]);
const firstCalendar = queryConnectedCalendars.data?.connectedCalendars.find(
(item) => item.calendars && item.calendars?.length > 0
);
const disabledNextButton = firstCalendar === undefined;
const destinationCalendar = queryConnectedCalendars.data?.destinationCalendar;
return (
<>
{/* Already connected calendars */}
{!queryConnectedCalendars.isLoading &&
firstCalendar &&
firstCalendar.integration &&
firstCalendar.integration.title &&
firstCalendar.integration.imageSrc && (
<>
<List className="rounded-md border border-gray-200 bg-white p-0 dark:bg-black">
<ConnectedCalendarItem
key={firstCalendar.integration.title}
name={firstCalendar.integration.title}
logo={firstCalendar.integration.imageSrc}
externalId={
firstCalendar && firstCalendar.calendars && firstCalendar.calendars.length > 0
? firstCalendar.calendars[0].externalId
: ""
}
calendars={firstCalendar.calendars}
integrationType={firstCalendar.integration.type}
/>
</List>
{/* Create event on selected calendar */}
<CreateEventsOnCalendarSelect calendar={destinationCalendar} />
<p className="mt-4 text-sm text-gray-500">{t("connect_calendars_from_app_store")}</p>
</>
)}
{/* Connect calendars list */}
{firstCalendar === undefined && queryIntegrations.data && queryIntegrations.data.items.length > 0 && (
<List className="mx-1 divide-y divide-gray-200 rounded-md border border-gray-200 bg-white p-0 dark:bg-black sm:mx-0">
{queryIntegrations.data &&
queryIntegrations.data.items.map((item) => (
<li key={item.title}>
{item.title && item.imageSrc && (
<CalendarItem
type={item.type}
title={item.title}
description={item.description}
imageSrc={item.imageSrc}
/>
)}
</li>
))}
</List>
)}
{queryIntegrations.isLoading && (
<ul className="divide-y divide-gray-200 rounded-md border border-gray-200 bg-white p-0 dark:bg-black">
{[0, 0, 0, 0].map((_item, index) => {
return (
<li className="flex w-full flex-row justify-center border-b-0 py-6" key={index}>
<SkeletonAvatar className="mx-6 h-8 w-8 px-4" />
<SkeletonText className="ml-1 mr-4 mt-3 h-5 w-full" />
<SkeletonButton className="mr-6 h-8 w-20 rounded-md p-5" />
</li>
);
})}
</ul>
)}
<button
type="button"
data-testid="save-calendar-button"
className={classNames(
"mt-8 flex w-full flex-row justify-center rounded-md border border-black bg-black p-2 text-center text-sm text-white",
disabledNextButton ? "cursor-not-allowed opacity-20" : ""
)}
onClick={() => nextStep()}
disabled={disabledNextButton}>
{firstCalendar ? `${t("continue")}` : `${t("next_step_text")}`}
<ArrowRightIcon className="ml-2 h-4 w-4 self-center" aria-hidden="true" />
</button>
</>
);
};
export { ConnectedCalendars };
@@ -0,0 +1,88 @@
import { ArrowRightIcon } from "@heroicons/react/solid";
import { useRouter } from "next/router";
import { useForm } from "react-hook-form";
import { Schedule } from "@calcom/features/schedules";
import { DEFAULT_SCHEDULE } from "@calcom/lib/availability";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc, TRPCClientErrorLike } from "@calcom/trpc/react";
import { AppRouter } from "@calcom/trpc/server/routers/_app";
import { Form } from "@calcom/ui/form/fields";
import { Button } from "@calcom/ui/v2";
interface ISetupAvailabilityProps {
nextStep: () => void;
defaultScheduleId?: number | null;
}
const SetupAvailability = (props: ISetupAvailabilityProps) => {
const { defaultScheduleId } = props;
const { t } = useLocale();
const { nextStep } = props;
const router = useRouter();
let queryAvailability;
if (defaultScheduleId) {
queryAvailability = trpc.useQuery(["viewer.availability.schedule", { scheduleId: defaultScheduleId }], {
enabled: router.isReady,
});
}
const availabilityForm = useForm({
defaultValues: {
schedule: queryAvailability?.data?.availability || DEFAULT_SCHEDULE,
},
});
const mutationOptions = {
onError: (error: TRPCClientErrorLike<AppRouter>) => {
throw new Error(error.message);
},
onSuccess: () => {
nextStep();
},
};
const createSchedule = trpc.useMutation("viewer.availability.schedule.create", mutationOptions);
const updateSchedule = trpc.useMutation("viewer.availability.schedule.update", mutationOptions);
return (
<Form
className="w-full bg-white text-black dark:bg-opacity-5 dark:text-white"
form={availabilityForm}
handleSubmit={async (values) => {
try {
if (defaultScheduleId) {
await updateSchedule.mutate({
scheduleId: defaultScheduleId,
name: t("default_schedule_name"),
...values,
});
} else {
await createSchedule.mutate({
name: t("default_schedule_name"),
...values,
});
}
} catch (error) {
if (error instanceof Error) {
// setError(error);
// @TODO: log error
}
}
}}>
<Schedule control={availabilityForm.control} name="schedule" weekStart={1} />
<div>
<Button
data-testid="save-availability"
type="submit"
className="mt-2 w-full justify-center p-2 text-sm sm:mt-8"
disabled={availabilityForm.formState.isSubmitting}>
{t("next_step_text")} <ArrowRightIcon className="ml-2 h-4 w-4 self-center" />
</Button>
</div>
</Form>
);
};
export { SetupAvailability };
@@ -0,0 +1,170 @@
import { ArrowRightIcon } from "@heroicons/react/solid";
import { useRouter } from "next/router";
import { FormEvent, useRef, useState } from "react";
import { useForm } from "react-hook-form";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { User } from "@calcom/prisma/client";
import { trpc } from "@calcom/trpc/react";
import { Button, showToast, TextArea } from "@calcom/ui/v2";
import ImageUploader from "@calcom/ui/v2/core/ImageUploader";
import { AvatarSSR } from "@components/ui/AvatarSSR";
interface IUserProfile {
user?: User;
}
type FormData = {
bio: string;
};
const UserProfile = (props: IUserProfile) => {
const { user } = props;
const { t } = useLocale();
const avatarRef = useRef<HTMLInputElement>(null!);
const bioRef = useRef<HTMLTextAreaElement>(null);
const {
register,
setValue,
handleSubmit,
formState: { errors },
} = useForm<FormData>({ defaultValues: { bio: user?.bio || "" } });
const { data: eventTypes } = trpc.useQuery(["viewer.eventTypes.list"]);
const [imageSrc, setImageSrc] = useState<string>(user?.avatar || "");
const utils = trpc.useContext();
const router = useRouter();
const createEventType = trpc.useMutation("viewer.eventTypes.create");
const mutation = trpc.useMutation("viewer.updateProfile", {
onSuccess: async (_data, context) => {
if (context.avatar) {
showToast(t("your_user_profile_updated_successfully"), "success");
await utils.refetchQueries(["viewer.me"]);
} else {
try {
if (eventTypes?.length === 0) {
await Promise.all(
DEFAULT_EVENT_TYPES.map(async (event) => {
return createEventType.mutate(event);
})
);
}
} catch (error) {
console.error(error);
}
await utils.refetchQueries(["viewer.me"]);
router.push("/");
}
},
onError: () => {
showToast(t("problem_saving_user_profile"), "error");
},
});
const onSubmit = handleSubmit((data: { bio: string }) => {
const { bio } = data;
mutation.mutate({
bio,
completedOnboarding: true,
});
});
async function updateProfileHandler(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const enteredAvatar = avatarRef.current.value;
mutation.mutate({
avatar: enteredAvatar,
});
}
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,
},
];
return (
<form onSubmit={onSubmit}>
<p className="font-cal text-sm">{t("profile_picture")}</p>
<div className="mt-4 flex flex-row items-center justify-start rtl:justify-end">
{user && <AvatarSSR user={user} alt="Profile picture" className="h-16 w-16" />}
<input
ref={avatarRef}
type="hidden"
name="avatar"
id="avatar"
placeholder="URL"
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 text-sm focus:border-neutral-800 focus:outline-none focus:ring-neutral-800"
defaultValue={imageSrc}
/>
<div className="flex items-center px-4">
<ImageUploader
target="avatar"
id="avatar-upload"
buttonMsg={t("upload")}
handleAvatarChange={(newAvatar) => {
avatarRef.current.value = newAvatar;
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"value"
)?.set;
nativeInputValueSetter?.call(avatarRef.current, newAvatar);
const ev2 = new Event("input", { bubbles: true });
avatarRef.current.dispatchEvent(ev2);
updateProfileHandler(ev2 as unknown as FormEvent<HTMLFormElement>);
setImageSrc(newAvatar);
}}
imageSrc={imageSrc}
/>
</div>
</div>
<fieldset className="mt-8">
<label htmlFor="bio" className="mb-2 block text-sm font-medium text-gray-700">
{t("about")}
</label>
<TextArea
{...register("bio", { required: true })}
ref={bioRef}
name="bio"
id="bio"
className="mt-1 block h-[60px] w-full rounded-sm border border-gray-300 px-3 py-2 focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
defaultValue={user?.bio || undefined}
onChange={(event) => {
setValue("bio", event.target.value);
}}
/>
{errors.bio && (
<p data-testid="required" className="py-2 text-xs text-red-500">
{t("required")}
</p>
)}
<p className="mt-2 font-sans text-sm font-normal text-gray-600 dark:text-white">
{t("few_sentences_about_yourself")}
</p>
</fieldset>
<Button
type="submit"
className="mt-8 flex w-full flex-row justify-center rounded-md border border-black bg-black p-2 text-center text-sm text-white">
{t("finish")}
<ArrowRightIcon className="ml-2 h-4 w-4 self-center" aria-hidden="true" />
</Button>
</form>
);
};
export default UserProfile;
@@ -0,0 +1,122 @@
import { ArrowRightIcon } from "@heroicons/react/outline";
import { useRef, useState } from "react";
import { useForm } from "react-hook-form";
import dayjs from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { User } from "@calcom/prisma/client";
import { trpc } from "@calcom/trpc/react";
import TimezoneSelect from "@calcom/ui/form/TimezoneSelect";
import { Button } from "@calcom/ui/v2";
import { UsernameAvailability } from "@components/ui/UsernameAvailability";
interface IUserSettingsProps {
user: User;
nextStep: () => void;
}
type FormData = {
name: string;
};
const UserSettings = (props: IUserSettingsProps) => {
const { user, nextStep } = props;
const { t } = useLocale();
const [selectedTimeZone, setSelectedTimeZone] = useState(user.timeZone ?? dayjs.tz.guess());
const { register, handleSubmit, formState } = useForm<FormData>({
defaultValues: {
name: user?.name || undefined,
},
reValidateMode: "onChange",
});
const { errors } = formState;
const defaultOptions = { required: true, maxLength: 255 };
const utils = trpc.useContext();
const onSuccess = async () => {
await utils.invalidateQueries(["viewer.me"]);
nextStep();
};
const mutation = trpc.useMutation("viewer.updateProfile", {
onSuccess: onSuccess,
});
const { data: stripeCustomer } = trpc.useQuery(["viewer.stripeCustomer"]);
const paymentRequired = stripeCustomer?.isPremium ? !stripeCustomer?.paidForPremium : false;
const onSubmit = handleSubmit((data) => {
if (paymentRequired) {
return;
}
mutation.mutate({
name: data.name,
timeZone: selectedTimeZone,
});
});
const [currentUsername, setCurrentUsername] = useState(user.username || undefined);
const [inputUsernameValue, setInputUsernameValue] = useState(currentUsername);
const usernameRef = useRef<HTMLInputElement>(null!);
return (
<form onSubmit={onSubmit}>
<div className="space-y-6">
{/* Username textfield */}
<UsernameAvailability
readonly={true}
currentUsername={currentUsername}
setCurrentUsername={setCurrentUsername}
inputUsernameValue={inputUsernameValue}
usernameRef={usernameRef}
setInputUsernameValue={setInputUsernameValue}
user={user}
/>
{/* Full name textfield */}
<div className="w-full">
<label htmlFor="name" className="mb-2 block text-sm font-medium text-gray-700">
{t("full_name")}
</label>
<input
{...register("name", defaultOptions)}
id="name"
name="name"
type="text"
autoComplete="off"
autoCorrect="off"
className="w-full rounded-md border border-gray-300 text-sm"
/>
{errors.name && (
<p data-testid="required" className="py-2 text-xs text-red-500">
{t("required")}
</p>
)}
</div>
{/* Timezone select field */}
<div className="w-full">
<label htmlFor="timeZone" className="block text-sm font-medium text-gray-700">
{t("timezone")}
</label>
<TimezoneSelect
id="timeZone"
value={selectedTimeZone}
onChange={({ value }) => setSelectedTimeZone(value)}
className="mt-2 w-full rounded-md text-sm"
/>
<p className="mt-3 flex flex-row font-sans text-xs leading-tight text-gray-500 dark:text-white">
{t("current_time")} {dayjs().tz(selectedTimeZone).format("LT").toString().toLowerCase()}
</p>
</div>
</div>
<Button
type="submit"
className="mt-8 flex w-full flex-row justify-center"
disabled={mutation.isLoading}>
{t("next_step_text")}
<ArrowRightIcon className="ml-2 h-4 w-4 self-center" aria-hidden="true" />
</Button>
</form>
);
};
export { UserSettings };
@@ -1,5 +1,5 @@
import { useMutation } from "@tanstack/react-query";
import { Fragment } from "react";
import { useMutation } from "react-query";
import { InstallAppButton } from "@calcom/app-store/components";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -185,8 +185,8 @@ function ConnectedCalendarsList(props: Props) {
) : (
<Alert
severity="warning"
title={t("calendar_error")}
message={item.error?.message}
title={t("something_went_wrong")}
message={t("calendar_error")}
actions={
<DisconnectIntegration
id={item.credentialId}
@@ -1,5 +1,5 @@
import { useMutation } from "@tanstack/react-query";
import { useState } from "react";
import { useMutation } from "react-query";
import showToast from "@calcom/lib/notification";
import { ButtonBaseProps } from "@calcom/ui/Button";
@@ -1,17 +1,20 @@
import React, { SyntheticEvent, useState } from "react";
import { ErrorCode } from "@calcom/lib/auth";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import Button from "@calcom/ui/Button";
import { ErrorCode } from "@lib/auth";
import { useLocale } from "@lib/hooks/useLocale";
const ChangePasswordSection = () => {
const [oldPassword, setOldPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const { t } = useLocale();
const { t, isLocaleReady } = useLocale();
// hold display until the locale is loaded
if (!isLocaleReady) {
return null;
}
const errorMessages: { [key: string]: string } = {
[ErrorCode.IncorrectPassword]: t("current_incorrect_password"),
@@ -4,6 +4,7 @@ import { trpc } from "@calcom/trpc/react";
import Badge from "@calcom/ui/Badge";
import Button from "@calcom/ui/Button";
/** @deprecated Use `packages/features/ee/teams/components/DisableTeamImpersonation.tsx` */
const DisableTeamImpersonation = ({ teamId, memberId }: { teamId: number; memberId: number }) => {
const { t } = useLocale();
@@ -13,6 +13,7 @@ type MembershipRoleOption = {
value: MembershipRole;
};
/** @deprecated Use `packages/features/ee/teams/components/MemberChangeRoleModal.tsx` */
export default function MemberChangeRoleModal(props: {
isOpen: boolean;
currentMember: MembershipRole;
@@ -26,6 +26,7 @@ type MembershipRoleOption = {
const _options: MembershipRoleOption[] = [{ value: "MEMBER" }, { value: "ADMIN" }, { value: "OWNER" }];
/** @deprecated Use `packages/features/ee/teams/components/MemberInvitationModal.tsx` */
export default function MemberInvitationModal(props: MemberInvitationModalProps) {
const [errorMessage, setErrorMessage] = useState("");
const { t, i18n } = useLocale();
@@ -33,6 +33,7 @@ interface Props {
member: inferQueryOutput<"viewer.teams.get">["members"][number];
}
/** @deprecated Use `packages/features/ee/teams/components/MemberListItem.tsx` */
export default function MemberListItem(props: Props) {
const { t } = useLocale();
+5 -18
View File
@@ -3,9 +3,9 @@ import { useRef, useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { Button } from "@calcom/ui";
import { Alert } from "@calcom/ui/Alert";
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { Alert } from "@calcom/ui/v2/core/Alert";
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/v2/core/Dialog";
interface Props {
isOpen: boolean;
@@ -28,15 +28,14 @@ export default function TeamCreate(props: Props) {
},
});
const createTeam = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const createTeam = () => {
createTeamMutation.mutate({ name: nameRef?.current?.value });
};
return (
<>
<Dialog open={props.isOpen} onOpenChange={props.onClose}>
<DialogContent>
<DialogContent type="creation" actionText={t("create_new_team")} actionOnClick={createTeam}>
<div className="mb-4 sm:flex sm:items-start">
<div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10">
<Icon.FiUsers className="text-brandcontrast h-6 w-6" />
@@ -50,7 +49,7 @@ export default function TeamCreate(props: Props) {
</div>
</div>
</div>
<form onSubmit={createTeam}>
<form>
<div className="mb-4">
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
{t("name")}
@@ -66,18 +65,6 @@ export default function TeamCreate(props: Props) {
/>
</div>
{errorMessage && <Alert severity="error" title={errorMessage} />}
<DialogFooter>
<Button type="button" color="secondary" onClick={props.onClose}>
{t("cancel")}
</Button>
<Button
type="submit"
color="primary"
className="ltr:ml-2 rtl:mr-2"
data-testid="create-new-team-button">
{t("create_team")}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
+1 -1
View File
@@ -1,7 +1,7 @@
import { useState } from "react";
import showToast from "@calcom/lib/notification";
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
import showToast from "@calcom/ui/v2/core/notifications";
import TeamListItem from "./TeamListItem";
+105 -113
View File
@@ -4,19 +4,21 @@ import Link from "next/link";
import classNames from "@calcom/lib/classNames";
import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
import Button from "@calcom/ui/Button";
import ConfirmationDialogContent from "@calcom/ui/ConfirmationDialogContent";
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import Button from "@calcom/ui/v2/core/Button";
import ButtonGroup from "@calcom/ui/v2/core/ButtonGroup";
import ConfirmationDialogContent from "@calcom/ui/v2/core/ConfirmationDialogContent";
import { Dialog, DialogTrigger } from "@calcom/ui/v2/core/Dialog";
import Dropdown, {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
DropdownItem,
} from "@calcom/ui/v2/core/Dropdown";
import { Tooltip } from "@calcom/ui/v2/core/Tooltip";
import showToast from "@calcom/ui/v2/core/notifications";
import Avatar from "@components/ui/Avatar";
@@ -84,7 +86,7 @@ export default function TeamListItem(props: Props) {
!isInvitee && "group hover:bg-neutral-50"
)}>
{!isInvitee ? (
<Link href={"/settings/teams/" + team.id}>
<Link href={"/settings/teams/" + team.id + "/profile"}>
<a className="flex-grow cursor-pointer truncate text-sm" title={`${team.name}`}>
{teamInfo}
</a>
@@ -112,7 +114,6 @@ export default function TeamListItem(props: Props) {
<DropdownMenuItem>
<Button
color="minimal"
size="sm"
className="w-full rounded-none font-medium"
StartIcon={Icon.FiCheck}
onClick={acceptInvite}>
@@ -121,8 +122,7 @@ export default function TeamListItem(props: Props) {
</DropdownMenuItem>
<DropdownMenuItem>
<Button
color="warn"
size="sm"
color="destructive"
className="w-full rounded-none font-medium"
StartIcon={Icon.FiX}
onClick={declineInvite}>
@@ -137,111 +137,103 @@ export default function TeamListItem(props: Props) {
{!isInvitee && (
<div className="flex space-x-2 rtl:space-x-reverse">
<TeamRole role={team.role} />
<Tooltip side="top" content={t("copy_link_team")}>
<Button
onClick={() => {
navigator.clipboard.writeText(process.env.NEXT_PUBLIC_WEBSITE_URL + "/team/" + team.slug);
showToast(t("link_copied"), "success");
}}
className="h-10 w-10 transition-none"
size="icon"
color="minimal"
type="button">
<Icon.FiLink className="h-5 w-5 group-hover:text-gray-600" />
</Button>
</Tooltip>
<Dropdown>
<DropdownMenuTrigger asChild>
<Button type="button" color="minimal" size="icon" StartIcon={Icon.FiMoreHorizontal} />
</DropdownMenuTrigger>
<DropdownMenuContent hidden={hideDropdown}>
{isAdmin && (
<ButtonGroup combined>
<Tooltip content={t("copy_link_team")}>
<Button
color="secondary"
onClick={() => {
navigator.clipboard.writeText(
process.env.NEXT_PUBLIC_WEBSITE_URL + "/team/" + team.slug
);
showToast(t("link_copied"), "success");
}}
size="icon"
StartIcon={Icon.FiLink}
combined
/>
</Tooltip>
<Dropdown>
<DropdownMenuTrigger asChild>
<Button type="button" color="secondary" size="icon" StartIcon={Icon.FiMoreHorizontal} />
</DropdownMenuTrigger>
<DropdownMenuContent hidden={hideDropdown}>
{isAdmin && (
<DropdownMenuItem>
<DropdownItem
type="button"
href={"/settings/teams/" + team.id + "/profile"}
StartIcon={Icon.FiEdit2}>
{t("edit_team") as string}
</DropdownItem>
</DropdownMenuItem>
)}
<DropdownMenuItem>
<Link href={"/settings/teams/" + team.id}>
<a>
<Button
color="minimal"
size="sm"
className="w-full rounded-none font-medium"
StartIcon={Icon.FiEdit2}>
{t("edit_team")}
</Button>
</a>
</Link>
<DropdownItem
type="button"
target="_blank"
href={`${process.env.NEXT_PUBLIC_WEBSITE_URL}/team/${team.slug}`}
StartIcon={Icon.FiExternalLink}>
{t("preview_team") as string}
</DropdownItem>
</DropdownMenuItem>
)}
<DropdownMenuItem>
<Link href={`${process.env.NEXT_PUBLIC_WEBSITE_URL}/team/${team.slug}`} passHref={true}>
<a target="_blank">
<Button
color="minimal"
size="sm"
className="w-full rounded-none font-medium"
StartIcon={Icon.FiExternalLink}>
{t("preview_team")}
</Button>
</a>
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator className="h-px bg-gray-200" />
{isOwner && (
<DropdownMenuItem>
<Dialog open={hideDropdown} onOpenChange={setHideDropdown}>
<DialogTrigger asChild>
<Button
onClick={(e) => {
e.stopPropagation();
}}
color="warn"
size="sm"
className="w-full rounded-none font-medium"
StartIcon={Icon.FiTrash}>
{t("disband_team")}
</Button>
</DialogTrigger>
<ConfirmationDialogContent
variety="danger"
title={t("disband_team")}
confirmBtnText={t("confirm_disband_team")}
isLoading={props.isLoading}
onConfirm={() => {
props.onActionSelect("disband");
}}>
{t("disband_team_confirmation_message")}
</ConfirmationDialogContent>
</Dialog>
</DropdownMenuItem>
)}
{!isOwner && (
<DropdownMenuItem>
<Dialog>
<DialogTrigger asChild>
<Button
type="button"
color="warn"
size="lg"
StartIcon={Icon.FiLogOut}
className="w-full rounded-none"
onClick={(e) => {
e.stopPropagation();
<DropdownMenuSeparator className="h-px bg-gray-200" />
{isOwner && (
<DropdownMenuItem>
<Dialog open={hideDropdown} onOpenChange={setHideDropdown}>
<DialogTrigger asChild>
<Button
onClick={(e) => {
e.stopPropagation();
}}
color="destructive"
className="rounded-none px-3 font-normal"
StartIcon={Icon.FiTrash}>
{t("disband_team")}
</Button>
</DialogTrigger>
<ConfirmationDialogContent
variety="danger"
title={t("disband_team")}
confirmBtnText={t("confirm_disband_team")}
isLoading={props.isLoading}
onConfirm={() => {
props.onActionSelect("disband");
}}>
{t("leave_team")}
</Button>
</DialogTrigger>
<ConfirmationDialogContent
variety="danger"
title={t("leave_team")}
confirmBtnText={t("confirm_leave_team")}
onConfirm={declineInvite}>
{t("leave_team_confirmation_message")}
</ConfirmationDialogContent>
</Dialog>
</DropdownMenuItem>
)}
</DropdownMenuContent>
</Dropdown>
{t("disband_team_confirmation_message")}
</ConfirmationDialogContent>
</Dialog>
</DropdownMenuItem>
)}
{!isOwner && (
<DropdownMenuItem>
<Dialog>
<DialogTrigger asChild>
<Button
type="button"
color="destructive"
size="lg"
StartIcon={Icon.FiLogOut}
className="w-full rounded-none"
onClick={(e) => {
e.stopPropagation();
}}>
{t("leave_team")}
</Button>
</DialogTrigger>
<ConfirmationDialogContent
variety="danger"
title={t("leave_team")}
confirmBtnText={t("confirm_leave_team")}
onConfirm={declineInvite}>
{t("leave_team_confirmation_message")}
</ConfirmationDialogContent>
</Dialog>
</DropdownMenuItem>
)}
</DropdownMenuContent>
</Dropdown>
</ButtonGroup>
</div>
)}
</div>
+1
View File
@@ -10,6 +10,7 @@ interface Props {
color?: PillColor;
}
/** @deprecated Use `packages/features/ee/teams/components/TeamPill.tsx` */
export default function TeamPill(props: Props) {
return (
<div
@@ -19,6 +19,7 @@ interface Props {
teamId: number;
}
/** @deprecated Use `packages/features/ee/teams/components/UpgradeToFlexibleProModal.tsx` */
export function UpgradeToFlexibleProModal(props: Props) {
const { t } = useLocale();
const [errorMessage, setErrorMessage] = useState<string | null>(null);
+34 -37
View File
@@ -1,10 +1,7 @@
import Link from "next/link";
import { TeamPageProps } from "pages/team/[slug]";
import React from "react";
import { WEBAPP_URL } from "@calcom/lib/constants";
import Button from "@calcom/ui/Button";
import { Icon } from "@calcom/ui/Icon";
import { Avatar } from "@calcom/ui/v2";
import { useLocale } from "@lib/hooks/useLocale";
@@ -13,46 +10,46 @@ type TeamType = TeamPageProps["team"];
type MembersType = TeamType["members"];
type MemberType = MembersType[number];
const Team = ({ team }: TeamPageProps) => {
const Member = ({ member, teamName }: { member: MemberType; teamName: string | null }) => {
const { t } = useLocale();
const Member = ({ member }: { member: MemberType }) => {
return (
<Link key={member.id} href={`/${member.username}`}>
<div className="sm:min-w-80 sm:max-w-80 dark:bg-darkgray-200 dark:hover:bg-darkgray-300 group flex h-full flex-col space-y-2 rounded-md bg-white p-4 hover:cursor-pointer hover:bg-gray-50 ">
<Avatar
size="md"
alt={member.name || ""}
imageSrc={WEBAPP_URL + "/" + member.username + "/avatar.png"}
/>
<section className="line-clamp-4 mt-2 w-full space-y-1">
<p className="font-medium text-neutral-900 dark:text-white">{member.name}</p>
<p className="line-clamp-3 overflow-ellipsis text-sm font-normal text-neutral-500 dark:text-white">
{member.bio || t("user_from_team", { user: member.name, team: team.name })}
</p>
</section>
</div>
</Link>
);
};
return (
<Link key={member.id} href={`/${member.username}`}>
<div className="sm:min-w-80 sm:max-w-80 dark:bg-darkgray-200 dark:hover:bg-darkgray-300 group flex min-h-full w-[90%] flex-col space-y-2 rounded-md bg-white p-4 hover:cursor-pointer hover:bg-gray-50 ">
<Avatar
size="md"
alt={member.name || ""}
imageSrc={WEBAPP_URL + "/" + member.username + "/avatar.png"}
/>
<section className="line-clamp-4 mt-2 w-full space-y-1">
<p className="font-medium text-neutral-900 dark:text-white">{member.name}</p>
<p className="line-clamp-3 overflow-ellipsis text-sm font-normal text-neutral-500 dark:text-white">
{member.bio || t("user_from_team", { user: member.name, team: teamName })}
</p>
</section>
</div>
</Link>
);
};
const Members = ({ members }: { members: MembersType }) => {
if (!members || members.length === 0) {
return null;
}
return (
<section className="lg:min-w-lg mx-auto flex min-w-full max-w-5xl flex-wrap justify-center gap-x-6 gap-y-6">
{members.map((member) => {
return member.username !== null && <Member key={member.id} member={member} />;
})}
</section>
);
};
const Members = ({ members, teamName }: { members: MembersType; teamName: string | null }) => {
if (!members || members.length === 0) {
return null;
}
return (
<section className="lg:min-w-lg mx-auto flex min-w-full max-w-5xl flex-wrap justify-center gap-x-6 gap-y-6">
{members.map((member) => {
return member.username !== null && <Member key={member.id} member={member} teamName={teamName} />;
})}
</section>
);
};
const Team = ({ team }: TeamPageProps) => {
return (
<div>
<Members members={team.members} />
<Members members={team.members} teamName={team.name} />
</div>
);
};
+1
View File
@@ -6,6 +6,7 @@ type AdminRequiredProps = {
children?: React.ReactNode;
};
/** @deprecated use PermssionContainer instead. Will delete once V2 goes live */
export const AdminRequired: FC<AdminRequiredProps> = ({ children, as, ...rest }) => {
const session = useSession();
+2 -2
View File
@@ -17,7 +17,7 @@ interface Props {
export default function AuthContainer(props: React.PropsWithChildren<Props>) {
return (
<div className="flex min-h-screen flex-col justify-center bg-neutral-50 py-12 sm:px-6 lg:px-8">
<div className="flex min-h-screen flex-col justify-center border-gray-200 bg-neutral-50 py-12 sm:px-6 lg:px-8">
<HeadSeo title={props.title} description={props.description} />
<div className={classNames(props.showLogo ? "text-center" : "", "sm:mx-auto sm:w-full sm:max-w-md")}>
{props.showLogo && (
@@ -34,7 +34,7 @@ export default function AuthContainer(props: React.PropsWithChildren<Props>) {
</div>
)}
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="mx-2 rounded-sm border border-neutral-200 bg-white px-4 py-8 sm:px-10">
<div className="mx-2 rounded-md border border-gray-300 bg-white px-4 py-8 sm:px-10">
{props.children}
</div>
<div className="mt-4 text-center text-sm text-neutral-600">{props.footerText}</div>
-1
View File
@@ -2,7 +2,6 @@ import React from "react";
import classNames from "@lib/classNames";
import Avatar from "@components/ui/Avatar";
import { AvatarSSR } from "@components/ui/AvatarSSR";
export type AvatarGroupProps = {
+30 -32
View File
@@ -1,50 +1,48 @@
import { useState } from "react";
import classNames from "classnames";
import React, { useState } from "react";
import { ControllerRenderProps } from "react-hook-form";
import { Icon } from "@calcom/ui/Icon";
const EditableHeading = ({
title,
const EditableHeading = function EditableHeading({
value,
onChange,
placeholder = "",
readOnly = false,
isReady,
...passThroughProps
}: {
title: string;
onChange?: (value: string) => void;
placeholder?: string;
readOnly?: boolean;
}) => {
isReady?: boolean;
} & Omit<JSX.IntrinsicElements["input"], "name" | "onChange"> &
ControllerRenderProps) {
const [isEditing, setIsEditing] = useState(false);
const enableEditing = () => !readOnly && setIsEditing(true);
const enableEditing = () => setIsEditing(true);
return (
<div className="group relative cursor-pointer" onClick={enableEditing}>
{!isEditing ? (
<>
<h1
style={{ fontSize: 22, letterSpacing: "-0.0009em" }}
className="inline pl-0 normal-case text-gray-900 focus:text-black group-hover:text-gray-500">
{title}
</h1>
{!readOnly ? (
<Icon.FiEdit2 className="ml-1 -mt-1 inline h-4 w-4 text-gray-700 group-hover:text-gray-500" />
) : null}
</>
) : (
<div style={{ marginBottom: -11 }}>
<div className="flex items-center">
<label className="min-w-8 relative inline-block">
<span className="whitespace-pre text-xl tracking-normal text-transparent">{value}&nbsp;</span>
{!isEditing && isReady && (
<Icon.FiEdit2 className="ml-1 inline h-4 w-4 align-top text-gray-700 group-hover:text-gray-500" />
)}
<input
{...passThroughProps}
type="text"
autoFocus
style={{ top: -6, fontSize: 22 }}
value={value}
required
className="relative h-10 w-full cursor-pointer border-none bg-transparent pl-0 text-gray-900 hover:text-gray-700 focus:text-black focus:outline-none focus:ring-0"
placeholder={placeholder}
defaultValue={title}
className={classNames(
"absolute top-0 left-0 w-full cursor-pointer border-none bg-transparent p-0 align-top text-xl text-gray-900 hover:text-gray-700 focus:text-black focus:outline-none focus:ring-0"
)}
onFocus={(e) => {
setIsEditing(true);
passThroughProps.onFocus && passThroughProps.onFocus(e);
}}
onBlur={(e) => {
setIsEditing(false);
onChange && onChange(e.target.value);
passThroughProps.onBlur && passThroughProps.onBlur(e);
}}
onChange={(e) => onChange && onChange(e.target.value)}
/>
</div>
)}
</label>
</div>
</div>
);
};
+27 -28
View File
@@ -1,6 +1,11 @@
import React, { FC } from "react";
import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
import Dropdown, {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenuPortal,
} from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
import Button from "@calcom/ui/v2/core/Button";
@@ -38,44 +43,41 @@ const DropdownActions = ({
<Dropdown>
{!actionTrigger ? (
<DropdownMenuTrigger asChild>
<Button type="button" color="minimal" size="icon" StartIcon={Icon.FiMoreHorizontal} />
<Button type="button" color="secondary" size="icon" StartIcon={Icon.FiMoreHorizontal} />
</DropdownMenuTrigger>
) : (
<DropdownMenuTrigger asChild>{actionTrigger}</DropdownMenuTrigger>
)}
<DropdownMenuContent portalled>
{actions.map((action) => (
<DropdownMenuItem key={action.id} className="focus-visible:outline-none">
<Button
type="button"
color="minimal"
className="w-full rounded-none font-normal"
href={action.href}
StartIcon={action.icon}
onClick={action.onClick || defaultAction}
data-testid={action.id}>
{action.label}
</Button>
</DropdownMenuItem>
))}
</DropdownMenuContent>
<DropdownMenuPortal>
<DropdownMenuContent>
{actions.map((action) => (
<DropdownMenuItem key={action.id} className="focus-visible:outline-none">
<Button
type="button"
color="minimal"
className="w-full rounded-none font-normal"
href={action.href}
StartIcon={action.icon}
onClick={action.onClick || defaultAction}
data-testid={action.id}>
{action.label}
</Button>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenuPortal>
</Dropdown>
);
};
const TableActions: FC<Props> = ({ actions }) => {
const mobileActions = actions.flatMap((action) => {
if (action.actions) {
return action.actions;
}
return action;
});
return (
<>
<div className="hidden space-x-2 rtl:space-x-reverse lg:block">
<div className="flex space-x-2 rtl:space-x-reverse">
{actions.map((action) => {
const button = (
<Button
className="whitespace-nowrap"
key={action.id}
data-testid={action.id}
href={action.href}
@@ -93,9 +95,6 @@ const TableActions: FC<Props> = ({ actions }) => {
return <DropdownActions key={action.id} actions={action.actions} actionTrigger={button} />;
})}
</div>
<div className="inline-block text-left lg:hidden">
<DropdownActions actions={mobileActions} />
</div>
</>
);
};
@@ -1,18 +1,20 @@
import classNames from "classnames";
import { debounce } from "lodash";
import { useRouter } from "next/router";
import { MutableRefObject, useCallback, useEffect, useState } from "react";
import { getPremiumPlanMode, getPremiumPlanPriceValue } from "@calcom/app-store/stripepayment/lib/utils";
import { fetchUsername } from "@calcom/lib/fetchUsername";
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { User } from "@calcom/prisma/client";
import { TRPCClientErrorLike } from "@calcom/trpc/client";
import { trpc } from "@calcom/trpc/react";
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import Button from "@calcom/ui/Button";
import { Dialog, DialogClose, DialogContent, DialogHeader } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { Input, Label } from "@calcom/ui/form/fields";
import { Icon, StarIconSolid } from "@calcom/ui/Icon";
import { Button } from "@calcom/ui/v2";
import { Input, Label } from "@calcom/ui/v2";
export enum UsernameChangeStatusEnum {
NORMAL = "NORMAL",
@@ -24,7 +26,7 @@ interface ICustomUsernameProps {
currentUsername: string | undefined;
setCurrentUsername: (value: string | undefined) => void;
inputUsernameValue: string | undefined;
usernameRef: MutableRefObject<HTMLInputElement>;
usernameRef: MutableRefObject<HTMLInputElement | null>;
setInputUsernameValue: (value: string) => void;
onSuccessMutation?: () => void;
onErrorMutation?: (error: TRPCClientErrorLike<AppRouter>) => void;
@@ -42,12 +44,31 @@ interface ICustomUsernameProps {
| "plan"
| "brandColor"
| "darkBrandColor"
| "metadata"
| "timeFormat"
| "allowDynamicBooking"
| "metadata"
>;
readonly?: boolean;
}
const obtainNewUsernameChangeCondition = ({
userIsPremium,
isNewUsernamePremium,
stripeCustomer,
}: {
userIsPremium: boolean;
isNewUsernamePremium: boolean;
stripeCustomer: inferQueryOutput<"viewer.stripeCustomer"> | undefined;
}) => {
if (!userIsPremium && isNewUsernamePremium && !stripeCustomer?.paidForPremium) {
return UsernameChangeStatusEnum.UPGRADE;
}
if (userIsPremium && !isNewUsernamePremium && getPremiumPlanMode() === "subscription") {
return UsernameChangeStatusEnum.DOWNGRADE;
}
return UsernameChangeStatusEnum.NORMAL;
};
const PremiumTextfield = (props: ICustomUsernameProps) => {
const { t } = useLocale();
const {
@@ -58,73 +79,39 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
usernameRef,
onSuccessMutation,
onErrorMutation,
readonly: disabled,
user,
} = props;
const [usernameIsAvailable, setUsernameIsAvailable] = useState(false);
const [markAsError, setMarkAsError] = useState(false);
const router = useRouter();
const { paymentStatus: recentAttemptPaymentStatus } = router.query;
const [openDialogSaveUsername, setOpenDialogSaveUsername] = useState(false);
const [usernameChangeCondition, setUsernameChangeCondition] = useState<UsernameChangeStatusEnum | null>(
null
);
const userIsPremium =
const { data: stripeCustomer } = trpc.useQuery(["viewer.stripeCustomer"]);
const isCurrentUsernamePremium =
user && user.metadata && hasKeyInMetadata(user, "isPremium") ? !!user.metadata.isPremium : false;
const [premiumUsername, setPremiumUsername] = useState(false);
const [isInputUsernamePremium, setIsInputUsernamePremium] = useState(false);
const debouncedApiCall = useCallback(
debounce(async (username) => {
debounce(async (username: string) => {
const { data } = await fetchUsername(username);
setMarkAsError(!data.available);
setPremiumUsername(data.premium);
setMarkAsError(!data.available && !!currentUsername && username !== currentUsername);
setIsInputUsernamePremium(data.premium);
setUsernameIsAvailable(data.available);
}, 150),
[]
);
useEffect(() => {
if (currentUsername !== inputUsernameValue) {
debouncedApiCall(inputUsernameValue);
} else if (inputUsernameValue === "") {
setMarkAsError(false);
setPremiumUsername(false);
setUsernameIsAvailable(false);
} else {
setPremiumUsername(userIsPremium);
setUsernameIsAvailable(false);
}
}, [inputUsernameValue]);
// Use the current username or if it's not set, use the one available from stripe
setInputUsernameValue(currentUsername || stripeCustomer?.username || "");
}, [setInputUsernameValue, currentUsername, stripeCustomer?.username]);
useEffect(() => {
if (usernameIsAvailable || premiumUsername) {
const condition = obtainNewUsernameChangeCondition({
userIsPremium,
isNewUsernamePremium: premiumUsername,
});
setUsernameChangeCondition(condition);
}
}, [usernameIsAvailable, premiumUsername]);
const obtainNewUsernameChangeCondition = ({
userIsPremium,
isNewUsernamePremium,
}: {
userIsPremium: boolean;
isNewUsernamePremium: boolean;
}) => {
let resultCondition: UsernameChangeStatusEnum;
if (!userIsPremium && isNewUsernamePremium) {
resultCondition = UsernameChangeStatusEnum.UPGRADE;
} else if (userIsPremium && !isNewUsernamePremium) {
resultCondition = UsernameChangeStatusEnum.DOWNGRADE;
} else {
resultCondition = UsernameChangeStatusEnum.NORMAL;
}
return resultCondition;
};
if (!inputUsernameValue) return;
debouncedApiCall(inputUsernameValue);
}, [debouncedApiCall, inputUsernameValue]);
const utils = trpc.useContext();
const updateUsername = trpc.useMutation("viewer.updateProfile", {
onSuccess: async () => {
onSuccessMutation && (await onSuccessMutation());
@@ -139,34 +126,64 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
},
});
const ActionButtons = (props: { index: string }) => {
const { index } = props;
return (usernameIsAvailable || premiumUsername) && currentUsername !== inputUsernameValue ? (
<div className="flex flex-row">
<Button
type="button"
color="primary"
className="mx-2"
onClick={() => setOpenDialogSaveUsername(true)}
data-testid={`update-username-btn-${index}`}>
{t("update")}
</Button>
<Button
type="button"
color="secondary"
className="mx-2"
onClick={() => {
if (currentUsername) {
setInputUsernameValue(currentUsername);
usernameRef.current.value = currentUsername;
}
}}>
{t("cancel")}
</Button>
</div>
) : (
<></>
);
// when current username isn't set - Go to stripe to check what username he wanted to buy and was it a premium and was it paid for
const paymentRequired = !currentUsername && stripeCustomer?.isPremium && !stripeCustomer?.paidForPremium;
const usernameChangeCondition = obtainNewUsernameChangeCondition({
userIsPremium: isCurrentUsernamePremium,
isNewUsernamePremium: isInputUsernamePremium,
stripeCustomer,
});
const usernameFromStripe = stripeCustomer?.username;
const paymentLink = `/api/integrations/stripepayment/subscription?intentUsername=${
inputUsernameValue || usernameFromStripe
}&action=${usernameChangeCondition}&callbackUrl=${router.asPath}`;
const ActionButtons = () => {
if (paymentRequired) {
return (
<div className="flex flex-row">
<Button
type="button"
color="primary"
className="mx-2"
href={paymentLink}
data-testid="reserve-username-btn">
{t("Reserve")}
</Button>
</div>
);
}
if ((usernameIsAvailable || isInputUsernamePremium) && currentUsername !== inputUsernameValue) {
return (
<div className="flex flex-row">
<Button
type="button"
color="primary"
className="mx-2"
onClick={() => setOpenDialogSaveUsername(true)}
data-testid="update-username-btn">
{t("update")}
</Button>
<Button
type="button"
color="secondary"
onClick={() => {
if (currentUsername) {
setInputUsernameValue(currentUsername);
if (usernameRef.current) {
usernameRef.current.value = currentUsername;
}
}
}}>
{t("cancel")}
</Button>
</div>
);
}
return <></>;
};
const saveUsername = () => {
@@ -177,81 +194,94 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
}
};
let paymentMsg = !currentUsername ? (
<span className="text-xs text-orange-400">
You need to reserve your premium username for {getPremiumPlanPriceValue()}
</span>
) : null;
if (recentAttemptPaymentStatus && recentAttemptPaymentStatus !== "paid") {
paymentMsg = (
<span className="text-sm text-red-500">
Your payment could not be completed. Your username is still not reserved
</span>
);
}
return (
<>
<div style={{ display: "flex", justifyItems: "center" }}>
<div>
<div className="flex justify-items-center">
<Label htmlFor="username">{t("username")}</Label>
</div>
<div className="mt-1 flex rounded-md">
<div className="mt-2 flex rounded-md">
<span
className={classNames(
"inline-flex items-center rounded-l-sm border border-gray-300 bg-gray-50 px-3 text-sm text-gray-500"
isInputUsernamePremium ? "border-1 border-orange-400 " : "",
"hidden h-9 items-center rounded-l-md border border-r-0 border-gray-300 border-r-gray-300 bg-gray-50 px-3 text-sm text-gray-500 md:inline-flex"
)}>
{process.env.NEXT_PUBLIC_WEBSITE_URL}/
{process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")}/
</span>
<div style={{ position: "relative", width: "100%" }}>
<div className="relative w-full">
<Input
ref={usernameRef}
name="username"
autoComplete="none"
autoCapitalize="none"
autoCorrect="none"
disabled={disabled}
className={classNames(
"mt-0 rounded-l-none",
"border-l-1 mb-0 mt-0 rounded-md rounded-l-none font-sans text-sm leading-4 focus:!ring-0",
isInputUsernamePremium
? "border-1 focus:border-1 border-orange-400 focus:border-orange-400"
: "border-1 focus:border-2",
markAsError
? "focus:shadow-0 focus:ring-shadow-0 border-red-500 focus:border-red-500 focus:outline-none focus:ring-0"
: ""
? "focus:shadow-0 focus:ring-shadow-0 border-red-500 focus:border-red-500 focus:outline-none"
: "border-l-gray-300",
disabled ? "bg-gray-100 text-gray-400 focus:border-0" : ""
)}
defaultValue={currentUsername}
value={inputUsernameValue}
onChange={(event) => {
event.preventDefault();
// Reset payment status
delete router.query.paymentStatus;
setInputUsernameValue(event.target.value);
}}
data-testid="username-input"
/>
{currentUsername !== inputUsernameValue && (
<div
className="top-0"
style={{
position: "absolute",
right: 2,
display: "flex",
flexDirection: "row",
}}>
<span
className={classNames(
"mx-2 py-1",
premiumUsername ? "text-orange-500" : "",
usernameIsAvailable ? "" : ""
)}>
{premiumUsername ? <Icon.FiStar className="mt-[4px] w-6" /> : <></>}
{!premiumUsername && usernameIsAvailable ? <Icon.FiCheck className="mt-[4px] w-6" /> : <></>}
</span>
</div>
)}
</div>
<div className="xs:hidden">
<ActionButtons index="desktop" />
<div className="absolute top-0 right-2 flex flex-row">
<span
className={classNames(
"mx-2 py-1",
isInputUsernamePremium ? "text-orange-400" : "",
usernameIsAvailable ? "" : ""
)}>
{isInputUsernamePremium ? <StarIconSolid className="mt-[2px] w-6" /> : <></>}
{!isInputUsernamePremium && usernameIsAvailable ? <Icon.FiCheck className="mt-2 w-6" /> : <></>}
</span>
</div>
</div>
{(usernameIsAvailable || isInputUsernamePremium) && currentUsername !== inputUsernameValue && (
<div className="flex justify-end">
<ActionButtons />
</div>
)}
</div>
{paymentMsg}
{markAsError && <p className="mt-1 text-xs text-red-500">Username is already taken</p>}
{usernameIsAvailable && (
<p className={classNames("mt-1 text-xs text-gray-900")}>
{usernameChangeCondition === UsernameChangeStatusEnum.DOWNGRADE && (
<>{t("standard_to_premium_username_description")}</>
<>{t("premium_to_standard_username_description")}</>
)}
</p>
)}
{(usernameIsAvailable || premiumUsername) && currentUsername !== inputUsernameValue && (
<div className="mt-2 flex justify-end sm:hidden">
<ActionButtons index="mobile" />
</div>
)}
<Dialog open={openDialogSaveUsername}>
<DialogContent>
<div style={{ display: "flex", flexDirection: "row" }}>
<div className="flex flex-row">
<div className="xs:hidden flex h-10 w-10 flex-shrink-0 justify-center rounded-full bg-[#FAFAFA]">
<Icon.FiEdit2 className="m-auto h-6 w-6" />
</div>
@@ -291,7 +321,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
type="button"
loading={updateUsername.isLoading}
data-testid="go-to-billing"
href={`/api/integrations/stripepayment/subscription?intentUsername=${inputUsernameValue}`}>
href={paymentLink}>
<>
{t("go_to_stripe_billing")} <Icon.FiExternalLink className="ml-1 h-4 w-4" />
</>
@@ -317,7 +347,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
</div>
</DialogContent>
</Dialog>
</>
</div>
);
};
@@ -7,16 +7,15 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { TRPCClientErrorLike } from "@calcom/trpc/client";
import { trpc } from "@calcom/trpc/react";
import { AppRouter } from "@calcom/trpc/server/routers/_app";
import Button from "@calcom/ui/Button";
import { Dialog, DialogClose, DialogContent, DialogHeader } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { Input, Label } from "@calcom/ui/form/fields";
import { Input, Label, Button } from "@calcom/ui/v2";
interface ICustomUsernameProps {
currentUsername: string | undefined;
setCurrentUsername: (value: string | undefined) => void;
inputUsernameValue: string | undefined;
usernameRef: MutableRefObject<HTMLInputElement>;
usernameRef: MutableRefObject<HTMLInputElement | null>;
setInputUsernameValue: (value: string) => void;
onSuccessMutation?: () => void;
onErrorMutation?: (error: TRPCClientErrorLike<AppRouter>) => void;
@@ -73,15 +72,14 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
},
});
const ActionButtons = (props: { index: string }) => {
const { index } = props;
const ActionButtons = () => {
return usernameIsAvailable && currentUsername !== inputUsernameValue ? (
<div className="flex flex-row">
<Button
type="button"
className="mx-2"
onClick={() => setOpenDialogSaveUsername(true)}
data-testid={`update-username-btn-${index}`}>
data-testid="update-username-btn">
{t("update")}
</Button>
<Button
@@ -91,7 +89,9 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
onClick={() => {
if (currentUsername) {
setInputUsernameValue(currentUsername);
usernameRef.current.value = currentUsername;
if (usernameRef.current) {
usernameRef.current.value = currentUsername;
}
}
}}>
{t("cancel")}
@@ -103,16 +103,16 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
};
return (
<>
<div>
<div>
<Label htmlFor="username">{t("username")}</Label>
</div>
<div className="mt-1 flex rounded-md">
<div className="mt-2 flex rounded-md">
<span
className={classNames(
"inline-flex items-center rounded-l-sm border border-gray-300 bg-gray-50 px-3 text-sm text-gray-500"
"inline-flex items-center rounded-l-md border border-r-0 border-gray-300 bg-gray-50 px-3 text-sm text-gray-500"
)}>
{process.env.NEXT_PUBLIC_WEBSITE_URL}/
{process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")}/
</span>
<div className="relative w-full">
<Input
@@ -122,7 +122,7 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
autoCapitalize="none"
autoCorrect="none"
className={classNames(
"mt-0 rounded-l-none",
"mb-0 mt-0 h-6 rounded-md rounded-l-none",
markAsError
? "focus:shadow-0 focus:ring-shadow-0 border-red-500 focus:border-red-500 focus:outline-none focus:ring-0"
: ""
@@ -136,21 +136,21 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
/>
{currentUsername !== inputUsernameValue && (
<div className="absolute right-[2px] top-0 flex flex-row">
<span className={classNames("mx-2 py-1")}>
{usernameIsAvailable ? <Icon.FiCheck className="mt-[4px] w-6" /> : <></>}
<span className={classNames("mx-2 py-2")}>
{usernameIsAvailable ? <Icon.FiCheck className="mt-[2px] w-6" /> : <></>}
</span>
</div>
)}
</div>
<div className="hidden md:inline">
<ActionButtons index="desktop" />
<ActionButtons />
</div>
</div>
{markAsError && <p className="mt-1 text-xs text-red-500">Username is already taken</p>}
{usernameIsAvailable && currentUsername !== inputUsernameValue && (
<div className="mt-2 flex justify-end sm:hidden">
<ActionButtons index="mobile" />
<div className="mt-2 flex justify-end md:hidden">
<ActionButtons />
</div>
)}
<Dialog open={openDialogSaveUsername}>
@@ -200,7 +200,7 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
</div>
</DialogContent>
</Dialog>
</>
</div>
);
};
+1 -2
View File
@@ -80,7 +80,7 @@ function Select<
<ReactSelect
theme={(theme) => ({
...theme,
borderRadius: 2,
borderRadius: 6,
colors: {
...theme.colors,
...(hasDarkTheme
@@ -88,7 +88,6 @@ function Select<
: {
/** Light Theme starts */
primary: "var(--brand-color)",
primary50: "rgba(209 , 213, 219, var(--tw-bg-opacity))",
primary25: "rgba(244, 245, 246, var(--tw-bg-opacity))",
/** Light Theme Ends */
@@ -0,0 +1,78 @@
import React from "react";
import { OptionProps } from "react-select";
import { InstallAppButton } from "@calcom/app-store/components";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import type { App } from "@calcom/types/App";
import { Button } from "@calcom/ui";
import { Icon } from "@calcom/ui/Icon";
import { Select } from "@calcom/ui/v2";
import { QueryCell } from "@lib/QueryCell";
interface AdditionalCalendarSelectorProps {
isLoading?: boolean;
}
const ImageOption = (optionProps: OptionProps<{ [key: string]: string; type: App["type"] }>) => {
const { t } = useLocale();
const { data } = optionProps;
return data.slug !== "add-new" ? (
<InstallAppButton
type={data.type}
render={(installProps) => {
return (
<Button {...installProps} className="w-full" color="minimal">
{data.image && (
<img className="float-left mr-3 inline h-5 w-5" src={data.image} alt={data.label} />
)}
<p>{data.label}</p>
</Button>
);
}}
/>
) : (
<Button className="w-full" color="minimal" href="/apps/categories/calendar">
<Icon.FiPlus className="text-color mr-3 ml-1 h-4 w-4" />
<p>{t("add_new_calendar")}...</p>
</Button>
);
};
const AdditionalCalendarSelector = ({ isLoading }: AdditionalCalendarSelectorProps): JSX.Element | null => {
const { t } = useLocale();
const query = trpc.useQuery(["viewer.integrations", { variant: "calendar", onlyInstalled: true }]);
return (
<QueryCell
query={query}
success={({ data }) => {
const options = data.items.map((item) => ({
label: item.name,
slug: item.slug,
image: item.logo,
type: item.type,
}));
options.push({
label: "Add new calendars",
slug: "add-new",
image: "",
type: "new_other",
});
return (
<Select
name="additionalCalendar"
placeholder={t("install_another")}
options={options}
isSearchable={false}
isLoading={isLoading}
components={{ Option: ImageOption }}
/>
);
}}
/>
);
};
export default AdditionalCalendarSelector;
+225 -247
View File
@@ -1,22 +1,25 @@
import Link from "next/link";
import React, { useEffect, useState } from "react";
import { useRouter } from "next/router";
import React, { useState } from "react";
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
import { InstallAppButton } from "@calcom/app-store/components";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import LicenseRequired from "@calcom/features/ee/common/components/v2/LicenseRequired";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { trpc } from "@calcom/trpc/react";
import { App as AppType } from "@calcom/types/App";
import Badge from "@calcom/ui/Badge";
import { Icon } from "@calcom/ui/Icon";
import { showToast, SkeletonText } from "@calcom/ui/v2";
import { Button, SkeletonButton, Shell } from "@calcom/ui/v2";
import DisconnectIntegration from "@calcom/ui/v2/modules/integrations/DisconnectIntegration";
const Component = ({
name,
type,
logo,
slug,
variant,
body,
categories,
author,
@@ -33,15 +36,15 @@ const Component = ({
images,
}: Parameters<typeof App>[0]) => {
const { t } = useLocale();
const { data: user } = trpc.useQuery(["viewer.me"]);
const hasImages = images && images.length > 0;
const router = useRouter();
const mutation = useAddAppMutation(null, {
onSuccess: () => {
showToast("App successfully installed", "success");
showToast(t("app_successfully_installed"), "success");
},
onError: (error) => {
if (error instanceof Error) showToast(error.message || "App could not be installed", "error");
if (error instanceof Error) showToast(error.message || t("app_could_not_be_installed"), "error");
},
});
@@ -50,253 +53,224 @@ const Component = ({
currency: "USD",
useGrouping: false,
}).format(price);
const [installedAppCount, setInstalledAppCount] = useState(0);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
async function getInstalledApp(appCredentialType: string) {
const queryParam = new URLSearchParams();
queryParam.set("app-credential-type", appCredentialType);
try {
const result = await fetch(`/api/app-store/installed?${queryParam.toString()}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
}).then((data) => {
setIsLoading(false);
return data;
});
if (result.status === 200) {
const res = await result.json();
setInstalledAppCount(res.count);
}
} catch (error) {
if (error instanceof Error) {
console.log(error.message);
}
}
}
getInstalledApp(type);
}, [type]);
const [existingCredentials, setExistingCredentials] = useState<number[]>([]);
const appCredentials = trpc.useQuery(["viewer.appCredentialsByType", { appType: type }], {
onSuccess(data) {
setExistingCredentials(data);
},
});
const allowedMultipleInstalls = categories.indexOf("calendar") > -1;
return (
<>
<div className="px-4 pb-3 pt-3 md:px-8 lg:px-0 lg:pt-0">
<Link href="/apps">
<a className="text-md hover:text-brand-400 inline-flex rounded-sm py-2 font-semibold text-gray-900">
<Icon.FiArrowLeft className="mr-2 h-6 w-6" /> {t("app_store")}
</a>
</Link>
</div>
<div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0">
{hasImages && (
<div className="flex-2 mb-4 -ml-4 -mr-4 flex w-auto snap-x snap-mandatory flex-row overflow-auto whitespace-nowrap bg-gray-100 p-4 md:mb-8 md:-ml-8 md:-mr-8 md:p-8 lg:mx-0 lg:mb-0 lg:max-w-2xl lg:flex-col lg:rounded-md">
{images &&
images.map((img) => (
<img
key={img}
src={img}
alt={`Screenshot of app ${name}`}
className="mr-4 h-auto max-h-80 max-w-[90%] snap-center rounded-md object-contain last:mb-0 md:max-h-min lg:mb-4 lg:mr-0 lg:max-w-full"
/>
))}
</div>
)}
<div
className={classNames(
"sticky top-0 max-w-xl flex-1 pb-12 text-sm lg:pb-0",
hasImages && "lg:ml-8"
)}>
<div className="mb-8 flex">
<header>
<div className="mb-4 flex items-center">
<img className="min-h-16 min-w-16 h-16 w-16" src={logo} alt={name} />
<h1 className="font-cal ml-4 text-3xl text-gray-900">{name}</h1>
{isProOnly && user?.plan === "FREE" ? (
<Badge className="ml-2" variant="default">
PRO
</Badge>
) : null}
</div>
<h2 className="text-sm font-medium text-gray-600">
<span className="rounded-md bg-gray-100 p-1 text-xs capitalize text-gray-800">
{categories[0]}
</span>{" "}
• {t("published_by", { author })}
</h2>
</header>
</div>
{!isLoading ? (
isGlobal || (installedAppCount > 0 && allowedMultipleInstalls) ? (
<div className="flex space-x-3">
<Button StartIcon={Icon.FiCheck} color="secondary" disabled>
{installedAppCount > 0 ? t("active_install", { count: installedAppCount }) : t("default")}
</Button>
{!isGlobal && (
<InstallAppButton
type={type}
isProOnly={isProOnly}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
onClick: () => {
mutation.mutate({ type });
},
loading: mutation.isLoading,
};
}
return (
<Button
StartIcon={Icon.FiPlus}
{...props}
// @TODO: Overriding color and size prevent us from
// having to duplicate InstallAppButton for now.
color="primary"
size="base"
data-testid="install-app-button">
{t("install_another")}
</Button>
);
}}
/>
)}
</div>
) : installedAppCount > 0 ? (
<Button color="secondary" disabled title="App already installed">
{t("installed")}
</Button>
) : (
<InstallAppButton
type={type}
isProOnly={isProOnly}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
onClick: () => {
mutation.mutate({ type });
},
loading: mutation.isLoading,
};
}
return (
<Button
data-testid="install-app-button"
{...props}
// @TODO: Overriding color and size prevent us from
// having to duplicate InstallAppButton for now.
color="primary"
size="base">
{t("install_app")}
</Button>
);
}}
<div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0">
{hasImages && (
<div className="flex-2 mb-4 -ml-4 -mr-4 flex min-h-[450px] w-auto snap-x snap-mandatory flex-row overflow-auto whitespace-nowrap bg-gray-100 p-4 md:mb-8 md:-ml-8 md:-mr-8 md:p-8 lg:mx-0 lg:mb-0 lg:max-w-2xl lg:flex-col lg:rounded-md">
{images ? (
images.map((img) => (
<img
key={img}
src={img}
alt={`Screenshot of app ${name}`}
className="mr-4 h-auto max-h-80 max-w-[90%] snap-center rounded-md object-contain last:mb-0 md:max-h-min lg:mb-4 lg:mr-0 lg:max-w-full"
/>
)
))
) : (
<SkeletonButton width="24" height="10" />
<SkeletonText />
)}
{price !== 0 && (
<span className="block text-right">
{feeType === "usage-based" ? commission + "% + " + priceInDollar + "/booking" : priceInDollar}
{feeType === "monthly" && "/" + t("month")}
</span>
)}
<div className="mt-8 space-x-2">{body}</div>
<h4 className="mt-8 font-semibold text-gray-900 ">{t("pricing")}</h4>
<span>
{price === 0 ? (
"Free"
) : (
<>
{Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
useGrouping: false,
}).format(price)}
{feeType === "monthly" && "/" + t("month")}
</>
)}
</span>
<h4 className="mt-8 mb-2 font-semibold text-gray-900 ">{t("learn_more")}</h4>
<ul className="prose-sm -ml-1 -mr-1 leading-5">
{docs && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-sm font-normal text-black no-underline hover:underline"
href={docs}>
<Icon.FiBookOpen className="mr-1 -mt-1 inline h-4 w-4 text-gray-500" />
{t("documentation")}
</a>
</li>
)}
{website && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={website}>
<Icon.FiExternalLink className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{website.replace("https://", "")}
</a>
</li>
)}
{email && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={"mailto:" + email}>
<Icon.FiMail className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{email}
</a>
</li>
)}
{tos && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={tos}>
<Icon.FiFile className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{t("terms_of_service")}
</a>
</li>
)}
{privacy && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={privacy}>
<Icon.FiShield className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{t("privacy_policy")}
</a>
</li>
)}
</ul>
<hr className="my-8" />
<span className="leading-1 block text-xs text-gray-500">
Every app published on the Cal.com App Store is open source and thoroughly tested via peer
reviews. Nevertheless, Cal.com, Inc. does not endorse or certify these apps unless they are
published by Cal.com. If you encounter inappropriate content or behaviour please report it.
</span>
<a className="mt-2 block text-xs text-red-500" href="mailto:help@cal.com">
<Icon.FiFlag className="inline h-3 w-3" /> Report App
</a>
</div>
)}
<div
className={classNames(
"sticky top-0 -mt-4 max-w-xl flex-1 pb-12 text-sm lg:pb-0",
hasImages && "lg:ml-8"
)}>
<div className="mb-8 flex pt-4">
<header>
<div className="mb-4 flex items-center">
<img className="min-h-16 min-w-16 h-16 w-16" src={logo} alt={name} />
<h1 className="font-cal ml-4 text-3xl text-gray-900">{name}</h1>
</div>
<h2 className="text-sm font-medium text-gray-600">
<Link href={`categories/${categories[0]}`}>
<a className="rounded-md bg-gray-100 p-1 text-xs capitalize text-gray-800">{categories[0]}</a>
</Link>{" "}
• {t("published_by", { author })}
</h2>
</header>
</div>
{!appCredentials.isLoading ? (
isGlobal ||
(existingCredentials.length > 0 && allowedMultipleInstalls ? (
<div className="flex space-x-3">
<Button StartIcon={Icon.FiCheck} color="secondary" disabled>
{existingCredentials.length > 0
? t("active_install", { count: existingCredentials.length })
: t("default")}
</Button>
{!isGlobal && (
<InstallAppButton
type={type}
isProOnly={isProOnly}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
onClick: () => {
mutation.mutate({ type, variant, slug });
},
loading: mutation.isLoading,
};
}
return (
<Button
StartIcon={Icon.FiPlus}
{...props}
// @TODO: Overriding color and size prevent us from
// having to duplicate InstallAppButton for now.
color="primary"
size="base"
data-testid="install-app-button">
{t("install_another")}
</Button>
);
}}
/>
)}
</div>
) : existingCredentials.length > 0 ? (
<DisconnectIntegration
label={t("disconnect")}
credentialId={existingCredentials[0]}
onSuccess={() => {
router.replace("/apps/installed");
}}
/>
) : (
<InstallAppButton
type={type}
isProOnly={isProOnly}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
onClick: () => {
mutation.mutate({ type, variant, slug });
},
loading: mutation.isLoading,
};
}
return (
<Button
data-testid="install-app-button"
{...props}
// @TODO: Overriding color and size prevent us from
// having to duplicate InstallAppButton for now.
color="primary"
size="base">
{t("install_app")}
</Button>
);
}}
/>
))
) : (
<SkeletonButton className="h-10 w-24" />
)}
{price !== 0 && (
<span className="block text-right">
{feeType === "usage-based" ? commission + "% + " + priceInDollar + "/booking" : priceInDollar}
{feeType === "monthly" && "/" + t("month")}
</span>
)}
<div className="prose prose-sm mt-8">{body}</div>
<h4 className="mt-8 font-semibold text-gray-900 ">{t("pricing")}</h4>
<span>
{price === 0 ? (
"Free"
) : (
<>
{Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
useGrouping: false,
}).format(price)}
{feeType === "monthly" && "/" + t("month")}
</>
)}
</span>
<h4 className="mt-8 mb-2 font-semibold text-gray-900 ">{t("learn_more")}</h4>
<ul className="prose-sm -ml-1 -mr-1 leading-5">
{docs && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-sm font-normal text-black no-underline hover:underline"
href={docs}>
<Icon.FiBookOpen className="mr-1 -mt-1 inline h-4 w-4 text-gray-500" />
{t("documentation")}
</a>
</li>
)}
{website && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={website}>
<Icon.FiExternalLink className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{website.replace("https://", "")}
</a>
</li>
)}
{email && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={"mailto:" + email}>
<Icon.FiMail className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{email}
</a>
</li>
)}
{tos && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={tos}>
<Icon.FiFile className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{t("terms_of_service")}
</a>
</li>
)}
{privacy && (
<li>
<a
target="_blank"
rel="noreferrer"
className="font-normal text-black no-underline hover:underline"
href={privacy}>
<Icon.FiShield className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
{t("privacy_policy")}
</a>
</li>
)}
</ul>
<hr className="my-8" />
<span className="leading-1 block text-xs text-gray-500">{t("every_app_published")}</span>
<a className="mt-2 block text-xs text-red-500" href="mailto:help@cal.com">
<Icon.FiFlag className="inline h-3 w-3" /> {t("report_app")}
</a>
</div>
</>
</div>
);
};
@@ -305,6 +279,8 @@ export default function App(props: {
type: AppType["type"];
isGlobal?: AppType["isGlobal"];
logo: string;
slug: string;
variant: string;
body: React.ReactNode;
categories: string[];
author: string;
@@ -321,8 +297,10 @@ export default function App(props: {
isProOnly: AppType["isProOnly"];
images?: string[];
}) {
const { t } = useLocale();
return (
<Shell large isPublic>
<Shell large isPublic heading={t("app_store")} backPath="/apps">
{props.licenseRequired ? (
<LicenseRequired>
<Component {...props} />
@@ -0,0 +1,332 @@
import { useMutation } from "@tanstack/react-query";
import Link from "next/link";
import { Fragment } from "react";
import { InstallAppButton } from "@calcom/app-store/components";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { trpc } from "@calcom/trpc/react";
import Button from "@calcom/ui/Button";
import { Icon } from "@calcom/ui/Icon";
import SkeletonLoader from "@calcom/ui/apps/SkeletonLoader";
import { Alert, EmptyScreen } from "@calcom/ui/v2";
import { List } from "@calcom/ui/v2/core/List";
import { ShellSubHeading } from "@calcom/ui/v2/core/Shell";
import Switch from "@calcom/ui/v2/core/Switch";
import DisconnectIntegration from "@calcom/ui/v2/modules/integrations/DisconnectIntegration";
import { QueryCell } from "@lib/QueryCell";
import SubHeadingTitleWithConnections from "@components/integrations/SubHeadingTitleWithConnections";
import AdditionalCalendarSelector from "@components/v2/apps/AdditionalCalendarSelector";
import DestinationCalendarSelector from "@components/v2/apps/DestinationCalendarSelector";
import IntegrationListItem from "@components/v2/apps/IntegrationListItem";
type Props = {
onChanged: () => unknown | Promise<unknown>;
fromOnboarding?: boolean;
destinationCalendarId?: string;
};
function CalendarSwitch(props: {
type: string;
externalId: string;
title: string;
defaultSelected: boolean;
destination?: boolean;
}) {
const { t } = useLocale();
const utils = trpc.useContext();
const mutation = useMutation<
unknown,
unknown,
{
isOn: boolean;
}
>(
async ({ isOn }) => {
const body = {
integration: props.type,
externalId: props.externalId,
};
if (isOn) {
const res = await fetch("/api/availability/calendar", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) {
throw new Error("Something went wrong");
}
} else {
const res = await fetch("/api/availability/calendar", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) {
throw new Error("Something went wrong");
}
}
},
{
async onSettled() {
await utils.invalidateQueries(["viewer.integrations"]);
},
onError() {
showToast(`Something went wrong when toggling "${props.title}""`, "error");
},
}
);
return (
<div className="flex py-1">
<Switch
key={props.externalId}
name="enabled"
label={props.title}
defaultChecked={props.defaultSelected}
onCheckedChange={(isOn: boolean) => {
mutation.mutate({ isOn });
}}
/>
{!!props.destination && (
<span className="ml-4 inline-flex items-center gap-1 rounded-md bg-gray-100 px-2 py-1 text-sm font-normal text-gray-800">
<Icon.FiArrowLeft className="h-4 w-4" />
{t("adding_events_to")}
</span>
)}
</div>
);
}
function CalendarList(props: Props) {
const { t } = useLocale();
const query = trpc.useQuery(["viewer.integrations", { variant: "calendar", onlyInstalled: false }]);
return (
<QueryCell
query={query}
success={({ data }) => (
<List>
{data.items.map((item) => (
<IntegrationListItem
name={item.name}
slug={item.slug}
key={item.title}
title={item.title}
logo={item.logo}
description={item.description}
actions={
<InstallAppButton
type={item.type}
render={(buttonProps) => (
<Button color="secondary" {...buttonProps}>
{t("connect")}
</Button>
)}
onChanged={() => props.onChanged()}
/>
}
/>
))}
</List>
)}
/>
);
}
function ConnectedCalendarsList(props: Props) {
const { t } = useLocale();
const query = trpc.useQuery(["viewer.connectedCalendars"], { suspense: true });
const { fromOnboarding } = props;
return (
<QueryCell
query={query}
empty={() => null}
success={({ data }) => {
if (!data.connectedCalendars.length) {
return null;
}
return (
<List className="flex flex-col gap-6" noBorderTreatment>
{data.connectedCalendars.map((item) => (
<Fragment key={item.credentialId}>
{item.calendars ? (
<IntegrationListItem
slug={item.integration.slug}
title={item.integration.title}
logo={item.integration.logo}
description={item.primary?.externalId || "No external Id"}
actions={
<div className="flex w-32 justify-end">
<DisconnectIntegration
credentialId={item.credentialId}
trashIcon
onSuccess={props.onChanged}
buttonProps={{ className: "border border-gray-300" }}
/>
</div>
}>
{!fromOnboarding && (
<>
<p className="px-4 pt-4 text-sm text-neutral-500">{t("toggle_calendars_conflict")}</p>
<ul className="space-y-2 p-4">
{item.calendars.map((cal) => (
<CalendarSwitch
key={cal.externalId}
externalId={cal.externalId}
title={cal.name || "Nameless calendar"}
type={item.integration.type}
defaultSelected={cal.isSelected}
destination={cal.externalId === props.destinationCalendarId}
/>
))}
</ul>
</>
)}
</IntegrationListItem>
) : (
<Alert
severity="warning"
title={t("something_went_wrong")}
message={
<span>
<Link href={"/apps/" + item.integration.slug}>{item.integration.name}</Link>:{" "}
{t("calendar_error")}
</span>
}
iconClassName="h-10 w-10 ml-2 mr-1 mt-0.5"
actions={
<div className="flex w-32 justify-end md:pr-1">
<DisconnectIntegration
credentialId={item.credentialId}
trashIcon
onSuccess={props.onChanged}
buttonProps={{ className: "border border-gray-300" }}
/>
</div>
}
/>
)}
</Fragment>
))}
</List>
);
}}
/>
);
}
export function CalendarListContainer(props: { heading?: boolean; fromOnboarding?: boolean }) {
const { t } = useLocale();
const { heading = true, fromOnboarding } = props;
const utils = trpc.useContext();
const onChanged = () =>
Promise.allSettled([
utils.invalidateQueries(["viewer.integrations", { variant: "calendar", onlyInstalled: true }], {
exact: true,
}),
utils.invalidateQueries(["viewer.connectedCalendars"]),
]);
const query = trpc.useQuery(["viewer.connectedCalendars"]);
const installedCalendars = trpc.useQuery([
"viewer.integrations",
{ variant: "calendar", onlyInstalled: true },
]);
const mutation = trpc.useMutation("viewer.setDestinationCalendar", {
onSuccess: () => {
utils.invalidateQueries(["viewer.connectedCalendars"]);
},
});
return (
<QueryCell
query={query}
customLoader={<SkeletonLoader />}
success={({ data }) => {
return (
<>
{!!data.connectedCalendars.length || !!installedCalendars.data?.items.length ? (
<>
{heading && (
<div className="flex flex-col gap-6 rounded-md border border-gray-200 p-7">
<ShellSubHeading
title={t("calendar")}
subtitle={t("installed_app_calendar_description")}
className="mb-0 flex flex-wrap items-center gap-4 md:mb-3 md:gap-0"
actions={
<div className="flex flex-col xl:flex-row xl:space-x-5">
{!!data.connectedCalendars.length && (
<div className=" flex items-center">
<AdditionalCalendarSelector isLoading={mutation.isLoading} />
</div>
)}
</div>
}
/>
<div className="flex justify-between rounded-md border border-gray-200 bg-gray-50 p-4">
<div className="flex w-full flex-col items-start gap-4 md:flex-row md:items-center">
<div className="relative rounded-md border border-gray-200 bg-white p-1.5">
<Icon.FiCalendar className="h-8 w-8" strokeWidth="1" />
<Icon.FiPlus
className="absolute left-4 top-1/2 ml-0.5 mt-[1px] h-2 w-2 text-black"
strokeWidth="4"
/>
</div>
<div className="md:w-6/12">
<h1 className="text-sm font-semibold">{t("create_events_on")}</h1>
<p className="text-sm font-normal">{t("set_calendar")}</p>
</div>
<div className="flex justify-end md:w-6/12">
<DestinationCalendarSelector
onChange={mutation.mutate}
hidePlaceholder
isLoading={mutation.isLoading}
value={data.destinationCalendar?.externalId}
/>
</div>
</div>
</div>
<ConnectedCalendarsList
onChanged={onChanged}
fromOnboarding={fromOnboarding}
destinationCalendarId={data.destinationCalendar?.externalId}
/>
</div>
)}
</>
) : fromOnboarding ? (
<>
{!!query.data?.connectedCalendars.length && (
<ShellSubHeading
className="mt-4"
title={<SubHeadingTitleWithConnections title={t("connect_additional_calendar")} />}
/>
)}
<CalendarList onChanged={onChanged} />
</>
) : (
<EmptyScreen
Icon={Icon.FiCalendar}
headline={t("no_category_apps", {
category: t("calendar").toLowerCase(),
})}
description={t(`no_category_apps_description_calendar`)}
buttonRaw={
<Button color="secondary" href="/apps/categories/calendar">
{t(`connect_calendar_apps`)}
</Button>
}
/>
)}
</>
);
}}
/>
);
}
@@ -0,0 +1,149 @@
import classNames from "classnames";
import React, { useEffect, useState } from "react";
import Select from "react-select";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { DestinationCalendar } from "@calcom/prisma/client";
import { trpc } from "@calcom/trpc/react";
interface Props {
onChange: (value: { externalId: string; integration: string }) => void;
isLoading?: boolean;
hidePlaceholder?: boolean;
/** The external Id of the connected calendar */
value: string | undefined;
destinationCalendar?: DestinationCalendar | null;
}
const DestinationCalendarSelector = ({
onChange,
isLoading,
value,
hidePlaceholder,
destinationCalendar,
}: Props): JSX.Element | null => {
const { t } = useLocale();
const query = trpc.useQuery(["viewer.connectedCalendars"]);
const [selectedOption, setSelectedOption] = useState<{ value: string; label: string } | null>(null);
// Extra styles to show prefixed text in react-select
const content = (hidePlaceholder = false) => {
if (!hidePlaceholder) {
return {
alignItems: "center",
display: "flex",
":before": {
content: `'${t("select_destination_calendar")}:'`,
display: "block",
marginRight: 8,
},
};
}
return {};
};
useEffect(() => {
const selected = query.data?.connectedCalendars
.map((connected) => connected.calendars ?? [])
.flat()
.find((cal) => cal.externalId === value);
if (selected) {
setSelectedOption({
value: `${selected.integration}:${selected.externalId}`,
label: `${selected.name} (${selected.externalId})` || "",
});
}
}, [query.data?.connectedCalendars, value]);
if (!query.data?.connectedCalendars.length) {
return null;
}
const options =
query.data.connectedCalendars.map((selectedCalendar) => ({
key: selectedCalendar.credentialId,
label: `${selectedCalendar.integration.title} (${selectedCalendar.primary?.name})`,
options: (selectedCalendar.calendars ?? [])
.filter((cal) => cal.readOnly === false)
.map((cal) => ({
label: `${cal.name} (${cal.externalId})` || "",
value: `${cal.integration}:${cal.externalId}`,
})),
})) ?? [];
const defaultCalendarSelectedString = destinationCalendar?.externalId
? `(${
destinationCalendar.externalId.length > 15
? destinationCalendar.externalId.substring(0, 15) + "..."
: destinationCalendar.externalId
})`
: "";
return (
<div
className="relative flex items-center"
title={`${t("select_destination_calendar")}: ${selectedOption?.label || ""}`}>
<Select
name="primarySelectedCalendar"
placeholder={
!hidePlaceholder ? (
`${t("select_destination_calendar")}`
) : (
<span className="whitespace-nowrap">
{t("default_calendar_selected")} {defaultCalendarSelectedString}
</span>
)
}
options={options}
styles={{
placeholder: (styles) => ({ ...styles, ...content(hidePlaceholder) }),
singleValue: (styles) => ({ ...styles, ...content(hidePlaceholder) }),
option: (defaultStyles, state) => ({
...defaultStyles,
backgroundColor: state.isSelected
? state.isFocused
? "var(--brand-color)"
: "var(--brand-color)"
: state.isFocused
? "var(--brand-color-dark-mode)"
: "var(--brand-text-color)",
}),
control: (defaultStyles) => {
return {
...defaultStyles,
borderRadius: "6px",
"@media only screen and (min-width: 640px)": {
...(defaultStyles["@media only screen and (min-width: 640px)"] as object),
width: "100%",
},
};
},
}}
isSearchable={false}
className={classNames(
"block w-full min-w-0 flex-1 rounded-none rounded-r-sm border-gray-300 text-sm",
!hidePlaceholder && "font-medium"
)}
onChange={(option) => {
setSelectedOption(option);
if (!option) {
return;
}
/* Split only the first `:`, since Apple uses the full URL as externalId */
const [integration, externalId] = option.value.split(/:(.+)/);
onChange({
integration,
externalId,
});
}}
isLoading={isLoading}
value={selectedOption}
components={{
IndicatorSeparator: () => null,
}}
/>
</div>
);
};
export default DestinationCalendarSelector;
@@ -0,0 +1,53 @@
import Link from "next/link";
import { useRouter } from "next/router";
import { ReactNode, useEffect, useState } from "react";
import { ListItem, ListItemText, ListItemTitle } from "@calcom/ui/v2/core/List";
import classNames from "@lib/classNames";
function IntegrationListItem(props: {
imageSrc?: string;
slug: string;
name?: string;
title?: string;
description: string;
actions?: ReactNode;
children?: ReactNode;
logo: string;
destination?: boolean;
}): JSX.Element {
const router = useRouter();
const { hl } = router.query;
const [highlight, setHighlight] = useState(hl === props.slug);
const title = props.name || props.title;
useEffect(() => {
const timer = setTimeout(() => setHighlight(false), 3000);
return () => {
clearTimeout(timer);
};
}, []);
return (
<ListItem
expanded={!!props.children}
className={classNames(
"my-0 flex-col border transition-colors duration-500 first:rounded-t-md last:rounded-b-md",
highlight ? "bg-yellow-100" : ""
)}>
<div className={classNames("flex w-full flex-1 items-center space-x-2 p-4 rtl:space-x-reverse")}>
{props.logo && <img className="h-11 w-11" src={props.logo} alt={title} />}
<div className="flex-grow truncate pl-2">
<ListItemTitle component="h3">
<Link href={"/apps/" + props.slug}>{props.name || title}</Link>
</ListItemTitle>
<ListItemText component="p">{props.description}</ListItemText>
</div>
<div>{props.actions}</div>
</div>
{props.children && <div className="w-full border-t border-gray-200">{props.children}</div>}
</ListItem>
);
}
export default IntegrationListItem;
@@ -0,0 +1,51 @@
import classNames from "classnames";
import React from "react";
import { SkeletonText } from "@calcom/ui/v2";
function SkeletonLoader() {
return (
<ul className="animate-pulse divide-y divide-neutral-200 rounded-md border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden">
<SkeletonItem />
<SkeletonItem />
<SkeletonItem />
</ul>
);
}
export default SkeletonLoader;
function SkeletonItem() {
return (
<li className="group flex w-full items-center justify-between px-2 py-[23px] sm:px-6">
<div className="flex-grow truncate text-sm">
<div className="flex flex-col space-y-2">
<SkeletonText className="h-4 w-32" />
<SkeletonText className="h-2 w-32" />
</div>
</div>
<div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 lg:flex">
<div className="flex justify-between space-x-2 rtl:space-x-reverse">
<SkeletonText className="h-6 w-12" />
</div>
</div>
</li>
);
}
export const SelectSkeletonLoader = ({ className }: { className?: string }) => {
return (
<li
className={classNames(
"group flex w-full items-center justify-between rounded-sm border border-gray-200 px-[10px] py-3",
className
)}>
<div className="flex-grow truncate text-sm">
<div className="flex justify-between">
<SkeletonText className="h-4 w-32" />
<SkeletonText className="h-4 w-4" />
</div>
</div>
</li>
);
};
@@ -0,0 +1,36 @@
import React from "react";
import { SkeletonText } from "@calcom/ui/v2";
function SkeletonLoader() {
return (
<ul className="animate-pulse divide-y divide-neutral-200 rounded-md border border-gray-200 bg-white sm:overflow-hidden">
<SkeletonItem />
<SkeletonItem />
<SkeletonItem />
</ul>
);
}
export default SkeletonLoader;
function SkeletonItem() {
return (
<li className="group flex w-full items-center justify-between px-4 py-4 sm:px-6">
<div className="flex-grow truncate text-sm">
<div className="flex">
<div className="flex flex-col space-y-2">
<SkeletonText className="h-5 w-16" />
<SkeletonText className="h-4 w-32" />
</div>
</div>
</div>
<div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 lg:flex">
<div className="flex justify-between space-x-2 rtl:space-x-reverse">
<SkeletonText className="h-6 w-16" />
<SkeletonText className="h-6 w-32" />
</div>
</div>
</li>
);
}
@@ -0,0 +1,144 @@
import { FormValues } from "pages/v2/event-types/[type]";
import { Controller, useFormContext } from "react-hook-form";
import dayjs from "@calcom/dayjs";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { weekdayNames } from "@calcom/lib/weekday";
import { trpc } from "@calcom/trpc/react";
import { Icon } from "@calcom/ui";
import Button from "@calcom/ui/v2/core/Button";
import Select from "@calcom/ui/v2/core/form/select";
import { SkeletonText } from "@calcom/ui/v2/core/skeleton";
import { SelectSkeletonLoader } from "@components/v2/availability/SkeletonLoader";
type AvailabilityOption = {
label: string;
value: number;
};
const AvailabilitySelect = ({
className = "",
...props
}: {
className?: string;
name: string;
value: number;
onBlur: () => void;
onChange: (value: AvailabilityOption | null) => void;
}) => {
const { data, isLoading } = trpc.useQuery(["viewer.availability.list"]);
if (isLoading) {
return <SelectSkeletonLoader />;
}
const schedules = data?.schedules || [];
const options = schedules.map((schedule) => ({
value: schedule.id,
label: schedule.name,
}));
const value = options.find((option) =>
props.value
? option.value === props.value
: option.value === schedules.find((schedule) => schedule.isDefault)?.id
);
return (
<Select
options={options}
isSearchable={false}
onChange={props.onChange}
className={classNames("block w-full min-w-0 flex-1 rounded-sm text-sm", className)}
value={value}
/>
);
};
const format = (date: Date) =>
Intl.DateTimeFormat(undefined, { hour: "numeric", minute: "numeric" }).format(
new Date(dayjs.utc(date).format("YYYY-MM-DDTHH:mm:ss"))
);
export const AvailabilityTab = () => {
const { t, i18n } = useLocale();
const { watch } = useFormContext<FormValues>();
const scheduleId = watch("schedule");
const { isLoading, data: schedule } = trpc.useQuery(["viewer.availability.schedule", { scheduleId }]);
const filterDays = (dayNum: number) =>
schedule?.schedule.availability.filter((item) => item.days.includes((dayNum + 1) % 7)) || [];
return (
<>
<div>
<div className="min-w-4 mb-2">
<label htmlFor="availability" className="mt-0 flex text-sm font-medium text-neutral-700">
{t("availability")}
</label>
</div>
<Controller
name="schedule"
render={({ field }) => (
<AvailabilitySelect
value={field.value}
onBlur={field.onBlur}
name={field.name}
onChange={(selected) => {
field.onChange(selected?.value || null);
}}
/>
)}
/>
</div>
<div className="space-y-4 rounded border p-8 py-6 pt-2">
<ol className="table border-collapse text-sm">
{weekdayNames(i18n.language, 1, "long").map((day, index) => {
const isAvailable = !!filterDays(index).length;
return (
<li key={day} className="my-6 flex border-transparent last:mb-2">
<span className={classNames("w-32 font-medium", !isAvailable && "text-gray-500 opacity-50")}>
{day}
</span>
{isLoading ? (
<SkeletonText className="block h-5 w-60" />
) : isAvailable ? (
<div className="space-y-3">
{filterDays(index).map((dayRange, i) => (
<div key={i} className="flex items-center leading-4">
<span className="w-28">{format(dayRange.startTime)}</span>
<span className="">-</span>
<div className="ml-6">{format(dayRange.endTime)}</div>
</div>
))}
</div>
) : (
<span className="text-gray-500 opacity-50 ">{t("unavailable")}</span>
)}
</li>
);
})}
</ol>
<hr />
<div className="flex justify-between">
<span className="flex items-center text-sm text-gray-600">
<Icon.FiGlobe className="mr-2" />
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
</span>
<Button
href={`/availability/${scheduleId}`}
color="minimal"
EndIcon={Icon.FiExternalLink}
target="_blank"
rel="noopener noreferrer">
{t("edit_availability")}
</Button>
</div>
</div>
</>
);
};
@@ -225,6 +225,7 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
)}
<Form
form={locationFormMethods}
className="space-y-4"
handleSubmit={async (values) => {
const { locationType: newLocation, displayLocationPublicly } = values;
@@ -284,7 +285,6 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
: locationOptions
}
isSearchable={false}
className="my-4 block w-full min-w-0 flex-1 rounded-md border border-gray-300 text-sm"
onChange={(val) => {
if (val) {
locationFormMethods.setValue("locationType", val.value);
@@ -19,6 +19,7 @@ import {
DialogContent,
Label,
showToast,
Skeleton,
Switch,
TextField,
Tooltip,
@@ -37,10 +38,11 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
const connectedCalendarsQuery = trpc.useQuery(["viewer.connectedCalendars"]);
const formMethods = useFormContext<FormValues>();
const { t } = useLocale();
const utils = trpc.useContext();
const [showEventNameTip, setShowEventNameTip] = useState(false);
const [hashedLinkVisible, setHashedLinkVisible] = useState(!!eventType.hashedLink);
const [redirectUrlVisible, setRedirectUrlVisible] = useState(!!eventType.successRedirectUrl);
const [hashedUrl, setHashedUrl] = useState(eventType.hashedLink?.link);
const [seatsInputVisible, setSeatsInputVisible] = useState(!!eventType.seatsPerTimeSlot);
const [customInputs, setCustomInputs] = useState<EventTypeCustomInput[]>(
eventType.customInputs.sort((a, b) => a.id - b.id) || []
);
@@ -49,7 +51,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
const placeholderHashedLink = `${CAL_URL}/d/${hashedUrl}/${eventType.slug}`;
const animationRef = useRef(null);
const seatsEnabled = !!eventType.seatsPerTimeSlot;
const seatsEnabled = formMethods.getValues("seatsPerTimeSlotEnabled");
useEffect(() => {
animationRef.current && autoAnimate(animationRef.current);
@@ -82,7 +84,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
</a>
</Link>
</div>
<div className="w-full">
<div className="-mt-1 w-full">
<Controller
control={formMethods.control}
name="destinationCalendar"
@@ -123,6 +125,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
<div className="flex space-x-3 ">
<Switch
checked={customInputs.length > 0}
fitToHeight={true}
onCheckedChange={(e) => {
if (e && customInputs.length === 0) {
// Push a placeholders
@@ -135,8 +138,12 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
}}
/>
<div className="flex flex-col">
<Label className="text-sm font-semibold leading-none text-black">{t("additional_inputs")}</Label>
<p className="-mt-2 text-sm leading-normal text-gray-600">{t("additional_input_description")}</p>
<Skeleton as={Label} className="text-sm font-semibold leading-none text-black">
{t("additional_inputs")}
</Skeleton>
<Skeleton as="p" className="-mt-2 text-sm leading-normal text-gray-600">
{t("additional_input_description")}
</Skeleton>
</div>
</div>
<ul className="my-4" ref={animationRef}>
@@ -172,20 +179,21 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
name="requiresConfirmation"
defaultValue={eventType.requiresConfirmation}
render={({ field: { value, onChange } }) => (
<div className="flex space-x-3 ">
<div className="flex space-x-3">
<Switch
name="requireConfirmation"
checked={value}
onCheckedChange={(e) => onChange(e)}
disabled={seatsEnabled}
fitToHeight={true}
/>
<div className="flex flex-col">
<Label className="text-sm font-semibold leading-none text-black">
<Skeleton as={Label} className="text-sm font-semibold leading-none text-black">
{t("requires_confirmation")}
</Label>
<p className="-mt-2 text-sm leading-normal text-gray-600">
</Skeleton>
<Skeleton as="p" className="-mt-2 text-sm leading-normal text-gray-600">
{t("requires_confirmation_description")}
</p>
</Skeleton>
</div>
</div>
)}
@@ -199,13 +207,18 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
<div className="flex space-x-3 ">
<Switch
name="disableGuests"
fitToHeight={true}
checked={value}
onCheckedChange={(e) => onChange(e)}
disabled={seatsEnabled}
/>
<div className="flex flex-col">
<Label className="text-sm font-semibold leading-none text-black">{t("disable_guests")}</Label>
<p className="-mt-2 text-sm leading-normal text-gray-600">{t("disable_guests_description")}</p>
<Skeleton as={Label} className="text-sm font-semibold leading-none text-black">
{t("disable_guests")}
</Skeleton>
<Skeleton as="p" className="-mt-2 text-sm leading-normal text-gray-600">
{t("disable_guests_description")}
</Skeleton>
</div>
</div>
)}
@@ -218,15 +231,66 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
defaultValue={eventType.hideCalendarNotes}
render={({ field: { value, onChange } }) => (
<div className="flex space-x-3 ">
<Switch name="hideCalendarNotes" checked={value} onCheckedChange={(e) => onChange(e)} />
<Switch
name="hideCalendarNotes"
fitToHeight={true}
checked={value}
onCheckedChange={(e) => onChange(e)}
/>
<div className="flex flex-col">
<Label className="text-sm font-semibold leading-none text-black">{t("disable_notes")}</Label>
<p className="-mt-2 text-sm leading-normal text-gray-600">{t("disable_notes_description")}</p>
<Skeleton as={Label} className="text-sm font-semibold leading-none text-black">
{t("disable_notes")}
</Skeleton>
<Skeleton as="p" className="-mt-2 text-sm leading-normal text-gray-600">
{t("disable_notes_description")}
</Skeleton>
</div>
</div>
)}
/>
<hr />
<Controller
name="successRedirectUrl"
control={formMethods.control}
defaultValue={hashedUrl}
render={({ field: { value, onChange } }) => (
<>
<div className="flex space-x-3 ">
<Switch
name="successRedirectUrlCheck"
fitToHeight={true}
defaultChecked={redirectUrlVisible}
onCheckedChange={(e) => {
setRedirectUrlVisible(e);
onChange(e ? value : "");
}}
/>
<div className="flex flex-col">
<Skeleton as={Label} className="text-sm font-semibold leading-none text-black">
{t("redirect_success_booking")}
</Skeleton>
<Skeleton as="p" className="-mt-2 text-sm leading-normal text-gray-600">
{t("redirect_url_description")}
</Skeleton>
</div>
</div>
{redirectUrlVisible && (
<div className="">
<TextField
label={t("redirect_success_booking")}
placeholder={t("external_redirect_url")}
required={redirectUrlVisible}
type="text"
defaultValue={eventType.successRedirectUrl || ""}
{...formMethods.register("successRedirectUrl")}
/>
</div>
)}
</>
)}
/>
<hr />
<Controller
name="hashedLink"
control={formMethods.control}
@@ -235,7 +299,9 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
<>
<div className="flex space-x-3 ">
<Switch
data-testid="hashedLinkCheck"
name="hashedLinkCheck"
fitToHeight={true}
defaultChecked={!!value}
onCheckedChange={(e) => {
setHashedLinkVisible(e);
@@ -243,8 +309,12 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
}}
/>
<div className="flex flex-col">
<Label className="text-sm font-semibold leading-none text-black">{t("private_link")}</Label>
<p className="-mt-2 text-sm leading-normal text-gray-600">{t("private_link_description")}</p>
<Skeleton as={Label} className="text-sm font-semibold leading-none text-black">
{t("private_link")}
</Skeleton>
<Skeleton as="p" className="-mt-2 text-sm leading-normal text-gray-600">
{t("private_link_description")}
</Skeleton>
</div>
</div>
@@ -283,6 +353,59 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
</>
)}
/>
<hr />
<Controller
name="seatsPerTimeSlotEnabled"
control={formMethods.control}
defaultValue={!!eventType.seatsPerTimeSlot}
render={({ field: { value, onChange } }) => (
<div className="flex space-x-3">
<Switch
name="seatsPerTimeSlotEnabled"
checked={value}
onCheckedChange={(e) => {
setSeatsInputVisible(e);
// Enabling seats will disable guests and requiring confimation until fully supported
if (e) {
formMethods.setValue("disableGuests", true);
formMethods.setValue("requiresConfirmation", false);
formMethods.setValue("seatsPerTimeSlot", 2);
} else {
formMethods.setValue("seatsPerTimeSlot", null);
}
onChange(e);
}}
fitToHeight={true}
/>
<div className="flex flex-col">
<Label className="text-sm font-semibold leading-none text-black">{t("offer_seats")}</Label>
<p className="-mt-2 text-sm leading-normal text-gray-600">{t("offer_seats_description")}</p>
</div>
</div>
)}
/>
{seatsInputVisible && (
<Controller
name="seatsPerTimeSlot"
control={formMethods.control}
defaultValue={eventType.seatsPerTimeSlot}
render={({ field: { value, onChange } }) => (
<div className="">
<TextField
required
name="seatsPerTimeSlot"
label={t("number_of_seats")}
type="number"
defaultValue={value || 2}
addOnSuffix={<>{t("seats")}</>}
onChange={(e) => {
onChange(Number(e.target.value));
}}
/>
</div>
)}
/>
)}
{showEventNameTip && (
<Dialog open={showEventNameTip} onOpenChange={setShowEventNameTip}>
<DialogContent
@@ -5,6 +5,7 @@ import { Controller } from "react-hook-form";
import { FormattedNumber, IntlProvider } from "react-intl";
import { SelectGifInput } from "@calcom/app-store/giphy/components";
import RainbowInstallForm from "@calcom/app-store/rainbow/components/RainbowInstallForm";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Icon } from "@calcom/ui";
import { Alert, Button, EmptyScreen, Select, Switch, TextField } from "@calcom/ui/v2";
@@ -32,11 +33,10 @@ const AppCard = ({
<span className="font-semibold leading-none text-black">{name}</span>
<p className="pt-2 text-sm font-normal text-gray-600">{description}</p>
</div>
<div className="ml-auto">
<div className="ml-auto flex items-center">
<Switch onCheckedChange={switchOnClick} checked={switchChecked} />
</div>
</div>
<hr className="my-6" />
{children}
</div>
);
@@ -47,14 +47,20 @@ export const EventAppsTab = ({
currency,
eventType,
hasGiphyIntegration,
hasRainbowIntegration,
}: Pick<
EventTypeSetupInfered,
"eventType" | "hasPaymentIntegration" | "hasGiphyIntegration" | "currency"
"eventType" | "hasPaymentIntegration" | "hasGiphyIntegration" | "hasRainbowIntegration" | "currency"
>) => {
const formMethods = useFormContext<FormValues>();
const [showGifSelection, setShowGifSelection] = useState(
hasGiphyIntegration && !!eventType.metadata["giphyThankYouPage"]
);
const [showRainbowSection, setShowRainbowSection] = useState(
hasRainbowIntegration &&
!!eventType.metadata["blockchainId"] &&
!!eventType.metadata["smartContractAddress"]
);
const [requirePayment, setRequirePayment] = useState(eventType.price > 0);
const recurringEventDefined = eventType.recurringEvent?.count !== undefined;
@@ -174,6 +180,29 @@ export const EventAppsTab = ({
)}
</AppCard>
)}
{hasRainbowIntegration && (
<AppCard
name="Rainbow"
description={t("confirmation_page_rainbow")}
logo="/api/app-store/rainbow/icon.svg"
switchOnClick={(e) => {
if (!e) {
formMethods.setValue("blockchainId", 1);
formMethods.setValue("smartContractAddress", "");
}
setShowRainbowSection(e);
}}
switchChecked={showRainbowSection}>
{showRainbowSection && (
<RainbowInstallForm
formMethods={formMethods}
blockchainId={(eventType.metadata.blockchainId as number) || 1}
smartContractAddress={(eventType.metadata.smartContractAddress as string) || ""}
/>
)}
</AppCard>
)}
</div>
);
};
@@ -6,7 +6,7 @@ import { useFormContext, Controller, useWatch } from "react-hook-form";
import { classNames } from "@calcom/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { PeriodType } from "@calcom/prisma/client";
import { Select, Switch, Label } from "@calcom/ui/v2";
import { Select, Switch, Label, Input, MinutesField } from "@calcom/ui/v2";
import DateRangePicker from "@calcom/ui/v2/core/form/date-range-picker/DateRangePicker";
export const EventLimitsTab = (props: Pick<EventTypeSetupInfered, "eventType">) => {
@@ -110,19 +110,29 @@ export const EventLimitsTab = (props: Pick<EventTypeSetupInfered, "eventType">)
}}
/>
</div>
</div>
<div className="flex flex-col space-y-4 pt-4 lg:flex-row lg:space-y-0 lg:space-x-4">
<div className="w-full">
<Label htmlFor="minimumBookingNotice">{t("minimum_booking_notice")} </Label>
<MinutesField
required
label={t("minimum_booking_notice")}
type="number"
placeholder="120"
{...formMethods.register("minimumBookingNotice", { valueAsNumber: true })}
/>
</div>
<div className="w-full">
<Label htmlFor="slotInterval">{t("slot_interval")} </Label>
<Controller
name="minimumBookingNotice"
name="slotInterval"
control={formMethods.control}
defaultValue={eventType.minimumBookingNotice || 0}
render={({ field: { onChange, value } }) => {
const minimumBookingOptions = [
render={() => {
const slotIntervalOptions = [
{
label: t("event_buffer_default"),
value: 0,
label: t("slot_interval_default"),
value: -1,
},
...[5, 10, 15, 20, 30, 45, 60, 90, 120].map((minutes) => ({
...[5, 10, 15, 20, 30, 45, 60].map((minutes) => ({
label: minutes + " " + t("minutes"),
value: minutes,
})),
@@ -131,12 +141,13 @@ export const EventLimitsTab = (props: Pick<EventTypeSetupInfered, "eventType">)
<Select
isSearchable={false}
onChange={(val) => {
if (val) onChange(val.value);
formMethods.setValue("slotInterval", val && (val.value || 0) > 0 ? val.value : null);
}}
defaultValue={
minimumBookingOptions.find((option) => option.value === value) || minimumBookingOptions[0]
slotIntervalOptions.find((option) => option.value === eventType.slotInterval) ||
slotIntervalOptions[0]
}
options={minimumBookingOptions}
options={slotIntervalOptions}
/>
);
}}
@@ -144,7 +155,7 @@ export const EventLimitsTab = (props: Pick<EventTypeSetupInfered, "eventType">)
</div>
</div>
<hr className="my-8" />
<div className="">
<div className="flex flex-col space-y-4 lg:flex-row lg:space-y-0 lg:space-x-4">
<fieldset className="block flex-col sm:flex">
<div className="flex space-x-3">
<Controller
@@ -153,6 +164,7 @@ export const EventLimitsTab = (props: Pick<EventTypeSetupInfered, "eventType">)
defaultValue={periodType?.type !== "UNLIMITED" ? "ROLLING" : "UNLIMITED"}
render={({ field: { value } }) => (
<Switch
fitToHeight={true}
checked={value !== "UNLIMITED"}
onCheckedChange={(bool) =>
formMethods.setValue("periodType", bool ? "ROLLING" : "UNLIMITED")
@@ -195,8 +207,8 @@ export const EventLimitsTab = (props: Pick<EventTypeSetupInfered, "eventType">)
</RadioGroup.Item>
{period.prefix ? <span>{period.prefix}&nbsp;</span> : null}
{period.type === "ROLLING" && (
<div className="flex ">
<input
<div className="flex h-9">
<Input
type="number"
className="block w-16 rounded-md border-gray-300 py-3 text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2"
placeholder="30"
@@ -205,7 +217,7 @@ export const EventLimitsTab = (props: Pick<EventTypeSetupInfered, "eventType">)
/>
<select
id=""
className="block w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-sm focus:outline-none"
className="block h-9 w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-sm focus:outline-none"
{...formMethods.register("periodCountCalendarDays")}
defaultValue={eventType.periodCountCalendarDays ? "1" : "0"}>
<option value="1">{t("calendar_days")}</option>
@@ -10,7 +10,7 @@ import { getEventLocationType, EventLocationType } from "@calcom/app-store/locat
import { CAL_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Icon } from "@calcom/ui/Icon";
import { Select, Label, TextField, Button } from "@calcom/ui/v2";
import { Select, Label, TextField, Button, Skeleton } from "@calcom/ui/v2";
import { slugify } from "@lib/slugify";
@@ -191,7 +191,6 @@ export const EventSetupTab = (
{...formMethods.register("title")}
/>
<TextField
required
label={t("description")}
placeholder={t("quick_video_meeting")}
defaultValue={eventType.description ?? ""}
@@ -223,7 +222,9 @@ export const EventSetupTab = (
}}
/>
<div>
<Label>{t("location")}</Label>
<Skeleton as={Label} loadingClassName="w-16">
{t("location")}
</Skeleton>
<Controller
name="locations"
control={formMethods.control}
@@ -1,6 +1,7 @@
import { TFunction } from "next-i18next";
import { useRouter } from "next/router";
import { EventTypeSetupInfered, FormValues } from "pages/v2/event-types/[type]";
import { useEffect, useMemo, useState } from "react";
import { useMemo, useState } from "react";
import { Loader } from "react-feather";
import { UseFormReturn } from "react-hook-form";
@@ -9,19 +10,18 @@ import { CAL_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { HttpError } from "@calcom/lib/http-error";
import { trpc, TRPCClientError } from "@calcom/trpc/react";
import ConfirmationDialogContent from "@calcom/ui/ConfirmationDialogContent";
import { Icon } from "@calcom/ui/Icon";
import {
Button,
ButtonGroup,
Tooltip,
showToast,
VerticalTabItemProps,
VerticalTabs,
HorizontalTabs,
Switch,
Label,
showToast,
Switch,
Tooltip,
VerticalTabs,
} from "@calcom/ui/v2";
import ConfirmationDialogContent from "@calcom/ui/v2/core/ConfirmationDialogContent";
import { Dialog } from "@calcom/ui/v2/core/Dialog";
import Dropdown, {
DropdownMenuContent,
@@ -30,8 +30,10 @@ import Dropdown, {
} from "@calcom/ui/v2/core/Dropdown";
import Shell from "@calcom/ui/v2/core/Shell";
import VerticalDivider from "@calcom/ui/v2/core/VerticalDivider";
import { Skeleton } from "@calcom/ui/v2/core/skeleton";
import { ClientSuspense } from "@components/ClientSuspense";
import { EmbedButton, EmbedDialog } from "@components/Embed";
type Props = {
children: React.ReactNode;
@@ -44,6 +46,59 @@ type Props = {
formMethods: UseFormReturn<FormValues>;
};
function getNavigation(props: {
t: TFunction;
eventType: Props["eventType"];
enabledAppsNumber: number;
enabledWorkflowsNumber: number;
}) {
const { eventType, t, enabledAppsNumber, enabledWorkflowsNumber } = props;
return [
{
name: "event_setup_tab_title",
href: `/event-types/${eventType.id}?tabName=setup`,
icon: Icon.FiLink,
info: `${eventType.length} Mins`, // TODO: Get this from props
},
{
name: "availability",
href: `/event-types/${eventType.id}?tabName=availability`,
icon: Icon.FiCalendar,
info: `Working Hours`, // TODO: Get this from props
},
{
name: "event_limit_tab_title",
href: `/event-types/${eventType.id}?tabName=limits`,
icon: Icon.FiClock,
info: `event_limit_tab_description`,
},
{
name: "event_advanced_tab_title",
href: `/event-types/${eventType.id}?tabName=advanced`,
icon: Icon.FiSliders,
info: `event_advanced_tab_description`,
},
{
name: "recurring",
href: `/event-types/${eventType.id}?tabName=recurring`,
icon: Icon.FiRepeat,
info: `recurring_event_tab_description`,
},
{
name: "apps",
href: `/event-types/${eventType.id}?tabName=apps`,
icon: Icon.FiGrid,
info: `${enabledAppsNumber} ${t("active")}`,
},
{
name: "workflows",
href: `/event-types/${eventType.id}?tabName=workflows`,
icon: Icon.FiZap,
info: `${enabledWorkflowsNumber} ${t("active")}`,
},
];
}
function EventTypeSingleLayout({
children,
eventType,
@@ -55,8 +110,8 @@ function EventTypeSingleLayout({
formMethods,
}: Props) {
const utils = trpc.useContext();
const router = useRouter();
const { t } = useLocale();
const router = useRouter();
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const hasPermsToDelete = currentUserMembership?.role !== "MEMBER" || !currentUserMembership;
@@ -65,6 +120,7 @@ function EventTypeSingleLayout({
onSuccess: async () => {
await utils.invalidateQueries(["viewer.eventTypes"]);
showToast(t("event_type_deleted_successfully"), "success");
await router.push("/event-types");
setDeleteDialogOpen(false);
},
onError: (err) => {
@@ -80,89 +136,39 @@ function EventTypeSingleLayout({
// Define tab navigation here
const EventTypeTabs = useMemo(() => {
const navigation = [
{
name: "event_setup_tab_title",
tabName: "setup",
icon: Icon.FiLink,
info: `${eventType.length} Mins`, // TODO: Get this from props
},
{
name: "availability",
tabName: "availability",
icon: Icon.FiCalendar,
info: `Working Hours`, // TODO: Get this from props
},
{
name: "event_limit_tab_title",
tabName: "limits",
icon: Icon.FiClock,
info: `event_limit_tab_description`,
},
{
name: "event_advanced_tab_title",
tabName: "advanced",
icon: Icon.FiSliders,
info: `event_advanced_tab_description`,
},
{
name: "recurring",
tabName: "recurring",
icon: Icon.FiRepeat,
info: `recurring_event_tab_description`,
},
{
name: "apps",
tabName: "apps",
icon: Icon.FiGrid,
info: `${enabledAppsNumber} ${t("active")}`,
},
{
name: "workflows",
tabName: "workflows",
icon: Icon.FiZap,
info: `${enabledWorkflowsNumber} ${t("active")}`,
},
] as VerticalTabItemProps[];
const navigation = getNavigation({ t, eventType, enabledAppsNumber, enabledWorkflowsNumber });
// If there is a team put this navigation item within the tabs
if (team)
navigation.splice(2, 0, {
name: "scheduling_type",
tabName: "team",
href: `/event-types/${eventType.id}?tabName=team`,
icon: Icon.FiUsers,
info: eventType.schedulingType === "COLLECTIVE" ? "collective" : "round_robin",
});
return navigation;
}, [eventType, enabledAppsNumber, team]);
useEffect(() => {
// Default to the first in the list
if (!router.query.tabName) {
router.push({
query: {
...router.query,
tabName: EventTypeTabs[0].tabName,
},
});
}
}, [EventTypeTabs, router]);
}, [t, eventType, enabledAppsNumber, enabledWorkflowsNumber, team]);
const permalink = `${CAL_URL}/${team ? `team/${team.slug}` : eventType.users[0].username}/${
eventType.slug
}`;
const embedLink = `${team ? `team/${team.slug}` : eventType.users[0].username}/${eventType.slug}`;
return (
<Shell
title={t("event_type_title", { eventTypeTitle: eventType.title })}
backPath="/event-types"
title={eventType.title + " | " + t("event_type")}
heading={eventType.title}
subtitle={eventType.description || ""}
CTA={
<div className="flex items-center justify-end">
<div className="flex items-center rounded-md px-2 sm:hover:bg-gray-100">
<Label htmlFor="hiddenSwitch" className="mt-2 hidden cursor-pointer self-center pr-2 sm:inline">
<Skeleton
as={Label}
htmlFor="hiddenSwitch"
className="mt-2 hidden cursor-pointer self-center pr-2 sm:inline">
{t("hide_from_profile")}
</Label>
</Skeleton>
<Switch
id="hiddenSwitch"
defaultChecked={formMethods.getValues("hidden")}
@@ -184,7 +190,6 @@ function EventTypeSingleLayout({
href={permalink}
rel="noreferrer"
StartIcon={Icon.FiExternalLink}
combined
/>
</Tooltip>
@@ -199,13 +204,18 @@ function EventTypeSingleLayout({
showToast("Link copied!", "success");
}}
/>
{/* TODO: Implement embed here @hariom */}
{/* <Button color="secondary" size="icon" StartIcon={Icon.FiCode} combined /> */}
<EmbedButton
embedUrl={encodeURIComponent(embedLink)}
StartIcon={Icon.FiCode}
color="secondary"
size="icon"
tooltip={t("embed")}
/>
<Button
color="secondary"
size="icon"
StartIcon={Icon.FiTrash}
combined
tooltip={t("delete")}
disabled={!hasPermsToDelete}
onClick={() => setDeleteDialogOpen(true)}
/>
@@ -218,45 +228,56 @@ function EventTypeSingleLayout({
<Icon.FiMoreHorizontal className="group-hover:text-gray-800" />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<DropdownMenuItem className="focus:ring-gray-100">
<Button
color="minimal"
StartIcon={Icon.FiExternalLink}
target="_blank"
href={permalink}
rel="noreferrer">
rel="noreferrer"
className="min-w-full">
{t("preview")}
</Button>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownMenuItem className="focus:ring-gray-100">
<Button color="minimal" StartIcon={Icon.FiLink}>
{t("copy_link")}
</Button>
</DropdownMenuItem>
<DropdownMenuItem>
<DropdownMenuItem className="focus:ring-gray-100">
<Button
color="minimal"
StartIcon={Icon.FiTrash}
disabled={!hasPermsToDelete}
onClick={() => setDeleteDialogOpen(true)}>
onClick={() => setDeleteDialogOpen(true)}
className="min-w-full">
{t("delete")}
</Button>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
<div className="border-l-2 border-gray-300" />
<Button className="ml-4 lg:ml-0" type="submit" form="event-type-form">
<Button
className="ml-4 lg:ml-0"
type="submit"
data-testid="update-eventtype"
form="event-type-form">
{t("save")}
</Button>
</div>
}>
<ClientSuspense fallback={<Loader />}>
<div className="mt-4 flex flex-col xl:flex-row xl:space-x-8">
<div className="-mt-2 flex flex-col xl:flex-row xl:space-x-8">
<div className="hidden xl:block">
<VerticalTabs tabs={EventTypeTabs} sticky />
<VerticalTabs
className="primary-navigation"
tabs={EventTypeTabs}
sticky
linkProps={{ shallow: true }}
/>
</div>
<div className="relative left-0 right-0 -mx-6 w-[calc(100%+40px)] p-2 md:mx-0 md:p-0 xl:hidden">
<HorizontalTabs tabs={EventTypeTabs} />
<div className="p-2 md:mx-0 md:p-0 xl:hidden">
<HorizontalTabs tabs={EventTypeTabs} linkProps={{ shallow: true }} />
</div>
<div className="w-full ltr:mr-2 rtl:ml-2">
<div
@@ -280,9 +301,10 @@ function EventTypeSingleLayout({
e.preventDefault();
deleteMutation.mutate({ id: eventType.id });
}}>
{t("delete_event_type_description") as string}
{t("delete_event_type_description")}
</ConfirmationDialogContent>
</Dialog>
<EmbedDialog />
</Shell>
);
}
@@ -41,6 +41,8 @@ export default function RecurringEventController({
<div className="flex space-x-3 ">
<Switch
name="requireConfirmation"
data-testid="recurring-event-check"
fitToHeight={true}
checked={recurringEventState !== null}
onCheckedChange={(e) => {
if (!e) {
@@ -72,7 +74,7 @@ export default function RecurringEventController({
type="number"
min="1"
max="20"
className="block w-16 rounded-md border-gray-300 text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2"
className="block h-[36px] w-16 rounded-md border-gray-300 text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2"
defaultValue={recurringEventState.interval}
onChange={(event) => {
const newVal = {
@@ -87,7 +89,7 @@ export default function RecurringEventController({
options={recurringEventFreqOptions}
value={recurringEventFreqOptions[recurringEventState.freq]}
isSearchable={false}
className="w-18 block min-w-0 rounded-md text-sm"
className="w-18 block h-[36px] min-w-0 rounded-md text-sm"
onChange={(event) => {
const newVal = {
...recurringEventState,
@@ -99,12 +101,12 @@ export default function RecurringEventController({
/>
</div>
<div className="mt-4 flex items-center">
<p className="mr-2 text-neutral-900">{t("max")}</p>
<p className="mr-2 text-neutral-900">{t("for_a_maximum_of")}</p>
<input
type="number"
min="1"
max="20"
className="block w-16 rounded-md border-gray-300 text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2"
className="block h-[36px] w-16 rounded-md border-gray-300 text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2"
defaultValue={recurringEventState.count}
onChange={(event) => {
const newVal = {
@@ -116,7 +118,7 @@ export default function RecurringEventController({
}}
/>
<p className="mr-2 text-neutral-900">
{t("occurrence", {
{t("events", {
count: recurringEventState.count,
})}
</p>
@@ -0,0 +1,47 @@
import { Icon } from "@calcom/ui/Icon";
import { SkeletonAvatar, SkeletonContainer, SkeletonText } from "@calcom/ui/v2";
function SkeletonLoader() {
return (
<SkeletonContainer>
<div className="mb-4 flex items-center">
<SkeletonAvatar className="h-8 w-8" />
<div className="flex flex-col space-y-1">
<SkeletonText className="h-4 w-16" />
<SkeletonText className="h-4 w-24" />
</div>
</div>
<ul className="divide-y divide-neutral-200 rounded-md border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden">
<SkeletonItem />
<SkeletonItem />
<SkeletonItem />
</ul>
</SkeletonContainer>
);
}
export default SkeletonLoader;
function SkeletonItem() {
return (
<li className="group flex w-full items-center justify-between px-4 py-4 sm:px-6">
<div className="flex-grow truncate text-sm">
<div>
<SkeletonText className="h-5 w-32" />
</div>
<div className="">
<ul className="mt-2 flex space-x-4 rtl:space-x-reverse ">
<li className="flex items-center whitespace-nowrap">
<Icon.FiClock className="mt-0.5 mr-1.5 inline h-4 w-4 text-gray-200" />
<SkeletonText className="h-4 w-12" />
</li>
<li className="flex items-center whitespace-nowrap">
<Icon.FiUser className="mt-0.5 mr-1.5 inline h-4 w-4 text-gray-200" />
<SkeletonText className="h-4 w-16" />
</li>
</ul>
</div>
</div>
</li>
);
}
@@ -1,4 +1,4 @@
import { useMutation } from "react-query";
import { useMutation } from "@tanstack/react-query";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
@@ -12,6 +12,7 @@ export function CalendarSwitch(props: {
externalId: string;
title: string;
defaultSelected: boolean;
isSelected: boolean;
}) {
const { t } = useLocale();
@@ -69,14 +70,14 @@ export function CalendarSwitch(props: {
key={props.externalId}
name="enabled"
label={props.title}
defaultChecked={props.defaultSelected}
defaultChecked={props.isSelected}
onCheckedChange={(isOn: boolean) => {
mutation.mutate({ isOn });
}}
/>
{props.defaultSelected && (
<Badge variant="gray">
<Icon.FiArrowLeft /> {t("adding_events_to")}
<Icon.FiArrowLeft className="mr-1" /> {t("adding_events_to")}
</Badge>
)}
</div>
@@ -1,10 +1,14 @@
import { SyntheticEvent, useState } from "react";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { ErrorCode } from "@calcom/lib/auth";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import Button from "@calcom/ui/v2/core/Button";
import { Dialog, DialogContent } from "@calcom/ui/v2/core/Dialog";
import { Form, Label } from "@calcom/ui/v2/core/form/fields";
import { PasswordField } from "@calcom/ui/v2/core/form/fields";
import { ErrorCode } from "@lib/auth";
import TwoFactor from "@components/auth/TwoFactor";
import TwoFactorAuthAPI from "./TwoFactorAuthAPI";
@@ -18,20 +22,24 @@ interface DisableTwoFactorAuthModalProps {
onDisable: () => void;
}
interface DisableTwoFactorValues {
totpCode: string;
password: string;
}
const DisableTwoFactorAuthModal = ({
onDisable,
onCancel,
open,
onOpenChange,
}: DisableTwoFactorAuthModalProps) => {
const [password, setPassword] = useState("");
const [isDisabling, setIsDisabling] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const { t } = useLocale();
async function handleDisable(e: SyntheticEvent) {
e.preventDefault();
const form = useForm<DisableTwoFactorValues>();
async function handleDisable({ totpCode, password }: DisableTwoFactorValues) {
if (isDisabling) {
return;
}
@@ -39,7 +47,7 @@ const DisableTwoFactorAuthModal = ({
setErrorMessage(null);
try {
const response = await TwoFactorAuthAPI.disable(password);
const response = await TwoFactorAuthAPI.disable(password, totpCode);
if (response.status === 200) {
onDisable();
return;
@@ -48,6 +56,12 @@ const DisableTwoFactorAuthModal = ({
const body = await response.json();
if (body.error === ErrorCode.IncorrectPassword) {
setErrorMessage(t("incorrect_password"));
}
if (body.error === ErrorCode.SecondFactorRequired) {
setErrorMessage(t("2fa_required"));
}
if (body.error === ErrorCode.IncorrectTwoFactorCode) {
setErrorMessage(t("incorrect_2fa"));
} else {
setErrorMessage(t("something_went_wrong"));
}
@@ -66,39 +80,31 @@ const DisableTwoFactorAuthModal = ({
description={t("disable_2fa_recommendation")}
type="creation"
useOwnActionButtons>
<form onSubmit={handleDisable}>
<Form form={form} handleSubmit={handleDisable}>
<div className="mb-4">
<label htmlFor="password" className="mt-4 block text-sm font-medium text-gray-700">
{t("password")}
</label>
<div className="mt-1">
<input
type="password"
name="password"
id="password"
required
value={password}
onInput={(e) => setPassword(e.currentTarget.value)}
className="block w-full rounded-sm border-gray-300 text-sm"
/>
</div>
<PasswordField
labelProps={{
className: "block text-sm font-medium text-gray-700",
}}
{...form.register("password")}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-black focus:outline-none focus:ring-black"
/>
<Label className="mt-4"> {t("2fa_code")}</Label>
<TwoFactor center={false} />
{errorMessage && <p className="mt-1 text-sm text-red-700">{errorMessage}</p>}
</div>
</form>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<Button
type="submit"
className="ltr:ml-2 rtl:mr-2"
onClick={handleDisable}
disabled={password.length === 0 || isDisabling}>
{t("disable")}
</Button>
<Button color="secondary" onClick={onCancel}>
{t("cancel")}
</Button>
</div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<Button type="submit" className="ltr:ml-2 rtl:mr-2" disabled={isDisabling}>
{t("disable")}
</Button>
<Button color="secondary" onClick={onCancel}>
{t("cancel")}
</Button>
</div>
</Form>
</DialogContent>
</Dialog>
);
@@ -1,10 +1,13 @@
import React, { SyntheticEvent, useState } from "react";
import React, { BaseSyntheticEvent, useState } from "react";
import { useForm } from "react-hook-form";
import { ErrorCode } from "@calcom/lib/auth";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import Button from "@calcom/ui/v2/core/Button";
import { Dialog, DialogContent } from "@calcom/ui/v2/core/Dialog";
import { Form, TextField } from "@calcom/ui/v2/core/form/fields";
import { ErrorCode } from "@lib/auth";
import TwoFactor from "@components/auth/TwoFactor";
import TwoFactorAuthAPI from "./TwoFactorAuthAPI";
@@ -41,8 +44,14 @@ const WithStep = ({
return step === current ? children : null;
};
interface EnableTwoFactorValues {
totpCode: string;
}
const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: EnableTwoFactorModalProps) => {
const { t } = useLocale();
const form = useForm<EnableTwoFactorValues>();
const setupDescriptions = {
[SetupStep.ConfirmPassword]: t("2fa_confirm_current_password"),
[SetupStep.DisplayQrCode]: t("2fa_scan_image_or_use_code"),
@@ -50,13 +59,12 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
};
const [step, setStep] = useState(SetupStep.ConfirmPassword);
const [password, setPassword] = useState("");
const [totpCode, setTotpCode] = useState("");
const [dataUri, setDataUri] = useState("");
const [secret, setSecret] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
async function handleSetup(e: SyntheticEvent) {
async function handleSetup(e: React.FormEvent) {
e.preventDefault();
if (isSubmitting) {
@@ -90,10 +98,10 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
}
}
async function handleEnable(e: SyntheticEvent) {
e.preventDefault();
async function handleEnable({ totpCode }: EnableTwoFactorValues, e: BaseSyntheticEvent | undefined) {
e?.preventDefault();
if (isSubmitting || totpCode.length !== 6) {
if (isSubmitting) {
return;
}
@@ -128,29 +136,19 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
title={t("enable_2fa")}
description={setupDescriptions[step]}
type="creation"
useOwnActionButtons
// Icon={Icon.FiAlertTriangle}>
>
{/* <TwoFactorModalHeader title={t("enable_2fa")} description={setupDescriptions[step]} /> */}
useOwnActionButtons>
<WithStep step={SetupStep.ConfirmPassword} current={step}>
<form onSubmit={handleSetup}>
<div className="mb-4">
<label htmlFor="password" className="mt-4 block text-sm font-medium text-gray-700">
{t("password")}
</label>
<div className="mt-1">
<input
type="password"
name="password"
id="password"
required
value={password}
onInput={(e) => setPassword(e.currentTarget.value)}
className="block w-full rounded-sm border-gray-300 text-sm"
/>
</div>
<TextField
label={t("password")}
type="password"
name="password"
id="password"
required
value={password}
onInput={(e) => setPassword(e.currentTarget.value)}
/>
{errorMessage && <p className="mt-1 text-sm text-red-700">{errorMessage}</p>}
</div>
</form>
@@ -166,64 +164,42 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
<p className="text-center font-mono text-xs">{secret}</p>
</>
</WithStep>
<WithStep step={SetupStep.EnterTotpCode} current={step}>
<form onSubmit={handleEnable}>
<Form handleSubmit={handleEnable} form={form}>
<WithStep step={SetupStep.EnterTotpCode} current={step}>
<div className="mb-4">
<label htmlFor="code" className="mt-4 block text-sm font-medium text-gray-700">
{t("code")}
</label>
<div className="mt-1">
<input
type="text"
name="code"
id="code"
required
value={totpCode}
maxLength={6}
minLength={6}
inputMode="numeric"
onInput={(e) => setTotpCode(e.currentTarget.value)}
className="block w-full rounded-sm border-gray-300 text-sm"
autoComplete="one-time-code"
/>
</div>
<TwoFactor center />
{errorMessage && <p className="mt-1 text-sm text-red-700">{errorMessage}</p>}
</div>
</form>
</WithStep>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<WithStep step={SetupStep.ConfirmPassword} current={step}>
<Button
type="submit"
className="ltr:ml-2 rtl:mr-2"
onClick={handleSetup}
disabled={password.length === 0 || isSubmitting}>
{t("continue")}
</Button>
</WithStep>
<WithStep step={SetupStep.DisplayQrCode} current={step}>
<Button
type="submit"
className="ltr:ml-2 rtl:mr-2"
onClick={() => setStep(SetupStep.EnterTotpCode)}>
{t("continue")}
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<WithStep step={SetupStep.ConfirmPassword} current={step}>
<Button
type="submit"
className="ltr:ml-2 rtl:mr-2"
onClick={handleSetup}
disabled={password.length === 0 || isSubmitting}>
{t("continue")}
</Button>
</WithStep>
<WithStep step={SetupStep.DisplayQrCode} current={step}>
<Button
type="submit"
className="ltr:ml-2 rtl:mr-2"
onClick={() => setStep(SetupStep.EnterTotpCode)}>
{t("continue")}
</Button>
</WithStep>
<WithStep step={SetupStep.EnterTotpCode} current={step}>
<Button type="submit" className="ltr:ml-2 rtl:mr-2" disabled={isSubmitting}>
{t("enable")}
</Button>
</WithStep>
<Button color="secondary" onClick={onCancel}>
{t("cancel")}
</Button>
</WithStep>
<WithStep step={SetupStep.EnterTotpCode} current={step}>
<Button
type="submit"
className="ltr:ml-2 rtl:mr-2"
onClick={handleEnable}
disabled={totpCode.length !== 6 || isSubmitting}>
{t("enable")}
</Button>
</WithStep>
<Button color="secondary" onClick={onCancel}>
{t("cancel")}
</Button>
</div>
</div>
</Form>
</DialogContent>
</Dialog>
);
@@ -19,10 +19,10 @@ const TwoFactorAuthAPI = {
});
},
async disable(password: string) {
async disable(password: string, code: string) {
return fetch("/api/auth/two-factor/totp/disable", {
method: "POST",
body: JSON.stringify({ password }),
body: JSON.stringify({ password, code }),
headers: {
"Content-Type": "application/json",
},
+9 -11
View File
@@ -17,27 +17,25 @@ interface Props {
export default function AuthContainer(props: React.PropsWithChildren<Props>) {
return (
<div className="flex min-h-screen flex-col justify-center bg-gray-100 py-12 sm:px-6 lg:px-8">
<div className="flex min-h-screen flex-col justify-center bg-[#f3f4f6] py-12 sm:px-6 lg:px-8">
<HeadSeo title={props.title} description={props.description} />
{props.showLogo && (
// eslint-disable-next-line @next/next/no-img-element
<img className="mb-auto h-4" src={LOGO} alt="Cal.com Logo" />
)}
<div className={classNames(props.showLogo ? "text-center" : "", "sm:mx-auto sm:w-full sm:max-w-md")}>
{props.showLogo && (
// eslint-disable-next-line @next/next/no-img-element
<img className="mx-auto h-6" src={LOGO} alt="Cal.com Logo" />
)}
{props.heading && (
<h2 className="font-cal mt-6 text-center text-3xl text-neutral-900">{props.heading}</h2>
)}
{props.heading && <h2 className="font-cal text-center text-3xl text-neutral-900">{props.heading}</h2>}
</div>
{props.loading && (
<div className="absolute z-50 flex h-screen w-full items-center bg-gray-50">
<Loader />
</div>
)}
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="border-1 mx-2 rounded-md border-gray-200 bg-white px-4 py-8 sm:px-10">
<div className="mb-auto mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="border-1 mx-2 rounded-md border-gray-200 bg-white px-4 py-10 sm:px-10">
{props.children}
</div>
<div className="mt-4 text-center text-sm text-neutral-600">{props.footerText}</div>
<div className="mt-8 text-center text-sm text-neutral-600">{props.footerText}</div>
</div>
</div>
);
@@ -2,6 +2,7 @@ import { Webhook } from "@prisma/client";
import { useEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import customTemplate, { hasTemplateIntegration } from "@calcom/features/webhooks/utils/integrationTemplate";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { trpc } from "@calcom/trpc/react";
@@ -11,11 +12,11 @@ import Switch from "@calcom/ui/Switch";
import { FieldsetLegend, Form, InputGroupBox, TextArea, TextField } from "@calcom/ui/form/fields";
import { WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP } from "@lib/webhooks/constants";
import customTemplate, { hasTemplateIntegration } from "@lib/webhooks/integrationTemplate";
import { TWebhook } from "@components/webhook/WebhookListItem";
import WebhookTestDisclosure from "@components/webhook/WebhookTestDisclosure";
/** @deprecated Moved to `packages/features/webhooks` */
export default function WebhookDialogForm(props: {
eventTypeId?: number;
defaultValues?: TWebhook;
@@ -21,6 +21,7 @@ export type WebhookListContainerType = {
appId?: string;
};
/** @deprecated Moved to `packages/features/webhooks` */
export default function WebhookListContainer(props: WebhookListContainerType) {
const router = useRouter();
const query = trpc.useQuery(
@@ -11,6 +11,7 @@ import { useLocale } from "@lib/hooks/useLocale";
export type TWebhook = inferQueryOutput<"viewer.webhook.list">[number];
/** @deprecated Moved to `packages/features/webhooks` */
export default function WebhookListItem(props: { webhook: TWebhook; onEditWebhook: () => void }) {
const { t } = useLocale();
const utils = trpc.useContext();
@@ -11,6 +11,7 @@ import { InputGroupBox } from "@calcom/ui/form/fields";
import { useLocale } from "@lib/hooks/useLocale";
/** @deprecated Moved to `packages/features/webhooks` */
export default function WebhookTestDisclosure() {
const subscriberUrl: string = useWatch({ name: "subscriberUrl" });
const payloadTemplate = useWatch({ name: "payloadTemplate" }) || null;

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