Compare commits

..
297 Commits
Author SHA1 Message Date
zomars 6415d42a1d v2.2.0
release.yml / release (push) Canceled after 0s
2022-11-16 14:22:08 -07:00
Omar Lópezandalannnc ee620c7fff Adds team upgrade banner (#5541)
* Adds team upgrade banner

* Update TeamListItem.tsx

* Update payments.ts

* Copy updates and mobile style fixes

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

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

Co-authored-by: alannnc <alannnc@gmail.com>
2022-11-16 21:07:20 +00:00
Jeroen Reumkens 112efbbda2 CAL-300 Ensure we show the default calendar name based in the advanced tab for event types. (#5551) 2022-11-16 20:03:19 +00:00
fac0b0fa32 Improving cancellation flow (#5447)
* Booking succes query refactor

The query is now using the uid as its main identifier for the success page

* Minor changes to the succes.tsx and tests

* Convert eventtype dates to string, and only select eventtype slug from db to have a smaller query (we don't need more data, and this way we don't need to convert the dates in here to smaller strings either.)

* In the payment component get the bookingUid from props instead of the query

* design fixes

* fix UI for recurring bookings

* fix success page for recurring events

* fix cancelling recurring events

* fix design of successful cancellation page

* fix email redirect + design fixes

* remove old cancel page

* fix v2 design for text area

* Changed the recurringMutation to use the uid for the success booking page

* redirect form old to new cancel page

* fix success page for recurring events

* rename to allRemainingbookings

* fix old cancel redirect

* fix recurring cancel link in email

* fix redirect when cancelling one recurring booking

* remove reschedule for recurring events in email

* create queryschema for cancel success page

* scroll down to bottom if cancel

* add cancellation reason

* fix tests

* fix old /cancel component

* code clean up

* Uses URL query as cancellation state

Also fixes string to boolean inference

Co-authored-by: Mischa Rouleaux <mischa-rouleaux@live.nl>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Jeroen Reumkens <hello@jeroenreumkens.nl>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2022-11-16 19:48:17 +00:00
Jeroen Reumkens 12d47ab949 Update UI components to be able to use them in Console (#5514)
* First steps in updating console ui components.

* Replaced remaining v1 components.

* Removed todo.

* Removed createelement from form component since we were not using it at all (we wrapped all our inputs in divs), and any input that was not wrapped, was actually broken...

* Undo update submodule.

* Reverted website submodule update.
2022-11-16 13:41:40 +00:00
4fef7ba5e2 Feat/top banner (#5443)
* wip

* wip

* chore: remove unused import

* feat: top banner component added

* feat: top banner stories added

* fix: icon bug

* Update packages/ui/components/topbanner/TopBanner.tsx

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

* Apply suggestions from code review

* Export fixes

* Feedback

* Type fixes

* Fixes

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-15 12:33:59 -07:00
6af0428a18 Booking success query refactor (#5298)
* Booking succes query refactor

The query is now using the uid as its main identifier for the success page

* Minor changes to the succes.tsx and tests

* Convert eventtype dates to string, and only select eventtype slug from db to have a smaller query (we don't need more data, and this way we don't need to convert the dates in here to smaller strings either.)

* In the payment component get the bookingUid from props instead of the query

* Changed the recurringMutation to use the uid for the success booking page

Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Jeroen Reumkens <hello@jeroenreumkens.nl>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-15 12:00:02 -07:00
Jeroen Reumkensandkodiakhq[bot] d66f3d1dc9 Event row style improvements (#5533)
* Made full row of event type clickable, and fixed event type loader dimensions.

* Fixed skeletons for teams and workflows.

* Fixed skeleton for workflow detail page.

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-15 15:30:24 +00:00
Udit Takkar a5249b6cb5 fix: use tooltip and max-w (#5531) 2022-11-15 14:24:23 +00:00
Udit Takkar 6771c11360 fix: remove type password from fields (#5530) 2022-11-15 14:08:25 +00:00
002375ac72 fix langauge in booking submitted email for attendee (#5526)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-11-15 13:55:02 +00:00
Alex van Andel 0402e46328 Reformat /booking attendees, add guests to success page, fix i18n (#5532) 2022-11-15 13:39:06 +00:00
Jeroen ReumkensandPeer Richelsen 02e3738a17 Improved event description truncation (actually removed it mostly) (#5374)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-15 08:36:20 +00:00
Alex van Andel f822177788 Use Intl.DateTimeFormat logic to determine locale. (#5519) 2022-11-14 19:46:58 +00:00
Alex van Andel 1edc9c50d5 Removed an invalidate onDelete + updated test (#5521) 2022-11-14 19:34:47 +00:00
Syed Ali Shahbaz 7beb17c007 stops content pushing on windows browsers due to overflow-y (#5513) 2022-11-14 11:07:44 +00:00
Hariom Balharaandkodiakhq[bot] ad7af451ff Fix text getting changed after browsing through Reporting tab (#5510)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-14 10:53:35 +00:00
f513ce8687 Removed unused ui components and updated skeleton components (#5412)
* Removed unused ui components and updated skeleton components throughout the app to use new version.

* Deleted unused app component and changed destructive button variants to secondary as per design.

Co-authored-by: zomars <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-14 10:31:36 +00:00
d83883e7e4 Minimum booking notice will allow hours and days (#5217)
* minimum booking notice allows hours and days

* Fixed error with Prisma not updating properly and input changing value to minutes autmatically

* fix type error

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

Co-authored-by: Peer Richelsen <peer@cal.com>

* moved minimumBookingNotice to clientside, removed migration from prisma, changed DurationField to regular InputField

* Update packages/lib/convertMinimumBookingNoticeToMinutes.ts

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

* added MINUTES_IN_HOUR, MINUTES_IN_DAY, and HOURS_IN_DAY as exportable consts

* Fix some comparison, mobile styles and const usages

* minimumBookingNotice values will never be floats

* fixed issue with minbookingnotice not updating properly in prisma

* Removed console.logs, converted minimumBookingNoticeType to ref, removed unreliable accessing of array

* remove yarn lockfile

* Revert "remove yarn lockfile"

This reverts commit fefc24c80269f5445c8eb7a640e2816d7d8b0407.

* Revert "Revert "remove yarn lockfile""

This reverts commit c8cc4d3c0eddb2c91d077d2277ad5ea10315ec95.

* Replaced currentDurationType export with state passed down from parent

* Improvements for duration notice type logic

* Undo adding submodule updates

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

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

* Update packages/lib/findDurationType.ts

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

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

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

* Update packages/lib/convertToNewDurationType.ts

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

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

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

* Update apps/web/test/lib/getSchedule.test.ts

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

* remove passing in as object

* Update apps/web/test/lib/getSchedule.test.ts

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

* Update packages/lib/convertToNewDurationType.ts

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

* Delete convertMinimumBookingNoticeToMinutes.ts

* Syntax fix

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Jeroen Reumkens <hello@jeroenreumkens.nl>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Omar López <zomars@me.com>
2022-11-14 09:12:28 +00:00
9e001c53e8 add tests (#5487)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-14 08:50:09 +00:00
Hariom BalharaandPeer Richelsen 3937b0c4c6 Add support to open a redirect URL in new tab (#5480)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-13 15:51:31 +00:00
Udit TakkarandPeer Richelsen cbe358bb6b fix: booking rejection message (#5497)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-12 21:21:01 +00:00
Leo Giovanetti 2b91102f3f Recurring emails fixed (#5499) 2022-11-12 16:46:27 +00:00
Udit Takkar 4e7804c46d fix: change max width for mobile (#5495) 2022-11-12 13:43:04 +00:00
Udit Takkar d7226fc3d4 fix: disable button and handle submit when loading (#5496) 2022-11-12 13:31:11 +00:00
Alex van Andel 0cb278d109 Update engines to >=16.x in apps/web and monorepo (#5493) 2022-11-11 21:35:38 +00:00
alannncandOmar López a1430101d1 fix/team-billing-update-publish-form (#5476)
* Display team slug on form when saved on metadata

* remove button disabled prop

* update mutation to latest trcp style

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

Co-authored-by: Omar López <zomars@me.com>
2022-11-11 20:38:21 +00:00
9092addd9c fix: use .watch() to disable update btn (#5482)
* fix: use .watch() to disable update btn

* Apply suggestions from code review

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2022-11-11 20:27:09 +00:00
Masumi Kawasakiandalannnc 7f14e00119 Correction of unclear Japanese translation (#5455)
Co-authored-by: alannnc <alannnc@gmail.com>
2022-11-11 20:05:46 +00:00
Omar Lópezandkodiakhq[bot] abc5ff2d97 Deprecated API endpoints cleanup (#5478)
* Deprecated API endpoints cleanup

* Linting

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-11 19:54:44 +00:00
Udit TakkarandPeer Richelsen fcde1f7a9d fix: ui bugs in apps (#5465)
* fix: ui bugs in apps

* fix: recurring text

* fix: logo size and font

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-11 19:35:10 +00:00
Udit TakkarandOmar López 30eea089ba fix: use outline none (#5486)
* fix: use outline none

* fix: dropdown trigger ui bugs

* Update packages/ui/Dropdown.tsx

Co-authored-by: Omar López <zomars@me.com>
2022-11-11 12:21:56 -07:00
zomars 3c8cb3a9ea Upgaded Snaplet CLI 2022-11-11 10:32:35 -07:00
54f4e665a3 Add alphanumeric sender ID to SMS workflow actions (#5471)
* add sender id

* add sender to twilio from

* added missing sender

* add migration

* fix design of add action dialog

* add cal as sender when creating new workflow

* fix type errors

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-11-11 15:01:17 +00:00
Alex van Andel ef3e7fae20 v2.1.5
release.yml / release (push) Canceled after 0s
2022-11-11 11:46:17 +00:00
Hariom Balharaandkodiakhq[bot] bb7815464c Feature/Routing-Forms/Reporting (#5375)
* Add very basic reporting logic

* Support more logical operators

* Add tests

* Fix types

* Add playwright test for reporting

* Fix table UI

* Remove only

* Better name variable

* Increase max_old_space to allow tests to run without reaciing limit

* Update test name

* Reuse getServerSideProps

* Variable renaming

* PR Suggestions

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-11 09:57:44 +00:00
Alex van AndelandOmar López acb6f292ec Bugfix/revert e5ba8616 (#5479)
* Revert e5ba8616

* Revert "Reverted Sentry for debug purposes (#5425)"

This reverts commit 4466c4ad4b.

Co-authored-by: Omar López <zomars@me.com>
2022-11-11 09:47:11 +00:00
0b2250caf9 fix: routing form edit button (#5466)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2022-11-11 15:01:13 +05:30
Julius Marmingeandzomars db7c3fb52f feat: trpc v10 migration (#5332)
* migrate router

* createTRPCReact

* frontend 1

* random format change

* frontend 2

* withQuery

* form router

* TS-ERROR: proxy on utils-client

* inferance

* ssg

* reuse

* trpc rc4

* Apply suggestions from code review

* skip test

* move skip one level up

* whops

* rc 6 with new setData func

Co-authored-by: zomars <zomars@me.com>
2022-11-10 23:40:01 +00:00
6d67808627 Team billing (#5453)
* WIP teams billing page

* WIP

* Create settings page

* Remove unused imports

* Create stripe customer on team creation

* Add Stripe ids to team record

* Add Stripe price ids for team to .env

* Create & delete Stripe customers

* Add string

* Merge branch 'main' into v2/teams-billing

* Create checkout session when creating team

* Create webhook to update team with Stripe ids

* Add Stripe migration files

* Move deleting team from Stripe under ee

* Some cleanup

* Merge branch 'v2/teams-billing' of https://github.com/calcom/cal.com into v2/teams-billing

* Small clean up

* Link to team's portal page

* Fix types

* Fix type errors

* Fix type errors

* Fix type error

* Delete old files & type fixes

* Address feedback

* Fix type errors

* Removes team creation modal

* WIP

* Removed billing frequency from team creation

* Add Stripe check for delete team customer

* Merge branch 'v2/teams-billing' of https://github.com/calcom/cal.com into v2/teams-billing

* Add high level form to create new team

* WIP

* Add new team to form

* Validate for invited members

* Add translations

* WIP

* Add validation for team name

* Add validation to team slug

* Clean up

* Fix type error

* Fix type errors

* WIP

* Abstract invite members function

* Add subscription status column

* Hide pending teams from settings

* Send email on paid subscription

* WIP

* Sync packages

* Add team subscription cols to schema

* WIP

* Matches locks vite version to <3

* Removed subscriptionStatus

* WIP

* Fix warning

* Query optimizations

* WIP

* Cleanup

* Wip

* WIP

* Runtime error fixes

* Cancellation fixes

* Delete team fixes

* Cleanup

* Type fixes

* Allows to check memebership in getTeamWithMembers

* Adds team creation tests

* Cleanup

* Cleanup

* Restored change

* Updated copy

* Moved component

* Cleanup

* Fix team members view

* Cleanup

* Adds failsafe for skipping publishing on update

* Cleanup

* Feedback

* More feedback

* Cleanup

* Cleanup

* Feedback

* Feedback

* Feedback

* Adds edge-case for slug conflicts

* Feedback

* e2e fixes

Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-10 13:23:56 -07:00
Nafees Nazikandkodiakhq[bot] 557b57a261 fix: floating pop up button embed modal size (#5415)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-10 13:09:19 +00:00
31875f7535 Test Preview for routing forms (#5436)
* add first version of testing routing forms

* small design changes

* use new shared component

* readd deleted code for showing result route

* add form validation for required fields

* design fixes

* add scroll

* fix design of select

* use old dialog with correct overflow behaviour

* code clean up

* remove unused import

* fix mobile view

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-10 12:58:07 +00:00
29c4efe4a8 Delete slack app (#5462)
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-11-10 11:54:15 +00:00
Udit Takkarandkodiakhq[bot] 7bda15aaa3 fix: ui of webhook segment (#5452)
* fix: ui of webhook segment

* feat: use toggle group component

* fix: default bug

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-10 11:38:40 +00:00
4176086ce0 Removed v1 component split for event types component (#5399)
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-10 11:23:55 +00:00
Hariom Balharaandkodiakhq[bot] 04e62a10e6 In selfhosted, calvideo isnt default and thus an event can be created without location. Handle that (#5457)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-10 11:07:16 +00:00
Carina WollendorferandCarinaWolli 0ff38dece2 FIx button text (#5456)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-11-10 09:07:26 +00:00
Hariom Balhara 8416b76bf6 Fix flakiness (#5454) 2022-11-10 07:45:10 +00:00
zomars c3a427346d Matches locks vite version to <3
# Conflicts:
#	yarn.lock
2022-11-09 16:42:49 -07:00
Udit Takkar a73b3fcaed fix: use Intl to format month name (#5449) 2022-11-09 19:14:01 +00:00
Alex van Andel 25d684ed3e v2.1.4
release.yml / release (push) Canceled after 0s
2022-11-09 16:29:23 +00:00
Alex van Andel 195cb84a8b Disable ADMIN role when password requirements aren't met (#5445) 2022-11-09 16:23:39 +00:00
4466c4ad4b Reverted Sentry for debug purposes (#5425)
* Reverted Sentry for debug purposes

* Downgraded yarn.lock

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-09 16:10:31 +00:00
4b532646cb fix: back url (#5414)
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-09 15:57:23 +00:00
Udit Takkar 6d42fdb71c chore: fix typo (#5432) 2022-11-09 15:45:42 +00:00
Bailey Pumfleet 1b23671812 Update password length requirement to 8 characters (#5438) 2022-11-09 12:42:30 +00:00
Leo GiovanettiandAlex van Andel 7d1fb7c659 Fixing count and redundant checks for recurring (#5426)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-11-08 20:59:44 +00:00
zomars 97b99deb5d Adds release action 2022-11-08 13:32:18 -07:00
zomars 4803c956dc DB previews take 2 2022-11-08 13:27:55 -07:00
zomars 8ff5d476d9 Sync submodules 2022-11-08 10:48:54 -07:00
Alex van Andel 4421981da7 v2.1.3 2022-11-08 16:55:55 +00:00
Udit TakkarandAlex van Andel 87ffedbebd fix: use hidden (#5422)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-11-08 16:50:13 +00:00
Udit TakkarandAlex van Andel 2ba5e6cdd4 fix: update form methods when username is updated (#5403)
* fix: update form methods when username is updated

* Using RHF to decrease state logic in profile.tsx for UsernameAvailability (#5405)

* Type fixes & fixed onboarding

* Removed undefined allowed value + removed invalidation

* No longer need usernameRef.current.value

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-11-08 16:36:14 +00:00
Alex van Andel 69100a0fe8 Hotfix foundConflict = false on init 2022-11-08 16:12:31 +00:00
Colin Griffin cde02e1a47 Check for existence of NEXTAUTH_URL before overwriting (#5419)
* check for existence of NEXTAUTH_URL before overwriting

* add comment
2022-11-08 15:04:18 +00:00
deeb8f38e5 Support header in Routing Form CSV (#5133)
* Dont actually delete fields and now add headers to CSV which is possible

* Fix TS issues

* Handle cases due to soft deletion

* Add submission time as a column

* Fix bug with fields getting deleted

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-08 14:21:53 +00:00
Hariom Balharaandkodiakhq[bot] 85d7341668 Downgrade plugin to match vite version (#5410)
* Downgrade plugin to match vite version

* Update yarn.lock

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-08 08:51:24 +00:00
+1 8593da4e25 [CAL-106] /onboarding - UI issues & small improvements (#5396)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Matheus Benini Ferreira <88898100+MatheusBeniniF@users.noreply.github.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-08 07:49:44 +00:00
Alex van Andel 8b5d57c573 Cleanup + migrate away from process.browser (#5408)
* Cleanup + migrate away from process.browser

* Bring back HeadSeo in ui/v2
2022-11-08 08:36:59 +01:00
Alex van Andel 7e19e0d9e2 Add data?.isDefault to disabled={} prop (#5407) 2022-11-08 00:09:57 +00:00
Alex van Andel 47a663f5a9 Fail lint check on error (#5398) 2022-11-07 14:12:12 -07:00
Leo GiovanettiandPeer Richelsen e5ba861621 Sendgrid app and code simplification (#5269)
* Sendgrid app and code simplification

* Applying app-store-cli + impl

* Fixing types

* Adding features to readme

* Fixing unit tests

* A few last tweaks regarding UX and env vars

* Applying feedback

* Using calcom icons

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-07 14:52:03 -03:00
Alex van Andel 82a6ab7957 Circumvents Dayjs issue by going native (#5363) 2022-11-07 15:16:35 +00:00
Alex van Andel 22dad1ae7c Fix lint error that made its way into main 2022-11-07 10:37:50 +00:00
github-actions[bot]andCrowdin Bot f1208eadc0 New Crowdin translations by Github Action (#5391)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-11-06 19:07:40 +00:00
Om RayandPeer Richelsen d9a867bbde Change Zap automation UI issues (#5355)
* changed styles for Zap automation ui to match figma

* Increased margintop value

* increase margintop value to mt-4

* fixed some styling issues

* fixed issue with buttons being at different heights

* changed vertical positioning of text and increased min height

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-06 16:09:10 +00:00
Hariom Balharaandkodiakhq[bot] 7625950950 Remove unsupported operators by JSOnLogic (#5394)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-06 09:33:44 -05:00
bf68ec1ad5 use LOGO constant instead of fixed string in OgImages (#5234)
* use LOGO constant instead of fixed string in OgImages

* fixed linting error

Co-authored-by: René Müller <rene.mueller@clicksports.de>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-05 20:20:59 +00:00
Om RayandPeer Richelsen 0fa7982084 Guests will be able to add their own location for in person meetings (#5282)
* Guests will be able to add their own location or in person meetings

* fix type error

* fixed issue with attendees location not showing on success page

* google meet booking will now go through, removed unnecessary change

* filter out attendee address when editing location

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-05 20:10:10 +00:00
Nafees NazikandPeer Richelsen a419613d29 chore: convert anchor tag to p tag (#5370)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-05 19:26:48 +00:00
alannnc dff08dc5b9 fix/google-calendar-removes-attendees-when-seats-5336 (#5366)
* Adding missing code that copied booking attendees to event attendees

* Clean up code

* Fix types

* Fix types

* Doing deep clone for sending seats email
2022-11-05 18:58:35 +00:00
alannncandAlex van Andel 7517feb62a fix/success-page-seats-description-5338 (#5390)
* Add validations for server side loading of bookingInfo for seats event types

* Fix prop used for validating if booking has seats enabled

* Fix types

* Removed frontend filter for seatsShowAttendees as not needed anymore

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-11-05 14:47:29 -04:00
5954144b98 Make sure body being jsoned is fresh (#5371)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2022-11-05 18:13:34 +00:00
Hariom BalharaandAlex van Andel a8c4a9c4c2 Fix viewer.me avatar size (#5392)
* Fix viewer.me avatar size

* Fix for i18n-next typecheck

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-11-05 18:00:10 +00:00
Alex van Andel b1bef50ba8 Downgrade vite (#5389) 2022-11-05 11:40:00 +05:30
Alex van Andel 5030245ad5 /settings -> /settings/.. redirect in next.config.js is server only (#5379) 2022-11-04 16:30:12 -04:00
zomars 2aa84cd5ce Sync package.json 2022-11-04 12:15:12 -07:00
Alex van Andel 324794e36c Fixed the profile page (#5378) 2022-11-04 13:25:07 -04:00
Leo GiovanettiandAlex van Andel 25e3e31045 Improving recurring info in bookings (#5253)
* Improving recurring info in bookings

* Adding fixes to success page, it was invalid on custom tz

* Remove dead code

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-11-04 12:43:02 -04:00
+6 9d1a0637f4 [CAL-153] Wrong redirect when clicking user or teams avatar in event types (#5348)
Co-authored-by: gitstart <gitstart@gitstart.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: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Matheus Benini Ferreira <88898100+MatheusBeniniF@users.noreply.github.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Matheus Benini <matheus_benini@hotmail.com>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: MuriloAmarals <muralha2000@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: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Matheus Benini Ferreira <88898100+MatheusBeniniF@users.noreply.github.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Matheus Benini <matheus_benini@hotmail.com>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: MuriloAmarals <muralha2000@gmail.com>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev>
2022-11-04 15:54:45 +00:00
b5b41da183 Cleaning up storybook files (#5290)
* storybook v2 init

* Merge config into storybook vite build

* Remove path

* Storybook config tweaks

* Added styles and settings for storybook v2, and started working on button documentation and examples.

* Badges + flex wrap on mobile

* Breadcrumbs+button+avatar

* Checkbox

* Input + moving files around

* WIP table

* WIP table grid

* Replaced imports for new components.

* Added first steps for varianttable.

* Small alignment fix.

* Custom Args Table - With scrollbar

* Adding table to components that need it + darkmode

* Add intro

* Fix types

* Remove V1 storybook and replace with V2

* Fix badge type error

* Fixed storybook dependencies

* Added cover image to storybook

* Remove vita from ts config, we dont use vite.

* Fixed button import.

* Explained postcss pseudo plugin.

* Fixed badge import.

* Add Avatar Stories

* ButtonGroup Stories

* Fixed imports

* Add checkbox stories

* Add  exports for differnt types of inputs

* Fix form and text area input

* Fix mass import errors

Co-authored-by: Jeroen Reumkens <hello@jeroenreumkens.nl>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-11-04 15:40:46 +00:00
Udit TakkarandAlex van Andel acad0619f5 fix: callback url (#5372)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-11-04 15:20:17 +00:00
Alex van Andel db9911a264 Bugfix/5271 wrong availability displayed on nov 6 (#5365)
* Fixes the localisation issue with recurring events

* Implement DST as Dayjs doesn't

* Fixed generated booking URL when different TZ is set

* manually apply DST offset to times

* Fix type error
2022-11-04 10:59:38 -04:00
Alex van Andel 328a354f4d Check for working hours + rename isAvailable (#5342)
* Check for working hours + rename isAvailable

* Return did not make it all the way back to callee

* Removed redundant break clause

* Fixes forEach return
2022-11-04 11:36:11 +00:00
Om Rayandalannnc eb14c1b796 When using seats, other guests (name, email) are no longer always listed on the success page (#5361)
* fixed incorrect ternary operator

* Update apps/web/pages/success.tsx

Co-authored-by: alannnc <alannnc@gmail.com>
2022-11-03 21:43:37 -07:00
Nafees Nazikandkodiakhq[bot] 179a9b7139 feat: Improve edit availability workflow (#5159)
* feat: return data fromsetupDefaultSchedule

* feat: update returned data of "schedule.update" resolver as same as "schedule" query resolver.

* feat: set query data from updated results and invalidate prev default availability if it's changed.

* chore: add comments

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-03 20:58:52 +00:00
Omar López 3f486a9b99 Revert "Fix (#5293)" (#5359)
This reverts commit f934dc6f4f.
2022-11-03 13:29:14 -07:00
Alex van Andelandalannnc b0f2ce4818 Fixes recurring hard crash in two places to ensure rrule gets valid parameters (#5350)
Co-authored-by: alannnc <alannnc@gmail.com>
2022-11-03 19:14:19 +00:00
Hariom Balharaandkodiakhq[bot] d7e9bb10b8 Routing Forms - UI Fixes (#5353)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-03 15:17:52 +00:00
4c24614e06 fix: use matcher in middleware (#5304)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-03 14:51:43 +00:00
6a002b900f Send Email to Owner on Form Submission (#5261)
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-03 20:10:03 +05:30
Hariom BalharaandAlex van Andel d751cca0f4 Add "Choose common schedule toggle" to team events (#5343)
* Add Choose common schedule togglee

* Fix types

* Add translations

* Improve variable name

* Move setting to config so that all such lightweight boolean settings can exist here

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

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

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-11-03 14:24:07 +00:00
Udit Takkarandkodiakhq[bot] 328f00a9b5 fix: add hour12 (#5349)
* fix: add hour12

* fix: timeformat variable

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-03 12:28:39 +00:00
alannncandHariom Balhara e6a5fe179b feature/sentry.io for web (#5326)
* Initial commits that install sentry.io on web app

* Updates yarn.lock

* Update yarn.lock

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2022-11-03 11:28:52 +00:00
Carina WollendorferandCarinaWolli b629333eb8 fix dynamic location variable (#5345)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-11-03 10:40:42 +01:00
06eca4b28a Make SMS workflow actions only available for teams plan (#5330)
* fix issue when updating workflow

* make SMS actions pro feature

* Improved clean up on inline functions

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
2022-11-03 09:24:41 +01:00
f0ca797902 New Crowdin translations by Github Action (#5323)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-02 11:17:43 +00:00
+6 3efd062dbf /getting-started/setup-availability the table of days breaks depending on the language (#5104)
Co-authored-by: gitstart <gitstart@gitstart.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: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Matheus Benini Ferreira <88898100+MatheusBeniniF@users.noreply.github.com>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: MuriloAmarals <muralha2000@gmail.com>
Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev>

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: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Matheus Benini Ferreira <88898100+MatheusBeniniF@users.noreply.github.com>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: MuriloAmarals <muralha2000@gmail.com>
Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-02 11:17:19 +00:00
0959cf7cd5 fix saving issue (#5328)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-02 10:06:08 +00:00
sean-brydon f34d6e3c17 Buffer limits fix (#5248)
* Buffer limits + remove redundant tests

* Fixing buffer

* Compound

* Afterbuffer fix for no event afterbuffer set

* Bug fixes

* Buffer includes eventType before
2022-11-02 09:40:30 +00:00
Leo Giovanetti edabf27929 Duplicate meeting within non-traditional calendars (#5208)
* Filter calendars and don't return

* Adding comment

* Applying feedback
2022-11-01 16:07:28 +00:00
Alex van Andelandkodiakhq[bot] 98f6fd836e Fixed description by manually implementing asSafeHTML (#5321)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-01 15:42:48 +00:00
Udit Takkarandkodiakhq[bot] a8710ce625 fix: add dark mode (#5318)
* fix: add dark mode

* chore: padding

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-01 14:14:18 +00:00
3043294a08 New Crowdin translations by Github Action (#5306)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-01 14:00:19 +00:00
66c2537f9f fix: use timeformat from getMe query (#5314)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-11-01 13:59:44 +00:00
sean-brydonandkodiakhq[bot] b3d74e575a Provide update feedback (#5319)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-01 13:56:46 +00:00
sean-brydon 190d0435e0 Improve error handling (#5316) 2022-11-01 13:45:02 +00:00
Jeroen Reumkens d0338dc71b 🧹 Cleaned up older v1 components for availability, settings and auth. (#5288)
* Cleaned up older v1 components for availability, settings and auth.

* Updated path.
2022-11-01 13:29:01 +00:00
Hariom Balharaandkodiakhq[bot] 2df8f4f145 Fix profile page data reset on re-focus of window (#5311)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-01 18:22:32 +05:30
Alex van Andel 8ecfe89a6f v2.1.2 2022-11-01 11:31:02 +00:00
b1ced10626 Added generic image variant for og images and make meta description longer. (#5285)
* Added generic image variant for og images.

* Made og and meta descriptions longer, removed difference between longer and shorter descriptions now we only have one variant.

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-11-01 08:16:08 +00:00
b3ba89c074 Fix avatar infinite redirect (#5299)
* If due to some reason avatar URL is same as route, avoid infinite redirection by serving default

* Fix avatar reverting issue

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-01 06:30:08 +00:00
77d339ae8f Without checkout session premium username isnt possbole (#5296)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2022-10-31 23:07:51 +00:00
alannnc ac9b2d0577 Feature/invalid credentials (#5120)
* Fixing types from handleErrorJson usage and Credential

* Replace credential prisma type for a better suitable

* Improvements on zoom video adapter

* Renamed extendedCredentialType and put it in a best suited file

* Frontend display invalid credential

* Fix styles and text

* Fix type required for fake daily credentials
2022-10-31 22:06:03 +00:00
Udit Takkar 03fd546e99 fix: add new url (#5295) 2022-10-31 18:53:59 +00:00
Alex van Andel 6484f1639c Default to null for now (#5302) 2022-10-31 18:42:45 +00:00
Hariom BalharaandAlex van Andel f934dc6f4f Fix (#5293)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-31 13:58:47 +00:00
Hariom Balhara dd9d32815a Ensure position is present (#5291) 2022-10-31 12:13:04 +00:00
Jeroen Reumkens 5c2ad7827e Removed old v1 app components in web. (#5286) 2022-10-31 10:42:01 +00:00
Jeroen ReumkensandPeer Richelsen 5dc5eadf28 Allow og image api in robots.txt (#5283)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-31 09:48:17 +00:00
Peer Richelsen d6fc551950 follow up: 4978 cal 87 re add tip in sidebar (#5016) 2022-10-31 08:11:24 +00:00
5bb868d2c3 React embed: fix modal close on click outside (#5181)
* fix: modal close on click outside

* fix: for sm viewport

* add: main class

Co-authored-by: root <root@pop-os.localdomain>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-31 07:57:09 +00:00
Alex van Andel ec9b3a4261 Fixes #5278 - wrong profile picture (#5279)
* Fixes #5278 - wrong profile picture

* users is not allowed to be undefined
2022-10-30 20:31:21 +00:00
Alex van Andel ce04f78a56 Fixed regression introduced when date.format() was removed (#5265) 2022-10-28 20:13:15 +01:00
Carina WollendorferandCarinaWolli 9f362db60c add attendee's email as dynamic text variable (#5258)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-10-28 09:53:06 +00:00
alannncandkodiakhq[bot] 7a93bec735 Add try-catch and validation to run triggers on reschedule (#5252)
* Add try-catch and validation to run triggers on reschedule

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

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-28 09:36:30 +00:00
7b1e824a36 Gegt only warn,error and fatal (#5259)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-28 09:19:48 +00:00
0746b1e372 Fix Zapier sample data and payload (#5241)
* fix zapier example and payload

* fix type error

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-28 09:09:15 +00:00
github-actions[bot]andCrowdin Bot 39747a151c New Crowdin translations by Github Action (#5243)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-28 09:56:09 +01:00
b6ae4529e9 Adds number for sms notifications from workflows to /success (#5210)
* add sms notification phone number to success page

* remove duplicate translation

* add non breaking space between sms and notifications

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-10-27 09:53:13 +00:00
Nafees Nazikandkodiakhq[bot] 8c76da8d79 fix: make bookings nav item selected, while switching to other filter tabs. (#5176)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-27 09:45:01 +00:00
Hariom Balharaandkodiakhq[bot] 4210f35e9c Add a test that could have caught bug #5142 (#5157)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-27 15:04:34 +05:30
+9 eae60043bb [CAL-183] email and calendar still use old “cancel/reschedule” screens (#5149)
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 Benini Ferreira <88898100+MatheusBeniniF@users.noreply.github.com>
Co-authored-by: MuriloAmarals <muralha2000@gmail.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: C000Ldude <coolmagnas@gmail.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Eman <emmanuelgatwech@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-27 09:13:52 +00:00
Nafees Nazik bf34296864 fix: truncte longer text (#5150) 2022-10-27 08:22:03 +00:00
github-actions[bot] d0a24f7c3c New Crowdin translations by Github Action (#5215) 2022-10-27 09:07:54 +01:00
Jeroen Reumkens f009321deb Stop long ursl from overflowing on booking page. (#5240) 2022-10-27 09:04:33 +01:00
4d04a4d9a6 add organizer as reply-to email (#5203)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-27 07:05:11 +00:00
Peer Richelsen 888f60a94c added sirius video app (#5187) 2022-10-27 06:06:53 +00:00
d6c4970bf4 Cleaned up v1 for app store pages. (#5204)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-27 05:50:16 +00:00
Hariom Balharaandalannnc 63ce4a68ac Make link relative to root (#5218)
Co-authored-by: alannnc <alannnc@gmail.com>
2022-10-26 21:29:09 +00:00
cddcd742f6 Add: copy link option (#5236)
* add: new item and local var

* add: copy link func

Co-authored-by: root <root@pop-os.localdomain>
Co-authored-by: alannnc <alannnc@gmail.com>
2022-10-26 21:12:25 +00:00
alannnc a7a82a6b51 Fix/event location when fallback video (#5216) 2022-10-26 21:16:38 +01:00
Udit TakkarandPeer Richelsen 1a104f172d fix: use max-w for description (#5237)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-26 19:27:28 +00:00
142be70b89 fix: overflow text, ui bugs (#5071)
* fix: overflow text, ui bugs

* fix: merge conflict

* fix: availability buttons ui

* fix: mobile view added for /event-type header

* fix: dropdown closing bug on mobile

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-10-26 16:29:01 +01:00
Peer Richelsen 5a0e4c8726 removed link from cal video (#5228)
* removed link from cal video

* nit
2022-10-26 14:31:19 +01:00
Syed Ali ShahbazandAlex van Andel f2cdb75f91 Hotfix/caldav reschedule jsonerror (#5193)
* fixed reschedule icalstring and json error

* logging update

* code quality changes

* removed unnecessary comment

* removes console log remnants

* fix for checktype

* Take care of all OK status response codes

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

* Add relevant comment in code

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

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-26 11:04:36 +00:00
352ce35b40 fix updating already deleted booking (#5201)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2022-10-25 19:16:16 +00:00
Leo Giovanetti 3fa52b0600 Fix add calendar button (#5212) 2022-10-25 12:50:19 -06:00
Carina Wollendorfer a4ff5d0cee fixes google calendar issue (#5205) 2022-10-25 13:45:43 +00:00
sean-brydon 5f1d20319a Segment Control (#5170)
* Segment

* Fix type errors

* Fix HUG
2022-10-25 10:47:03 +00:00
Jeroen Reumkens d597aa91e5 Fixed alignment for CTA buttons in PWA. (#5173) 2022-10-25 08:51:43 +00:00
github-actions[bot] 803ab6c2e9 New Crowdin translations by Github Action (#5174) 2022-10-25 09:41:29 +01:00
Hariom Balhara 0a23be4579 Ensure the URL is absolute before checking the origin (#5194) 2022-10-25 13:48:19 +05:30
Hariom Balharaandkodiakhq[bot] 00c3e9680c Fix subjects for emails for Optin Bookings (#5148)
* Fix subject for manually confirmed booking

* Fix subject for request email to attendee

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-25 07:32:05 +00:00
zomars 68ae3c48a0 v2.1.1 2022-10-24 18:41:47 -06:00
alannncandOmar López 1c59c0dd06 try catch all the promises after reservation its completed (#5182)
Co-authored-by: Omar López <zomars@me.com>
2022-10-24 18:32:14 -06:00
2d5bf1ffc1 feat: new route added for deletion (#5160)
* feat: new route added for deletion
required password removed from update profile

* fix: update url

Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-24 18:32:00 -06:00
Joe Au-Yeung 317909d20a Fix 404 error when editing default availability & create new availability from event type page (#5085)
* Edit/create new availability

* Connect new schedule to event type

* Remove comment

* Shell back button executes window.history.back

* Fix default schedule

* Add default badge

* Fix type error
2022-10-24 18:29:49 -06:00
Jeroen ReumkensandPeer Richelsen 16eb516a9b Added improved designs for OG images. (#5102)
* Added improved designs for OG images.

* Changed size of additional attendees circle in og image.

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-24 18:20:01 -06:00
Jeroen ReumkensandOmar López a0e93e1eeb Feature/improved timeformat selection booking page (#5175)
* Aded new togglegroup component.

* Make 12/24h time format toggle functional and store value in localstorage.

* Responsive tweaks

* Added translations

* Added 12/24h timeformat hint on profile page.

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

Co-authored-by: Omar López <zomars@me.com>
2022-10-24 18:14:06 -06:00
sean-brydonandOmar López b18eabc6e8 #5131 -Event Type Settings Inconsistency (#5147)
* Reusable component

* Fix limits not being toggleable

* Remove custom input margin

* addTestId

* Limits+adv

* Reccuring Tab

* Remove console .log

Co-authored-by: Omar López <zomars@me.com>
2022-10-24 17:11:07 -06:00
Alex van AndelandLeo Giovanetti 5305f31266 Bugfix/recurring dst change (#5172)
* Structural fix to recurring times

* Remove conversion regression

* Revert current time -> startTime based utcOffset

* Fixing remaining events count

* Using user's preference for recurring tooltip

* Missing refactor

* Showing better datetime in booking page
Added an extra seeded example for recurring

Co-authored-by: Leo Giovanetti <hello@leog.me>
2022-10-24 16:37:55 -06:00
sean-brydonandalannnc 6232a111ef Empty Screen Improvements to Public event type list (#5178)
* Empty Page

* Declare const and reuse it

Co-authored-by: alannnc <alannnc@gmail.com>
2022-10-24 22:20:41 +00:00
0ea58103c8 Harsh/fix layout shift on loading ss (#5184)
* fix: layout shift in ss apps page

* Fix for mobile style justify-center

Co-authored-by: root <root@pop-os.localdomain>
Co-authored-by: Alan <alannnc@gmail.com>
2022-10-24 21:36:19 +00:00
3d595e9299 Fix design of showing generated API key (#5180)
* fix design of api key

* fix copy icon

* Fix copy key height

* Add mobile style fixes

* Fix styles

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
2022-10-24 21:13:27 +00:00
f135f7baad Fix unauthorized error when saving workflows (#5127)
* fix wrong unauthorized error when no userId and teamId

* use find instead of filter

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-24 09:38:05 +02:00
Peer RichelsenandHariom Balhara 9f1341e94e added ga4 google analytics (#5126)
* added ga4 google analytics

* added ga4 script as TODO

* get analytics app working

* Remove stale comment

* Fix types

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2022-10-22 09:39:36 +00:00
79df03de49 Profile dropdown menu will close after avatar is clicked (#5136)
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-21 20:59:14 +01:00
+9 b7892b10ef Add/Edit location dialog: "can't find the right video app? visit our app store" (#4990)
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Matheus Benini Ferreira <88898100+MatheusBeniniF@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 Benini <matheus_benini@hotmail.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: MuriloAmarals <muralha2000@gmail.com>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: C000Ldude <coolmagnas@gmail.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Eman <emmanuelgatwech@gmail.com>
2022-10-21 18:50:15 +01:00
Hariom Balharaandkodiakhq[bot] ff5fd43a9e Remove accidental use of hashedUrl for successRedirectUrl (#5153)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-21 10:09:51 +00:00
Peer Richelsen 34c13db872 pipedream app (#5119) 2022-10-21 09:49:49 +00:00
+8 e044f963f8 [CAL-216] Confirming a booking shows old toast. Also removes old notification component. (#5146)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: MuriloAmarals <muralha2000@gmail.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@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: gitstart <gitstart@gitstart.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
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: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Eman <emmanuelgatwech@gmail.com>
2022-10-21 08:54:18 +00:00
Hariom Balhara 0344130f68 Fix unconfirmed booking marked as confirmed in DB (#5141) 2022-10-21 05:48:24 +00:00
Leo GiovanettiandAlex van Andel 5f78eceb89 Abstracting booking cancellation (#5105)
* Abstracting booking cancellation

* Tweaking to use id and not uid

* Standardizing http errors

* Moving schema to prisma zod-utils

* Update apps/web/pages/api/cancel.ts

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

* Cancel only for delete and post verbs

* Make cancellation work with uid, and id

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-20 23:28:02 +00:00
39a3a1affb allow null value for attendee phone number (#5132)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-20 18:21:27 +00:00
bc6f959c7a fix: make all apps category tab scrollable (#5113)
* feat: make left and right button workable

* refactor: separate category tab into a seperate component
to reduce unnecessary re-rendering while scrolling through the arrow buttons.

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2022-10-20 17:59:24 +00:00
github-actions[bot]andCrowdin Bot bb45b9617b New Crowdin translations by Github Action (#5130)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-20 12:43:13 +01:00
+8 8725034b6c Wrong css on event-types dropdown button (#4934)
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: MuriloAmarals <muralha2000@gmail.com>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.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: C000Ldude <coolmagnas@gmail.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Eman <emmanuelgatwech@gmail.com>

Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: MuriloAmarals <muralha2000@gmail.com>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.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: C000Ldude <coolmagnas@gmail.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Eman <emmanuelgatwech@gmail.com>
2022-10-20 11:35:12 +00:00
Joe Au-YeungandPeer Richelsen e53abd921e Add CTA to Meta component & button to add calendar on /settings/calendars (#5099)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-20 10:03:39 +00:00
github-actions[bot]andCrowdin Bot 7bb3397503 New Crowdin translations by Github Action (#5123)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-20 10:32:44 +01:00
github-actions[bot]andCrowdin Bot 72d19d1d62 New Crowdin translations by Github Action (#5121)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-20 10:07:43 +01:00
Nafees NazikandPeer Richelsen 2931f4f84a refactor: use the useAnimate hook (#5103)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-20 05:09:52 +00:00
Hariom BalharaandPeer Richelsen 7756b9c3a1 The links that can be directly given to embed should pre-render(Either SSG/SSR) (#4975)
* Embed SSG and consistently pass embedType query param across pages

* Embed fixes

* Code cleanup

* Add main class which tells embed which helps in identifying which area is outside the main content

* remove any special optimization handling for routing forms

* Add comments

* Small fixes

* Fix broken team booking page in embed

* Fix Fallback message dark theme

* TS Fixes

* Fixes

* Fix tests

* Remove not required code

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-19 21:25:03 +00:00
github-actions[bot]andCrowdin Bot 477941545e New Crowdin translations by Github Action (#5115)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-19 21:59:13 +01:00
zomars 237828e4ed Upgrades zod 2022-10-19 13:28:54 -06:00
e23f3b27bf New Crowdin translations by Github Action (#5107)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-19 19:01:39 +01:00
Udit TakkarandPeer Richelsen ee084aa8bd fix: add tooltip, truncate (#5096)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-19 19:00:48 +01:00
Peer Richelsen 8ece2525e5 Update README.md 2022-10-19 17:59:01 +01:00
github-actions[bot]andCrowdin Bot 865c67a962 New Crowdin translations by Github Action (#5100)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-19 17:27:37 +01:00
Leo GiovanettiandOmar López b6be94fd14 Apps Status + Updates to non-traditional calendars (#5034)
* Apps Status + Updates to non-traditional calendars

* Recurring booking tweaks

* Last tweaks

* Fixing checks

* Fixing eslint

* Reverting unneeded changes, using plain text email

* More unneeded changes revert

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

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

* Fixing lint

* Refactored appsStatus in emails

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

Co-authored-by: Omar López <zomars@me.com>
2022-10-19 13:11:50 -03:00
Om Ray f298865d9e shows all locations and team members on /book page (#5087) 2022-10-19 09:54:18 -06:00
Kiran K 9e85770435 Fix the "Sign in with SAML" button on the login page (#5089)
* Remove the unused component

* Fix the login with SAML
2022-10-19 09:50:25 -06:00
Hariom Balhara 1427c8e792 Fix social previews title and description by doing server side translation (#5098) 2022-10-19 14:28:48 +00:00
github-actions[bot]andCrowdin Bot baa641d1f4 New Crowdin translations by Github Action (#5072)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-19 12:25:31 +01:00
Syed Ali ShahbazandAlex van Andel 51e08a7642 iCalendar timezone fix in CalDAV (#5091)
* adds support to timezone change using tzid

* code improvement with math absolute

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

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-19 11:18:08 +00:00
373c255733 Feat/3334 show times in timezones for bookingpage (#4971)
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-19 09:45:44 +00:00
sean-brydon f4fb4ddad1 Adds impersonation settings (#5088) 2022-10-19 10:33:40 +01:00
Udit TakkarandAlan 79c491986a fix: text overflow (#5080)
Co-authored-by: Alan <alannnc@gmail.com>
2022-10-18 23:31:41 +00:00
Kiran Kandzomars 759a89bb0c Reintroduce SAML SSO (#4938)
* wip reintroduce SAML SSO

* Fix the imports

* wip

* Some tweaks

* Fix the type

* Reduce the textarea height

* Cleanup

* Fix the access issues

* Make the SAML SSO active on the sidebar

* Add SP's instructions

* Remove the console.log

* Add the condition to check SAML SSO is enabled

* Replace SAML SSO with Single Sign-On

* Update to SAML feature

* Upgrade the @boxyhq/saml-jackson

* Fix the SAML part and other cleanup

* Tweaks to SAML SSO setup

* Fix the type

* Fix the import path

* Remove samlLoginUrl

* Import fixes

* Simplifies endpoints

Co-authored-by: zomars <zomars@me.com>
2022-10-18 14:34:32 -06:00
zomars 41689ecc92 Type fixes 2022-10-18 13:57:42 -06:00
9118ca328f migrate /api/book/request-reschedule to viewer.booking.requestResched… (#4488)
* migrate /api/book/request-reschedule to viewer.booking.requestReschedule in trpc

* Type fixes

Co-authored-by: hussamkhatib <hussamkhatib@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2022-10-18 13:47:36 -06:00
8fc4d342fd Hide other attendees on event types with seats (#4766)
* Add seatsHideAttendees to schema

* Add migration

* Add frontend option to hide attendees

* Pass hide attendees to email

* Hide attendee names on success email

* Add types for existing attendees

* Hide other attendees if hidden

* Pass seatsHideAttendees to Google Cal

* Add translation

* Reduce redundancy

* Fix type error

* Change toggle to show attendee information

* Minor text change

* Fix type errors

* Update snapshots

* Merge branch 'main' into seats-hide-attendees

* Add back email

* Add close.com specific types

* Add eslint ignore comments

* Merge branch 'seats-hide-attendees' of https://github.com/calcom/cal.com into seats-hide-attendees

* Simplify tests

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-18 13:41:50 -06:00
7f2560e1e3 feat: add optimistic updates for deleting and changing hidden status of event types. (#4721)
* fix: remove loading state

* feat: use optimistic update for deleting

* feat: use optimistic update for hiding

* Moved PR changes

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2022-10-18 13:34:01 -06:00
Carina Wollendorfer 3e201c9fe8 fix cancellation reason not shown (#5084) 2022-10-18 19:02:04 +00:00
Joe Au-YeungandOmar López 5ab5af753a Fix destination calendar overflow on installed and move DestinationCalendarSelector to feature package (#4778)
* Standardize destination calendar selector

* Move DestinationCalendarSelector to feature package

* Render integration name

* Add custom components to label and selected

* Render destinationCalendar on page load

* Change name to just Outlook

* Small fixes

* Merge branch 'main' into hotfix/install-calendar-overflow

* Merge branch 'main' into hotfix/install-calendar-overflow

* Fix type errors

* Fix type errors

* Update apps/web/pages/settings/my-account/calendars.tsx

* More type fixes

Co-authored-by: Omar López <zomars@me.com>
2022-10-18 18:06:26 +00:00
Jeroen Reumkens 5c01467caa #4252 Use vercel og to generate og images for meetings and apps. (#4943)
* #4252 Use vercel og to generate og images for meetings and apps.

* Removed duplication.

* Added new design for og images (wip).

* Implemented og image design for meeting image, including support for all variants.

* Implemented design for app og images.

* clenaup

* Fixed types

* Added the option to not render headseo in main shell in order to render your own.

* Added comments.

* fix

* Small tweaks.

* Fixed lock file.

* Fixed types

* Optimized svg's so vercel og supports them.

* Fixed og image on user page.

* Added truncate utils.

* Small style tweaks

* App og image alignment.

* Added og image to team/slug pages.

* Added correct variable to og image path constant.
2022-10-18 11:46:22 -06:00
Carina WollendorferandCarinaWolli 3cf47c2ee9 don't show location if only one (#5074)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-10-18 16:41:14 +02:00
8272fcd5c7 New Crowdin translations by Github Action (#5052)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-18 13:47:47 +01:00
95e3f55f26 Add checkbox that defines if SMS reminder phone number should be required (#5047)
* add phone number required checkbox

* make input required only if numberRequired is true

* fix error that smsReminderNumber is null

* code clean up

* fix typo

* save no input as null

* fix code change

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2022-10-18 12:47:15 +00:00
Carina WollendorferandCarinaWolli 431e4224fe fix bug with setup page (#5068)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-10-18 12:00:42 +00:00
59ef4fe459 Hotfix: Avoid theme flicker in routing form embed. (#5067)
* Hotfix: metadata parsing

* Dont remove loader as theme switching can still occur in plain sight :(

Co-authored-by: zomars <zomars@me.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-10-18 11:47:06 +00:00
Hariom Balharaandzomars 4211ec1a82 Fix App Redirect logic to allow EventTypeApps requiring auth to correctly redirect (#5065)
Co-authored-by: zomars <zomars@me.com>
2022-10-18 12:18:39 +01:00
Hariom Balharaandzomars 2e169801e8 Hotfix: Installed Apps Tabs Switchi g (#5063)
* Hotfix: metadata parsing

* Use Router query as tabs use shallow routing, data-fetching doesnt occur on switching tabs

Co-authored-by: zomars <zomars@me.com>
2022-10-18 08:46:06 +00:00
zomars d0d793a719 Hotfix: removed data64 avatar for 4mb payload limit 2022-10-17 14:23:00 -06:00
zomars 05f303bfad Hotfix: removed data64 avatar for 4mb payload limit 2022-10-17 13:52:04 -06:00
+9 c1b41d8251 Wrong focus ring on Help Dropdown (#5049)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: MuriloAmarals <muralha2000@gmail.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: Matheus Benini Ferreira <88898100+MatheusBeniniF@users.noreply.github.com>
Co-authored-by: C000Ldude <coolmagnas@gmail.com>
Co-authored-by: Klinger Matheus <50892465+KlingerMatheus@users.noreply.github.com>
Co-authored-by: Eman <emmanuelgatwech@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
2022-10-17 19:05:00 +00:00
06732c5c6d Harsh/auto add url (#4997)
* add: auto complete for slug

* Remove useStates not required for feature

* remove: states logic

* Using touched validation from react-hook-form

* Fix size of width for steps indicador

* Apply same changes for react-hook-form touched from createNewTeam

Co-authored-by: root <root@pop-os.localdomain>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
2022-10-17 18:52:24 +00:00
zomars aa038acafd Hotfix: metadata parsing 2022-10-17 12:18:59 -06:00
zomars 9842c18d50 v2.1.0 2022-10-17 11:33:43 -06:00
Alex van Andel 68f05ce2c7 Uses utcOffset to display bookings according to current time & timeZone (#5050) 2022-10-17 17:28:57 +00:00
zomars 1aade4fc57 Hotfix: bookings/past browser crash 2022-10-17 11:10:23 -06:00
github-actions[bot]andCrowdin Bot 33d71f2390 New Crowdin translations by Github Action (#5043)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-17 17:50:30 +01:00
Peer RichelsenandHariom Balhara 44ef1f80e9 added QR code app, needs eventType.URL (#4701)
* wip added QR code app, needs settings

* added QR code settings, needs eventType.URL

* Make URL prop available to apps

* Add recurringEvent in available. It was missing earlier

* added autoAnimate to AppSettings

* Remove isSunrise demo prop

* Simplify schema even more

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2022-10-17 16:38:43 +00:00
9e3e1418c2 Fix disabled locations when rescheduling (#5046)
* fix phone number input when attendee phone number is default checked

* add selected location for rescheduling and remove default checked

* don't show locations when rescheduling on availability page

* fix type error

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2022-10-17 15:47:11 +02:00
Jeroen ReumkensandAlex van Andel 0284ffc75f Removed v2 pages directory, moved all pages to root and deleted old v1 pages and middleware redirect. (#5044)
* Removed v2 pages directory, moved all pages to root and deleted old v1 pages and middleware redirect.

* Removed v2 from imports path.

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-17 18:14:43 +05:30
Hariom Balhara aee1b3b596 Fix RHF data on bg update (#5045) 2022-10-17 11:21:49 +00:00
c1bc0286d1 Additional Notes Required (#4897)
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-17 09:43:16 +00:00
48d1e95509 use v2 button (#5042)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-17 09:19:15 +00:00
Alex van Andel d8489a662e Adds a iso8601 helper function to parse a ISO Date String into a Date (#5039) 2022-10-17 09:07:49 +00:00
github-actions[bot]andCrowdin Bot 0fc1449dd9 New Crowdin translations by Github Action (#5037)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-17 09:53:41 +01:00
d5024d1b70 Fixed dropdown width according to items (#4995)
* Fixed dropdown width according to items

* Add the “Add” before the calendar type item

* Fix dropdown cut off on mobile

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2022-10-17 08:46:37 +00:00
Alex van Andelandkodiakhq[bot] b7161694a5 Update to message for invalid method (#5038)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-17 06:25:23 +00:00
Peer Richelsen cc7e4b3b33 fixed team URL in settings (#5031) 2022-10-17 11:40:06 +05:30
github-actions[bot]andCrowdin Bot 3b9653df0e New Crowdin translations by Github Action (#5028)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-16 22:30:33 +01:00
Leo Giovanetti 83442caf86 Fixing rescheduled email date (#5035) 2022-10-16 18:46:10 +01:00
Leo Giovanetti e6f912bf0c Fixing reschedule button in success page (#5033) 2022-10-15 19:38:39 -06:00
Carina Wollendorfer e75f200ff7 fix type (#5032) 2022-10-15 19:37:05 -06:00
Hariom Balhara 2d30f673e3 Fixes double bottom border in Integration List Item (#5026)
* Fixes double bottom border in Integration List Item

* Remove unused import
2022-10-15 11:08:01 -06:00
Leo GiovanettiandPeer Richelsen 2e83c7fbf8 Fixing bookings for all calendar-related apps (#5018)
* Fixing bookings for apps

* Missing assigning let var

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-15 11:02:24 -06:00
github-actions[bot]andCrowdin Bot 1b215e4faf New Crowdin translations by Github Action (#5022)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-15 11:15:17 +01:00
49429c87cb New Crowdin translations by Github Action (#5021)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-15 11:05:47 +01:00
cf423dda70 Add AppStore General Settings support (#4998)
* added weather in calendar wrapper

* added textfield to weather app, needs GeneralSettings abstraction

* add automation cateogry for n8n and zapier

* fix border radius for seperated apps

* create app specific settings in installed apps

* Fixed design issues caused by app settings

* add first version of template cards with logos

* Add link to zapier template

* Add missing translations

* fix mobile view

* better readable app description in mobile view

* code clean up

* fix issue that lisItem was always expanded

* code clean up

* code clean up

* fix import

* fix typo

* code clean up

* Add AppStore General Settings support

* add missing alt to img

* use FiShare2 icon instead of FiZap

* Remove duplicate entries

* Add border to AppSettings consistently

Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-15 10:05:14 +00:00
6ee9de6dd8 Add links to Zapier templates in installed apps (#4982)
* add automation cateogry for n8n and zapier

* fix border radius for seperated apps

* create app specific settings in installed apps

* Fixed design issues caused by app settings

* add first version of template cards with logos

* Add link to zapier template

* Add missing translations

* fix mobile view

* better readable app description in mobile view

* code clean up

* fix issue that lisItem was always expanded

* code clean up

* code clean up

* fix import

* fix typo

* code clean up

* add missing alt to img

* use FiShare2 icon instead of FiZap

* Remove duplicate entries

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2022-10-15 08:05:37 +00:00
alannnc e54bdb4c46 Custom zoom response handler (#5017) 2022-10-14 18:24:57 -06:00
zomars 1455a6f603 Adds url schema to webhooks 2022-10-14 17:41:52 -06:00
zomars 9b980b3b85 Better handling of customInput error messages 2022-10-14 16:45:40 -06:00
github-actions[bot]andCrowdin Bot 6420218cb3 New Crowdin translations by Github Action (#5011)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-14 21:41:38 +01:00
Peer Richelsen f9ca763523 <Tips> in sidebar (#4999) 2022-10-14 19:38:05 +00:00
Joe Au-YeungandPeer Richelsen c4f3dec854 Fix time format on booking page (#5006)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-14 19:15:03 +00:00
zomars ff01da540d Avoids mixing metadata and tsx
Prevents circular dependencies
2022-10-14 12:51:28 -06:00
github-actions[bot]andCrowdin Bot 9021486cd7 New Crowdin translations by Github Action (#5007)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-14 19:41:28 +01:00
zomars 550f98a636 Splits appData schemas to prevent circular dependencies 2022-10-14 12:35:44 -06:00
Peer Richelsen 9a4746844f fixed booking page padding (#5009) 2022-10-14 18:04:54 +00:00
Hariom BalharaandPeer Richelsen 271d4319b9 Introduce EventTypeAppCard in app-store and make it super easy to add it through CLI - Also adds Fathom app (#4727)
* Add OmniInstall button

* Make AppCards configurable by the app itself

* Make OmniInstallAppButton not redirect

* Fixes

* Add extendsFeature support to CLI

* Move to automatic file generation approach as dynamic import checking doesnt work correctly

* Use zod everywhere consistenly for metadata and fix all TS issues

* Fix viewer.eventTypes endpoint. Make prisma base select and _ prefixed models consistent in expecting scalars only

* Remove unnecessary zod parsing of event-types as it is making the scope of the PR huge

* Fix UI TS errors

* wip

* Add zod types support in EventTypeAppCard.tsx

* Fixes during PR review and other failing tests

* Remove unused app

* Fix stripe installation flow

* More fixes

* Fix apps and active apps count

* self review

* Add loading attribute to OmniInsall button

* Handle empty state

* Improve types

* Fix stripe app installation bug

* added fathom app (#4804)

* added fathom app wrapper, needs script injection to public booking page

* new logo

* Add Fathom script support on booking pages and add it as an eventTypeapp

* Add automation and analytics apps

* Add missing pieces for analytics category

* Rename BookingPageScripts to BookingPageTagManager

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

* Fix lint error

* Fix runtime error with legayAppData being undefined

* Remove duplicate automation key

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-14 10:24:43 -06:00
Peer Richelsen 215e3c93d6 remove unused workflow (#5000) 2022-10-14 09:33:08 -03:00
Nafees NazikandAlex van Andel 670aed9fe0 fix: add a unique key (#4711)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-14 12:33:32 +01:00
Hariom BalharaandAlex van Andel 8583d9b2e6 Embed Improvements: Add auto-scroll support back, reduce CLS, remove loader for Routing Form embed (#4819)
* Add back auto-scroll fix with proper check of visibilith

* Do improvement in CLS and dont show "Loading" text

* Improve experience with routing forms embed

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-14 10:10:54 +00:00
Carina WollendorferandCarinaWolli 88cad2e15f Workflow security fixes (#4996)
* security fix to not add even-types from other users

* throw unauthroized error if a step does not belong to workflow

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-10-14 10:52:20 +01:00
1c165bd934 Fix/fallback to calvideo (#4992)
* try/catch on createMeeting zoom

* add handle response body from zoom

* db invalid credential flag

* Update VideoApiAdapter.ts

* Added notes and try/catches

* Remove double response error validation

* Fallback to calvideo

* Fix types for credentials

* Add nullable to invalid field from credential table

* Fix calendar service credential creation with bad type

* revert db migration invalid on credential table

* Revert "Fix types for credentials"

This reverts commit 1623305aea23a33c39ba7b6fb5604dd71c6205d7.

* Revert "Fix calendar service credential creation with bad type"

This reverts commit 3fd3b08f2ee6c7019428a8ff454d371e23759ad7.

* Remove migration

* Fix for property name for api key on dailyvideo

* remove unused prop from credential

* Revert "Fix for property name for api key on dailyvideo"

This reverts commit 32976934e05e6923210718ccd5634c1357828f65.

* Tests fixes

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2022-10-14 02:36:43 +00:00
zomars 74a16e28f4 e2e now needs daily as fallback 2022-10-13 20:26:04 -06:00
github-actions[bot]andCrowdin Bot 3e417330b5 New Crowdin translations by Github Action (#4988)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-13 17:50:59 +01:00
Joe Au-YeungandPeer Richelsen 3af31860bb Close menu and prevent overscroll (#4739)
* Close menu and prevent overscroll

* Update Shell.tsx

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-13 09:50:08 -06:00
Carina WollendorferandCarinaWolli f0aded92db Add skeleton loader to offer seats (#4987)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-10-13 13:33:41 +01:00
Alex van Andel a28325e69a Adjust booking list startTime to the timeZone of the logged in user (#4984) 2022-10-13 12:01:11 +01:00
github-actions[bot]andCrowdin Bot f949cf1bd1 New Crowdin translations by Github Action (#4977)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-13 10:36:15 +01:00
zomars ba31c9dcb5 GitHub Workflow: when label: “help wanted” add to Project 2022-10-12 10:33:39 -06:00
Omar LópezandAlex van Andel e5af99f572 Moved booking logic to calcom/features (#4960)
* Moved booking logic to calcom/features

Needed to reuse on public API

* Type fixes

* Cleanup

* Cleanup

* Cleanup

* Restored still needed V1 pages

This reverts commit 790a304a6aa3b9e16525ea3b015bef773f36ded1.

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-10-12 13:04:51 +00:00
Peer Richelsen c40ff2e6bb added new license required /signup page (#4966) 2022-10-12 13:52:59 +01:00
Carina Wollendorfer b1656c7900 don't count unconfirmed recurring bookings that are in the past (#4968) 2022-10-12 11:08:30 +01:00
a9b18496e0 refactor: left booking information box component (#4251)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Jeroen Reumkens <hello@jeroenreumkens.nl>
2022-10-12 08:39:14 +00:00
github-actions[bot]andCrowdin Bot d35ad510c9 New Crowdin translations by Github Action (#4955)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-12 09:28:17 +01:00
+9 6e834aa8eb Delete Dialog once closed, makes the entire app unresponsive. (#4906)
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: RubensRafael <rubensrafael2@live.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
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: 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: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-10-12 12:55:37 +05:30
5293db29d9 Feat Booking Limits (#4759)
* Add db relevant stuff

* Basic UI there still buggy

* This UI is hard - some progress

* Fix awful state mangament

* Fix re-ordering

* Working UI logic!

* Partical working minMax function

* Fix min max

* bookingLImits api + tests

* Moved checkBookingLimits to backend only code

* Fix httperror import

* Return busy times

* Remove avaliablity calc

* Working for everything but year

* Remove redundant + fix async forloop

* Add compatible type

* Future proof with evenTypeId filter

* Fix commonjson

* Sorting + validation + tests + passing

* Add empty test

* Move validation check  to backend

* Add bookinglimits in trpc

* Add test for undefined

* Apply suggestions from code review

Co-authored-by: Jeroen Reumkens <hello@jeroenreumkens.nl>

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

Co-authored-by: Jeroen Reumkens <hello@jeroenreumkens.nl>

* Rename value for eligiability

* Rename keyof type

* status code

* Fix toggle not toggling off

* Update apps/web/pages/v2/event-types/[type]/index.tsx

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

* Update apps/web/pages/v2/event-types/[type]/index.tsx

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

* Change back to undefined as it is working for sean. See if it fails on testapp

* Fixing test builder

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Jeroen Reumkens <hello@jeroenreumkens.nl>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>
2022-10-12 05:29:04 +00:00
Alex van Andel e772b4203e Unlicensed should return false, not error 2022-10-12 02:40:52 +01:00
Alex van Andel 6cc8fcae52 Handle double quotes situation in checkLicense function (#4961) 2022-10-11 19:34:27 -06:00
Hariom Balhara 7e5c686e26 Switch to using tRPC as router.push weirdly doesnt refetch when using middleware and that too after build (#4954) 2022-10-11 15:52:18 +00:00
2c6c768145 New Crowdin translations by Github Action (#4949)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-11 16:32:52 +01:00
Peer Richelsen a5fee47989 fixed n8n link (#4953) 2022-10-11 16:29:52 +01:00
github-actions[bot]andCrowdin Bot bceeee70e0 New Crowdin translations by Github Action (#4940)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-10-11 15:17:42 +01:00
zomars baecd923af Makes title and uid non-empty for booking references 2022-10-10 21:33:50 -06:00
zomars 1fe1130af1 If a team is deleted, it cascades to memberships 2022-10-10 19:24:46 -06:00
zomars 49805054ba Updates deprecated nonempty 2022-10-10 18:28:48 -06:00
zomars e2362d8f2a Makes team names and slug required 2022-10-10 18:28:48 -06:00
Peer Richelsen 9453c1fc3a return null if impersonating (#4939) 2022-10-10 17:39:15 -06:00
47e496fded Remove <Loader/> from app setup pages and team user availability view (#4931)
* remove loader from setup screens + zapier loading

* remove loader from team user availability

* create skeleton loader

* code clean up

* code clean up

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-10-10 17:31:16 -06:00
zomars 0289e727ad sync monorepo packages 2022-10-10 17:22:52 -06:00
zomars 04b5125121 Sync submodules 2022-10-10 17:22:36 -06:00
933 changed files with 24389 additions and 30347 deletions
+7 -4
View File
@@ -96,11 +96,10 @@ 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_TEAM_MONTHLY_PRICE_ID=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRO_PLAN_PRODUCT_ID=
STRIPE_PREMIUM_PLAN_PRODUCT_ID=
STRIPE_FREE_PLAN_PRODUCT_ID=
STRIPE_PRIVATE_KEY=
STRIPE_CLIENT_ID=
# Use for internal Public API Keys and optional
API_KEY_PREFIX=cal_
@@ -137,3 +136,7 @@ CLOSECOM_API_KEY=
# Sendgrid internal email sender
SENDGRID_API_KEY=
# Sentry
NEXT_PUBLIC_SENTRY_DSN=
SENTRY_IGNORE_API_RESOLUTION_ERROR=
-16
View File
@@ -1,16 +0,0 @@
name: Add PRs to project
on:
pull_request:
types:
- opened
jobs:
add-PR-to-project:
name: Add PR to project
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@v0.1.0
with:
project-url: https://github.com/orgs/calcom/projects/1
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
+2 -2
View File
@@ -17,6 +17,6 @@ jobs:
steps:
- uses: actions/add-to-project@v0.1.0
with:
project-url: https://github.com/orgs/calcom/projects/1
project-url: https://github.com/orgs/calcom/projects/9
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
labeled: 🐛 bug, 🎨 needs design, 🙋🏻‍♂️help wanted, 🚨 urgent
labeled: 🙋🏻‍♂️help wanted
+37
View File
@@ -0,0 +1,37 @@
# .github/workflows/preview.yml
name: Preview Environment
env:
SNAPLET_ACCESS_TOKEN: ${{ secrets.SNAPLET_ACCESS_TOKEN }}
SNAPLET_PROJECT_ID: ${{ secrets.SNAPLET_PROJECT_ID }}
VERCEL_ACCESS_TOKEN: ${{ secrets.VERCEL_ACCESS_TOKEN }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
on:
pull_request:
types: [opened, synchronize, closed]
branches:
- main
jobs:
db-preview-deploy:
if: ${{ github.event.action == 'opened' || github.event.action == 'synchronize' }}
runs-on: ubuntu-latest
steps:
- id: snaplet
uses: snaplet/action@v1
- uses: snaplet/vercel-action@v1
with:
env: |
DATABASE_URL=${{ steps.snaplet.outputs.database-url }}
db-preview-delete:
if: ${{ github.event.action == 'closed' }}
runs-on: ubuntu-latest
steps:
- uses: snaplet/action@v1
with:
delete: true
- uses: snaplet/vercel-action@v1
with:
delete: true
+1
View File
@@ -25,6 +25,7 @@ jobs:
GOOGLE_LOGIN_ENABLED: true
# CRON_API_KEY: xxx
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
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 }}
+4 -1
View File
@@ -30,7 +30,10 @@ jobs:
if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn --frozen-lockfile
- name: Lint
- name: Test Code Linting
run: yarn lint
- name: Save Code Linting Reports
run: yarn lint:report
continue-on-error: true
+11
View File
@@ -0,0 +1,11 @@
on:
push:
# Pattern matched against refs/tags
tags:
- "*" # Push events to every tag not containing /
jobs:
release:
runs-on: ubuntu-latest
steps:
- run: curl -X POST -d {} ${{ secrets.VERCEL_VERSIONED_DEPLOY_HOOK }}
+3 -2
View File
@@ -1,7 +1,7 @@
name: Unit tests
on:
push:
branches: [fixes/e2e-consolidation] # TODO: Remove this after merged in main
branches: [ feat/routing-forms/reporting ] # TODO: Remove this after merged in main
pull_request_target: # So we can test on forks
branches:
- main
@@ -21,7 +21,8 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }} # So we can test on forks
fetch-depth: 2
- run: echo 'NODE_OPTIONS="--max_old_space_size=4096"' >> $GITHUB_ENV
# Should be an 8GB machine as per https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners
- run: echo 'NODE_OPTIONS="--max_old_space_size=6144"' >> $GITHUB_ENV
- name: Use Node 16.x
uses: actions/setup-node@v3
with:
+1 -1
View File
@@ -1,4 +1,4 @@
{
"projectId": "cl4u26bwz7962859ply7ibuo43t",
"databaseUrl": "postgresql://postgres@localhost:5450/calendso"
"targetDatabaseUrl": "postgresql://postgres@localhost:5450/calendso"
}
+1 -1
View File
@@ -1,3 +1,3 @@
{
"version": "0.3.0"
"version": "0.22.3"
}
+2 -52
View File
@@ -37,7 +37,7 @@
<a href="https://hub.docker.com/r/calendso/calendso"><img src="https://img.shields.io/docker/pulls/calendso/calendso"></a>
<a href="https://twitter.com/calcom"><img src="https://img.shields.io/twitter/follow/calcom?style=flat"></a>
<a href="https://twitch.tv/calcomtv"><img src="https://img.shields.io/twitch/status/calcomtv?style=flat"></a>
<a href="https://github.com/orgs/calcom/projects/1/views/25"><img src="https://img.shields.io/badge/Help%20Wanted-Contribute-blue"></a>
<a href="https://github.com/orgs/calcom/projects/9"><img src="https://img.shields.io/badge/Help%20Wanted-Contribute-blue"></a>
<a href="https://cal.com/figma"><img src="https://img.shields.io/badge/Figma-Design%20System-blueviolet"></a>
<a href="https://calendso.slack.com/archives/C02BY67GMMW"><img src="https://img.shields.io/badge/translations-contribute-brightgreen" /></a>
<a href="https://www.contributor-covenant.org/version/1/4/code-of-conduct/ "><img src="https://img.shields.io/badge/Contributor%20Covenant-1.4-purple" /></a>
@@ -324,7 +324,7 @@ We have a list of [help wanted](https://github.com/orgs/calcom/projects/1/views/
6. In the third page (Test Users), add the Google account(s) you'll using. Make sure the details are correct on the last page of the wizard and your consent screen will be configured.
7. Now select [Credentials](https://console.cloud.google.com/apis/credentials) from the side pane and then select Create Credentials. Select the OAuth Client ID option.
8. Select Web Application as the Application Type.
9. Under Authorized redirect URI's, select Add URI and then add the URI `<Cal.com URL>/api/integrations/googlecalendar/callback` replacing Cal.com URL with the URI at which your application runs.
9. Under Authorized redirect URI's, select Add URI and then add the URI `<Cal.com URL>/api/integrations/googlecalendar/callback` and `<Cal.com URL>/api/auth/callback/google` replacing Cal.com URL with the URI at which your application runs.
10. The key will be created and you will be redirected back to the Credentials page. Select the newly generated client ID under OAuth 2.0 Client IDs.
11. Select Download JSON. Copy the contents of this file and paste the entire JSON string in the .env file as the value for GOOGLE_API_CREDENTIALS key.
@@ -354,56 +354,6 @@ following
5. Use **Application (client) ID** as the **MS_GRAPH_CLIENT_ID** attribute value in .env
6. Click **Certificates & secrets** create a new client secret and use the value as the **MS_GRAPH_CLIENT_SECRET** attribute
### Obtaining Slack Client ID and Secret and Signing Secret
To test this you will need to create a Slack app for yourself on [their apps website](https://api.slack.com/apps).
Copy and paste the app manifest below into the setting on your slack app. Be sure to replace `YOUR_DOMAIN` with your own domain or your proxy host if you're testing locally.
<details>
<summary>App Manifest</summary>
```yaml
display_information:
name: Cal.com Slack
features:
bot_user:
display_name: Cal.com Slack
always_online: false
slash_commands:
- command: /create-event
url: https://YOUR_DOMAIN/api/integrations/slackmessaging/commandHandler
description: Create an event within Cal!
should_escape: false
- command: /today
url: https://YOUR_DOMAIN/api/integrations/slackmessaging/commandHandler
description: View all your bookings for today
should_escape: false
oauth_config:
redirect_urls:
- https://YOUR_DOMAIN/api/integrations/slackmessaging/callback
scopes:
bot:
- chat:write
- commands
- chat:write.public
settings:
interactivity:
is_enabled: true
request_url: https://YOUR_DOMAIN/api/integrations/slackmessaging/interactiveHandler
message_menu_options_url: https://YOUR_DOMAIN/api/integrations/slackmessaging/interactiveHandler
org_deploy_enabled: false
socket_mode_enabled: false
token_rotation_enabled: false
```
</details>
Add the integration as normal - slack app - add. Follow the oauth flow to add it to a server.
Next make sure you have your app running `yarn dx`. Then in the slack chat type one of these commands: `/create-event` or `/today`
> NOTE: Next you will need to setup a proxy server like [ngrok](https://ngrok.com/) to allow your local host machine to be hosted on a public https server.
### Obtaining Zoom Client ID and Secret
+1 -1
View File
@@ -17,7 +17,7 @@
"license": "MIT",
"dependencies": {
"iframe-resizer-react": "^1.1.0",
"next": "^12.2.5",
"next": "^12.3.1",
"nextra": "^1.1.0",
"nextra-theme-docs": "^1.2.2",
"react": "^18.2.0",
Executable → Regular
+19 -33
View File
@@ -1,38 +1,24 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
next-env.d.ts
# production
/build
# misc
.DS_Store
*.pem
# debug
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
pnpm-debug.log*
lerna-debug.log*
# local env files
.env*.local
node_modules
dist
dist-ssr
*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
storybook-static/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+9
View File
@@ -0,0 +1,9 @@
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
i18n.use(initReactI18next).init({
resources: [],
debug: true,
});
export default i18n;
+14 -20
View File
@@ -1,34 +1,29 @@
const path = require("path");
module.exports = {
stories: ["../stories/**/*.stories.mdx", "../stories/**/*.stories.@(js|jsx|ts|tsx)"],
stories: [
"../intro.stories.mdx",
"../../../packages/ui/components/**/*.stories.mdx",
"../../../packages/ui/components/**/*.stories.@(js|jsx|ts|tsx)",
],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
"storybook-addon-designs",
"@storybook/addon-a11y",
"storybook-addon-next",
"storybook-react-i18next",
{
name: "storybook-addon-next",
options: {
nextConfigPath: path.resolve(__dirname, "../../web/next.config.js"),
},
},
],
framework: "@storybook/react",
core: {
builder: "webpack5",
},
webpackFinal: async (config) => {
/**
* Fixes font import with /
* @see https://github.com/storybookjs/storybook/issues/12844#issuecomment-867544160
*/
config.resolve.roots = [path.resolve(__dirname, "../public"), "node_modules"];
config.resolve.alias = {
...config.resolve.alias,
"@/interfaces": path.resolve(__dirname, "../interfaces"),
};
/**
* Why webpack5... Just why?
* @type {{console: boolean, process: boolean, timers: boolean, os: boolean, querystring: boolean, sys: boolean, fs: boolean, url: boolean, crypto: boolean, path: boolean, zlib: boolean, punycode: boolean, util: boolean, stream: boolean, assert: boolean, string_decoder: boolean, domain: boolean, vm: boolean, tty: boolean, http: boolean, buffer: boolean, constants: boolean, https: boolean, events: boolean}}
*/
staticDirs: ["../public"],
webpackFinal: async (config, { configType }) => {
config.resolve.fallback = {
fs: false,
assert: false,
@@ -55,7 +50,6 @@ module.exports = {
vm: false,
zlib: false,
};
return config;
},
};
@@ -1,4 +1,5 @@
<script>
window.global = window;
window.isEmbed = ()=> {
return location.search.includes("embed=")
}
@@ -1,14 +1,9 @@
import { RouterContext } from "next/dist/shared/lib/router-context";
import * as NextImage from "next/image";
import { addDecorator } from "@storybook/react";
import { I18nextProvider } from "react-i18next";
import "../styles/globals.css";
const OriginalNextImage = NextImage.default;
Object.defineProperty(NextImage, "default", {
configurable: true,
value: (props) => <OriginalNextImage {...props} unoptimized />,
});
import "../styles/storybook-styles.css";
import i18n from "./i18next";
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
@@ -18,11 +13,10 @@ export const parameters = {
date: /Date$/,
},
},
nextRouter: {
Provider: RouterContext.Provider,
},
};
addDecorator((storyFn) => <I18nextProvider i18n={i18n}>{storyFn()}</I18nextProvider>);
window.getEmbedNamespace = () => {
const url = new URL(document.URL);
const namespace = url.searchParams.get("embed");
-21
View File
@@ -1,21 +0,0 @@
# Storybook & UI
Storybook is home to all of our commonly used components. We use this app to visually show all components that we have within the project + automatic type documentation generated for each component.
All changes to storybook/ui must require a visual/code review.
- Visual reviews happen on a tool called [Chormatic](http://chromatic.com/) and these reviews will happen by our product team.
- Code reviews happen as normal on any changes to storybook/ui designs
## Deployment
To deploy this project run
```bash
cd apps/storybook
```
```bash
yarn dev
```
@@ -0,0 +1,23 @@
import { ArgsTable } from "@storybook/addon-docs";
import { SortType } from "@storybook/components";
import { PropDescriptor } from "@storybook/store";
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore storybook addon types component as any so we have to do
type Component = any;
type BaseProps = {
include?: PropDescriptor;
exclude?: PropDescriptor;
sort?: SortType;
};
type OfProps = BaseProps & {
of: "." | "^" | Component;
};
export function CustomArgsTable({ of, sort }: OfProps) {
return (
<div className="custom-args-wrapper">
<ArgsTable of={of} sort={sort} />
</div>
);
}
+32
View File
@@ -0,0 +1,32 @@
import { classNames } from "@calcom/lib";
interface ExampleProps {
children: React.ReactNode;
title: string;
isFullWidth?: boolean;
}
export const Example = ({ children, title, isFullWidth = false }: ExampleProps) => {
return (
<div className={classNames("examples-item", isFullWidth && "w-full")}>
<span className={classNames("examples-item-title", isFullWidth && "mb-0 mt-2")}>{title}</span>
<div className="examples-item-content">{children}</div>
</div>
);
};
interface ExamplesProps {
children: React.ReactNode;
title: string;
footnote?: React.ReactNode;
dark?: boolean;
}
export const Examples = ({ children, title, footnote = null, dark }: ExamplesProps) => {
return (
<div className={classNames("examples", dark && "dark")}>
<h2 className="examples-title">{title}</h2>
<div className="examples-content">{children}</div>
{!!footnote && <div className="examples-footnote">{footnote}</div>}
</div>
);
};
+5
View File
@@ -0,0 +1,5 @@
export const Note = ({ children }: { children: React.ReactNode }) => (
<div className="story-note">
<div>{children}</div>
</div>
);
+21
View File
@@ -0,0 +1,21 @@
export const Title = ({
title,
suffix,
subtitle,
offset,
}: {
title: string;
suffix?: string;
subtitle?: string;
offset?: boolean;
}) => {
return (
<div className={`story-title ${offset && "offset"}`}>
<h1>
{title}
{suffix && <span>{suffix}</span>}
</h1>
{subtitle && <p>{subtitle}</p>}
</div>
);
};
@@ -0,0 +1,80 @@
import React, { ReactElement, ReactNode } from "react";
import { classNames } from "@calcom/lib";
export function VariantsTable({
children,
titles,
isDark,
columnMinWidth = 150,
}: {
children: ReactElement<RowProps> | ReactElement<RowProps>[];
titles: string[];
isDark?: boolean;
// Mainly useful on mobile, so components don't get squeesed
columnMinWidth?: number;
}) {
const columns = React.Children.toArray(children) as ReactElement<RowProps>[];
return (
<div
className={classNames(
isDark &&
"relative py-8 before:absolute before:left-0 before:top-0 before:block before:h-full before:w-screen before:bg-[#22252A]"
)}>
<div className="z-1 relative mx-auto w-full max-w-[1200px] overflow-auto pr-8 pt-6">
<table>
<RowTitles titles={titles} />
{columns.map((column) => (
<tr className="p-2 pr-6 pb-6" key={column.props.variant}>
<th
className="p-2 pr-6 pb-6 text-left text-sm font-normal text-[#8F8F8F]"
key={column.props.variant}>
{column.props.variant}
</th>
{React.Children.count(column.props.children) &&
React.Children.map(column.props.children, (cell) => (
<td className="p-2 pr-6 pb-6" style={{ minWidth: `${columnMinWidth}px` }}>
{cell}
</td>
))}
</tr>
))}
</table>
</div>
{!isDark && (
<div data-mode="dark">
<VariantsTable titles={titles} isDark columnMinWidth={columnMinWidth}>
{children}
</VariantsTable>
</div>
)}
</div>
);
}
interface RowProps {
variant: string;
children: ReactNode;
}
/**
* There are two reasons we have this "empty" wrapper component:
* 1. In order to have an isolate group per variant, which we iterate through in the table component.
* 2. To have a way to pass the variant.
*/
export function VariantRow({ children }: RowProps) {
return <>{children}</>;
}
export function RowTitles({ titles }: { titles: string[] }) {
return (
<tr>
<th className="p-2 pr-6 pb-6 text-left text-sm font-normal text-[#8F8F8F]" />
{titles.map((title) => (
<th className="p-2 pr-6 pb-6 text-left text-sm font-normal text-[#8F8F8F]" key={title}>
{title}
</th>
))}
</tr>
);
}
+5
View File
@@ -0,0 +1,5 @@
export * from "./Examples";
export * from "./Note";
export * from "./VariantsTable";
export * from "./Title";
export * from "./CustomArgsTable";
+11
View File
@@ -0,0 +1,11 @@
import { Meta } from '@storybook/addon-docs';
<Meta title="Introduction" />
<div className="text-center flex flex-col items-center">
<h1 style={{marginBottom: '24px', marginTop: '36px'}}>Welcome to Cal.com UI</h1>
<p>This is the beginning of our storybook impovments to match Figma as close as possible. Like our Figma library, we will be adding more components as we go along.</p>
<p>Our <a href="https://www.figma.com/file/9MOufQNLtdkpnDucmNX10R/%E2%9D%96-Cal-DS" target="_blank">Figma</a> library is avalible for anyone to view and use. If you have any questions or concerns, please reach out to the design team.</p>
</div>
<img src="/sb-cover.jpg" />
-7
View File
@@ -1,7 +0,0 @@
const withTM = require("next-transpile-modules")(["@calcom/dayjs", "@calcom/ui"]);
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
};
module.exports = withTM(nextConfig);
+28 -40
View File
@@ -1,18 +1,13 @@
{
"name": "@calcom/storybook",
"version": "0.1.0",
"private": true,
"version": "0.0.0",
"scripts": {
"dev:next": "next dev",
"build:next": "next build",
"start": "next start",
"lint": "eslint . --ignore-path .gitignore",
"lint:report": "eslint . --format json --output-file ../../lint-results/storybook.json",
"dev": "start-storybook -p 6006",
"build": "build-storybook",
"chromatic": "npx chromatic --project-token=56fadc9ab496"
"build": "build-storybook"
},
"dependencies": {
"@calcom/config": "*",
"@calcom/dayjs": "*",
"@calcom/ui": "*",
"@radix-ui/react-avatar": "^1.0.0",
@@ -25,42 +20,35 @@
"@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",
"next": "^12.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-feather": "^2.0.10",
"react-hot-toast": "^2.3.0"
"react-dom": "^18.2.0"
},
"devDependencies": {
"@babel/core": "^7.18.10",
"@calcom/config": "*",
"@storybook/addon-a11y": "^6.5.10",
"@storybook/addon-actions": "^6.5.10",
"@storybook/addon-essentials": "^6.5.10",
"@storybook/addon-interactions": "^6.5.10",
"@storybook/addon-links": "^6.5.10",
"@storybook/addon-postcss": "^2.0.0",
"@storybook/builder-vite": "^0.2.2",
"@storybook/builder-webpack5": "^6.5.10",
"@storybook/manager-webpack5": "^6.5.10",
"@storybook/react": "^6.5.10",
"@tailwindcss/line-clamp": "^0.4.0",
"@types/node": "16.9.1",
"@babel/core": "^7.19.6",
"@storybook/addon-actions": "^6.5.13",
"@storybook/addon-essentials": "^6.5.13",
"@storybook/addon-interactions": "^6.5.13",
"@storybook/addon-links": "^6.5.13",
"@storybook/builder-vite": "^0.2.4",
"@storybook/builder-webpack5": "^6.5.13",
"@storybook/manager-webpack5": "^6.5.13",
"@storybook/react": "^6.5.13",
"@storybook/testing-library": "^0.0.13",
"@types/react": "^18.0.17",
"@types/react-dom": "18.0.4",
"autoprefixer": "^10.4.8",
"@types/react-dom": "^18.0.6",
"@vitejs/plugin-react": "^2.1.0",
"autoprefixer": "^10.4.12",
"babel-loader": "^8.2.5",
"chromatic": "^6.7.4",
"eslint": "^8.22.0",
"postcss": "^8.4.16",
"postcss-loader": "^7.0.0",
"fs": "^0.0.1-security",
"postcss": "^8.4.18",
"postcss-pseudo-companion-classes": "^0.1.1",
"rollup-plugin-polyfill-node": "^0.10.2",
"storybook-addon-designs": "^6.3.1",
"storybook-addon-next": "^1.6.7",
"tailwindcss": "^3.1.8",
"tsconfig-paths-webpack-plugin": "^3.5.2",
"typescript": "^4.7.4"
},
"readme": "ERROR: No README data found!",
"_id": "@calcom/storybook@0.1.0"
"storybook-addon-next": "^1.6.9",
"storybook-react-i18next": "^1.1.2",
"tailwindcss": "^3.2.1",
"typescript": "^4.7.4",
"vite": "^2.9.15"
}
}
-9
View File
@@ -1,9 +0,0 @@
import type { AppProps } from "next/app";
import "../styles/globals.css";
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
export default MyApp;
-63
View File
@@ -1,63 +0,0 @@
import type { NextPage } from "next";
import Head from "next/head";
import Image from "next/image";
const Home: NextPage = () => {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1 className={styles.title}>
Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>
<p className={styles.description}>
Get started by editing <code className={styles.code}>pages/index.tsx</code>
</p>
<div className={styles.grid}>
<a href="https://nextjs.org/docs" className={styles.card}>
<h2>Documentation &rarr;</h2>
<p>Find in-depth information about Next.js features and API.</p>
</a>
<a href="https://nextjs.org/learn" className={styles.card}>
<h2>Learn &rarr;</h2>
<p>Learn about Next.js in an interactive course with quizzes!</p>
</a>
<a href="https://github.com/vercel/next.js/tree/canary/examples" className={styles.card}>
<h2>Examples &rarr;</h2>
<p>Discover and deploy boilerplate example Next.js projects.</p>
</a>
<a
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}>
<h2>Deploy &rarr;</h2>
<p>Instantly deploy your Next.js site to a public URL with Vercel.</p>
</a>
</div>
</main>
<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer">
Powered by{" "}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a>
</footer>
</div>
);
};
export default Home;
+16
View File
@@ -2,5 +2,21 @@ module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
/**
* We use this postcss plugin to create custom css classes for
* any pseudo classes (eg hover, focus) mentioned below.
* This way you can eg show a hover state in Storybook by adding
* this classname .sb-pseudo--hover to the item.
*
* These styles will only be added in storybook, and will NOT
* end up in the final css bundle of apps using the components.
*/
"postcss-pseudo-companion-classes": {
prefix: "sb-pseudo--",
// We have to keep a restrictTo list here, because otherwise
// this library will have issues processing tailwind's \: prefixed classes
// and start adding dots everywhere, breaking this functionality.
restrictTo: [":hover", ":focus"],
},
},
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 715 KiB

-4
View File
@@ -1,4 +0,0 @@
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

-26
View File
@@ -1,26 +0,0 @@
import { ComponentMeta } from "@storybook/react";
import Avatar from "@calcom/ui/v2/core/Avatar";
export default {
title: "Avatar",
component: Avatar,
} as ComponentMeta<typeof Avatar>;
export const Default = () => {
return (
<>
<Avatar size="sm" alt="Avatar Story" gravatarFallbackMd5="Ui@CAL.com" />
<Avatar size="lg" alt="Avatar Story" gravatarFallbackMd5="Ui@CAL.com" />
</>
);
};
export const Accepted = () => {
return (
<>
<Avatar size="sm" alt="Avatar Story" gravatarFallbackMd5="Ui@CAL.com" accepted />
<Avatar size="lg" alt="Avatar Story" gravatarFallbackMd5="Ui@CAL.com" accepted />
</>
);
};
@@ -1,60 +0,0 @@
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { ComponentMeta } from "@storybook/react";
import AvatarGroup from "@calcom/ui/v2/core/AvatarGroup";
export default {
title: "Avatar/Group",
component: AvatarGroup,
} as ComponentMeta<typeof AvatarGroup>;
const IMAGES = [
{
image: "https://cal.com/stakeholder/peer.jpg",
alt: "Peer",
title: "Peer Richelsen",
},
{
image: "https://cal.com/stakeholder/bailey.jpg",
alt: "Bailey",
title: "Bailey Pumfleet",
},
{
image: "https://cal.com/stakeholder/alex-van-andel.jpg",
alt: "Alex",
title: "Alex Van Andel",
},
{
image: "https://cal.com/stakeholder/ciaran.jpg",
alt: "Ciarán",
title: "Ciarán Hanrahan",
},
{
image: "https://cal.com/stakeholder/peer.jpg",
alt: "Peer",
title: "Peer Richelsen",
},
{
image: "https://cal.com/stakeholder/bailey.jpg",
alt: "Bailey",
title: "Bailey Pumfleet",
},
{
image: "https://cal.com/stakeholder/alex-van-andel.jpg",
alt: "Alex",
title: "Alex Van Andel",
},
{
image: "https://cal.com/stakeholder/ciaran.jpg",
alt: "Ciarán",
title: "Ciarán Hanrahan",
},
];
export const Default = () => {
return (
<TooltipProvider>
<AvatarGroup size="lg" items={IMAGES} />
</TooltipProvider>
);
};
-58
View File
@@ -1,58 +0,0 @@
import { ComponentMeta } from "@storybook/react";
import { Bell } from "react-feather";
import Badge from "@calcom/ui/v2/core/Badge";
export default {
title: "Badge",
component: Badge,
} as ComponentMeta<typeof Badge>;
export const All = () => (
<div className="">
<h1>Default</h1>
<div className="mb-4 flex space-x-2">
<Badge variant="gray">Badge</Badge>
<Badge variant="red">Badge</Badge>
<Badge variant="green">Badge</Badge>
<Badge variant="orange">Badge</Badge>
<Badge variant="blue">Badge</Badge>
</div>
<h1>Icons</h1>
<div className="mb-4 flex space-x-2">
<Badge variant="gray" StartIcon={Bell}>
Badge
</Badge>
<Badge variant="red" StartIcon={Bell}>
Badge
</Badge>
<Badge variant="green" StartIcon={Bell}>
Badge
</Badge>
<Badge variant="orange" StartIcon={Bell}>
Badge
</Badge>
<Badge variant="blue" StartIcon={Bell}>
Badge
</Badge>
</div>
<h1>Large</h1>
<div className="flex space-x-2">
<Badge variant="gray" size="lg">
Badge
</Badge>
<Badge variant="red" size="lg">
Badge
</Badge>
<Badge variant="green" size="lg">
Badge
</Badge>
<Badge variant="orange" size="lg">
Badge
</Badge>
<Badge variant="blue" size="lg">
Badge
</Badge>
</div>
</div>
);
-46
View File
@@ -1,46 +0,0 @@
import { ComponentStory, ComponentMeta } from "@storybook/react";
import { Info } from "react-feather";
import Banner from "@calcom/ui/v2/core/banner";
export default {
title: "Banner",
component: Banner,
} as ComponentMeta<typeof Banner>;
export const Default = () => {
return (
<Banner
variant="neutral"
title="Summarise what happened"
description="Describe what can be done about it here."
Icon={Info}
onDismiss={() => console.log("dismissed")}
/>
);
};
export const Warning = () => {
return (
<Banner
variant="warning"
title="Summarise what happened"
description="Describe what can be done about it here."
Icon={Info}
onDismiss={() => console.log("dismissed")}
/>
);
};
export const Error = () => {
return (
<Banner
variant="error"
title="Summarise what happened"
description="Describe what can be done about it here."
errorMessage="Event creation failed"
Icon={Info}
onDismiss={() => console.log("dismissed")}
/>
);
};
@@ -1,22 +0,0 @@
import { ComponentMeta } from "@storybook/react";
import Breadcrumb, { BreadcrumbItem } from "@calcom/ui/v2/core/Breadcrumb";
export default {
title: "Breadcrumbs",
component: Breadcrumb,
} as ComponentMeta<typeof Breadcrumb>;
export const Default = () => (
<Breadcrumb>
<BreadcrumbItem href="/">Home</BreadcrumbItem>
<BreadcrumbItem href="/">Test</BreadcrumbItem>
</Breadcrumb>
);
Default.parameters = {
nextRouter: {
path: "/test",
asPath: "/test",
},
};
-98
View File
@@ -1,98 +0,0 @@
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Trash2 } from "react-feather";
import Button from "@calcom/ui/v2/core/Button";
export default {
title: "Button",
component: Button,
argTypes: {
color: {
options: ["primary", "secondary", "minimal", "destructive"],
control: { type: "select" },
},
disabled: {
options: [true, false],
control: { type: "boolean" },
},
loading: {
options: [true, false],
control: { type: "boolean" },
},
size: {
options: ["base", "lg", "icon"],
control: { type: "radio" },
},
},
} as ComponentMeta<typeof Button>;
const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;
export const All = () => (
<div>
<TooltipProvider>
<h1>Primary</h1>
<div className="flex space-x-2">
<Button aria-label="Button Text">Button Text</Button>
<Button disabled aria-label="Button Text">
Button Text
</Button>
</div>
<h1>Secondary</h1>
<div className="flex space-x-2">
<Button color="secondary" aria-label="Button Text">
Button Text
</Button>
<Button disabled color="secondary" aria-label="Button Text">
Button Text
</Button>
<Button size="icon" color="secondary" StartIcon={Trash2} aria-label="Button Text" />
</div>
<h1>Minimal</h1>
<div className="flex">
<Button color="minimal" aria-label="Button Text">
Button Text
</Button>
<Button disabled color="minimal" aria-label="Button Text">
Button Text
</Button>
<Button size="icon" color="minimal" StartIcon={Trash2} aria-label="Button Text" />
</div>
<h1>Destructive</h1>
<Button size="icon" color="destructive" StartIcon={Trash2} aria-label="Button Text" />
<h1>Tooltip</h1>
<Button
tooltip="Deletes EventTypes"
size="icon"
color="destructive"
StartIcon={Trash2}
aria-label="Button Text"
/>
</TooltipProvider>
</div>
);
export const Default = Template.bind({});
Default.args = {
color: "primary",
children: "Button Text",
};
export const Disabled = Template.bind({});
Disabled.args = {
...Default.args,
disabled: true,
};
export const Loading = Template.bind({});
Loading.args = {
...Default.args,
loading: true,
};
export const Icon = Template.bind({});
Icon.args = {
color: "secondary",
StartIcon: Trash2,
size: "icon",
};
@@ -1,41 +0,0 @@
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { ComponentMeta } from "@storybook/react";
import { ArrowLeft, ArrowRight, Clipboard, Navigation2, Trash2 } from "react-feather";
import Button from "@calcom/ui/v2/core/Button";
import ButtonGroup from "@calcom/ui/v2/core/ButtonGroup";
export default {
title: "Button Group",
component: ButtonGroup,
decorators: [
(Story) => (
<TooltipProvider>
<Story />
</TooltipProvider>
),
],
} as ComponentMeta<typeof ButtonGroup>;
export const Default = () => (
<ButtonGroup>
<Button StartIcon={Trash2} size="icon" color="secondary" />
<Button StartIcon={Navigation2} size="icon" color="secondary" />
<Button StartIcon={Clipboard} size="icon" color="secondary" />
</ButtonGroup>
);
export const Combined = () => (
<div className="flex flex-col space-y-2">
<ButtonGroup 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" />
<Button StartIcon={ArrowRight} size="icon" color="secondary" />
<Button StartIcon={ArrowRight} href="/" size="icon" color="secondary" />
</ButtonGroup>
</div>
);
-79
View File
@@ -1,79 +0,0 @@
import { ComponentMeta } from "@storybook/react";
import { useState } from "react";
import Card, { BaseCardProps } from "@calcom/ui/v2/core/Card";
export default {
title: "Cards",
component: Card,
} as ComponentMeta<typeof Card>;
const profileProps: BaseCardProps[] = [
{
variant: "ProfileCard",
title: "Alex Fisher",
image:
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80",
description: "Co-founder at Attio. Book a meeting to discuss enterprise deals.",
},
{
variant: "ProfileCard",
title: "Julian Wan",
image:
"https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80",
description: "Co-founder at Attio. Book a meeting to discuss enterprise deals.",
},
];
export const Profile = () => {
return (
<div className="flex w-full space-x-2 bg-gray-200 p-4">
<Card {...profileProps[0]} />
<Card {...profileProps[1]} />
</div>
);
};
const appStoreProps: BaseCardProps = {
variant: "AppStore",
title: "Giphy",
image: "https://app.cal.com/api/app-store/giphy/icon.svg",
description: "Add GIFs to spice up your booking confirmations.",
actionButton: {
href: "#",
child: "View Details",
},
};
export const AppStore = () => {
return (
<div className="w-full bg-gray-200 p-4">
<Card {...appStoreProps} />
</div>
);
};
const sidebarCardProps: BaseCardProps = {
variant: "SidebarCard",
thumbnailUrl: "https://img.youtube.com/vi/60HJt8DOVNo/0.jpg",
mediaLink: "https://www.youtube.com/watch?v=60HJt8DOVNo",
title: "Dynamic boooking links",
description: "Booking link that allows people to quickly schedule meetings.",
learnMore: {
href: "https://cal.com/blog/cal-v-1-9",
text: "Learn more",
},
};
export const SidebarCard = () => {
const [visible, setVisible] = useState(true); // save state in localStorage, cookie or db
return (
<header className="w-full max-w-[225px] bg-gray-100 p-3">
{visible && (
<div>
<Card {...sidebarCardProps} actionButton={{ onClick: () => setVisible(false), child: "Dismiss" }} />
</div>
)}
</header>
);
};
@@ -1,31 +0,0 @@
import { ComponentMeta } from "@storybook/react";
import Checkbox from "@calcom/ui/v2/core/form/Checkbox";
export default {
title: "Checkbox",
component: Checkbox,
} as ComponentMeta<typeof Checkbox>;
export const All = () => (
<div className="flex flex-col space-y-2">
<Checkbox label="Default" description="Toggle on and off something" />
<Checkbox label="Error" description="Toggle on and off something" error />
<Checkbox label="Disabled" description="Toggle on and off something" disabled />
<Checkbox label="Disabled Checked" description="Toggle on and off something" checked disabled />
<hr />
<Checkbox description="Default" descriptionAsLabel />
<Checkbox description="Error" descriptionAsLabel error />
<Checkbox description="Disabled" descriptionAsLabel disabled />
<Checkbox description="Disabled" descriptionAsLabel disabled checked />
</div>
);
export const CheckboxField = () => <Checkbox description="Default" descriptionAsLabel />;
export const CheckboxError = () => <Checkbox description="Error" descriptionAsLabel />;
export const CheckboxDisabled = () => (
<>
<Checkbox description="Disabled" descriptionAsLabel disabled />
<Checkbox description="Disabled" descriptionAsLabel disabled checked />
</>
);
-208
View File
@@ -1,208 +0,0 @@
import { useState } from "react";
import ColorPicker from "@calcom/ui/v2/core/colorpicker";
// eslint-disable-next-line import/no-anonymous-default-export
export default {
title: "Colors",
component: ColorPicker,
};
const COLORS = {
brand: {
50: "#f3f3f4",
100: "#e7e8e9",
200: "#c4c5c9",
300: "#a0a3a9",
400: "#585d68",
500: "#111827", // Brand color
600: "#0f1623",
700: "#0d121d",
800: "#0a0e17",
900: "#080c13",
},
gray: {
50: "#F8F8F8",
100: "#F5F5F5",
200: "#E1E1E1",
300: "#CFCFCF",
400: "#ACACAC",
500: "#888888",
600: "#494949",
700: "#3E3E3E",
800: "#313131",
900: "#292929",
},
neutral: {
50: "#F8F8F8",
100: "#F5F5F5",
200: "#E1E1E1",
300: "#CFCFCF",
400: "#ACACAC",
500: "#888888",
600: "#494949",
700: "#3E3E3E",
800: "#313131",
900: "#292929",
},
primary: {
50: "#F4F4F4",
100: "#E8E8E8",
200: "#C6C6C6",
300: "#A3A3A3",
400: "#5F5F5F",
500: "#1A1A1A",
600: "#171717",
700: "#141414",
800: "#101010",
900: "#0D0D0D",
},
secondary: {
50: "#F5F8F7",
100: "#EBF0F0",
200: "#CDDAD9",
300: "#AEC4C2",
400: "#729894",
500: "#356C66",
600: "#30615C",
700: "#28514D",
800: "#20413D",
900: "#223B41",
},
red: {
50: "#FEF2F2",
100: "#FEE2E2",
200: "#FECACA",
300: "#FCA5A5",
400: "#F87171",
500: "#EF4444",
600: "#DC2626",
700: "#B91C1C",
800: "#991B1B",
900: "#7F1D1D",
},
orange: {
50: "#FFF7ED",
100: "#FFEDD5",
200: "#FED7AA",
300: "#FDBA74",
400: "#FB923C",
500: "#F97316",
600: "#EA580C",
700: "#C2410C",
800: "#9A3412",
900: "#7C2D12",
},
green: {
50: "#ECFDF5",
100: "#D1FAE5",
200: "#A7F3D0",
300: "#6EE7B7",
400: "#34D399",
500: "#10B981",
600: "#059669",
700: "#047857",
800: "#065F46",
900: "#064E3B",
},
};
export const All = () => {
return (
<div className="w-full">
<div>
{Object.keys(COLORS.brand).map((color) => (
<div className="flex flex-row space-x-2" key={COLORS.brand[color]}>
<div className="w-full">Brand</div>
<div className="w-full">{color}</div>
<div className="w-full">{COLORS.brand[color]}</div>
<div
style={{
backgroundColor: COLORS.brand[color],
width: "100%",
height: "32px",
}}
/>
</div>
))}
<hr />
{Object.keys(COLORS.gray).map((color) => (
<div className="flex flex-row space-x-2" key={COLORS.gray[color]}>
<div className="w-full">gray</div>
<div className="w-full">{color}</div>
<div className="w-full">{COLORS.gray[color]}</div>
<div
style={{
backgroundColor: COLORS.gray[color],
width: "100%",
height: "32px",
}}
/>
</div>
))}
<hr />
{Object.keys(COLORS.secondary).map((color) => (
<div className="flex flex-row space-x-2" key={COLORS.secondary[color]}>
<div className="w-full">secondary</div>
<div className="w-full">{color}</div>
<div className="w-full">{COLORS.secondary[color]}</div>
<div
style={{
backgroundColor: COLORS.secondary[color],
width: "100%",
height: "32px",
}}
/>
</div>
))}
<hr />
{Object.keys(COLORS.red).map((color) => (
<div className="flex flex-row space-x-2" key={COLORS.red[color]}>
<div className="w-full">red</div>
<div className="w-full">{color}</div>
<div className="w-full">{COLORS.red[color]}</div>
<div
style={{
backgroundColor: COLORS.red[color],
width: "100%",
height: "32px",
}}
/>
</div>
))}
<hr />
{Object.keys(COLORS.orange).map((color) => (
<div className="flex flex-row space-x-2" key={COLORS.orange[color]}>
<div className="w-full">orange</div>
<div className="w-full">{color}</div>
<div className="w-full">{COLORS.orange[color]}</div>
<div
style={{
backgroundColor: COLORS.orange[color],
width: "100%",
height: "32px",
}}
/>
</div>
))}
<hr />
{Object.keys(COLORS.green).map((color) => (
<div className="flex flex-row space-x-2" key={COLORS.green[color]}>
<div className="w-full">green</div>
<div className="w-full">{color}</div>
<div className="w-full">{COLORS.green[color]}</div>
<div
style={{
backgroundColor: COLORS.green[color],
width: "100%",
height: "32px",
}}
/>
</div>
))}
</div>
</div>
);
};
export const ColorPickerComponent = () => {
const [color, setColor] = useState("3B82F6");
return <ColorPicker defaultValue={color} onChange={(val) => setColor(val)} />;
};
@@ -1,17 +0,0 @@
import { useState } from "react";
import DatePicker from "@calcom/ui/v2/modules/booker/DatePicker";
export default {
title: "Datepicker",
component: DatePicker,
};
export const Default = () => {
const [selected, setSelected] = useState<Date>(new Date());
return (
<div style={{ width: "455px" }}>
<DatePicker selected={selected} onChange={setSelected} locale="en" />
</div>
);
};
@@ -1,45 +0,0 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import "@wojtekmaj/react-daterange-picker/dist/DateRangePicker.css";
import { useState } from "react";
import DateRangePicker from "@calcom/ui/v2/core/form/date-range-picker/DateRangePicker";
import "@calcom/ui/v2/core/form/date-range-picker/styles.css";
function getISOLocalDate(date) {
const year = padStart(date.getFullYear(), 4);
const month = padStart(date.getMonth() + 1);
const day = padStart(date.getDate());
return [`${year}-${month}-${day}`];
}
function padStart(num, val = 2) {
const numStr = `${num}`;
if (numStr.length >= val) {
return num;
}
return `0000${numStr}`.slice(-val);
}
export default {
title: "Date Range Picker",
component: DateRangePicker,
} as ComponentMeta<typeof DateRangePicker>;
export const Default: ComponentStory<typeof DateRangePicker> = () => {
const [startDate, setStartDate] = useState<Date>(new Date());
const [endDate, setEndDate] = useState<Date>(new Date());
return (
<div>
<DateRangePicker
startDate={getISOLocalDate(startDate) as unknown as Date}
endDate={getISOLocalDate(endDate) as unknown as Date}
onDatesChange={({ startDate, endDate }) => {
setStartDate(startDate);
setEndDate(endDate);
}}
/>
</div>
);
};
@@ -1,26 +0,0 @@
import { ComponentMeta } from "@storybook/react";
import { Bell } from "react-feather";
import EmptyScreen from "@calcom/ui/v2/core/EmptyScreen";
export default {
title: "pattern/Empty Screen",
component: EmptyScreen,
decorators: [
(Story) => (
<div className="flex items-center justify-center">
<Story />
</div>
),
],
} as ComponentMeta<typeof EmptyScreen>;
export const Default = () => (
<EmptyScreen
Icon={Bell}
headline="Empty State Header"
description="Ullamco dolor nulla sint nulla occaecat aliquip id elit fugiat et excepteur magna. Nisi tempor anim do tempor irure fugiat ad occaecat. Mollit ea eiusmod pariatur sunt deserunt eu eiusmod. Sit reprehenderit "
buttonText="Veniam ut ipsum"
buttonOnClick={() => console.log("Button Clicked")}
/>
);
@@ -1,27 +0,0 @@
import { ComponentMeta } from "@storybook/react";
import { useState } from "react";
import FormStep from "@calcom/ui/v2/core/form/FormStep";
export default {
title: "Form Step",
component: FormStep,
} as ComponentMeta<typeof FormStep>;
export const Default = () => {
const STEPS = 4;
const [currentStep, setCurrentStep] = useState(1);
return (
<div className="flex flex-col items-center justify-center space-y-14 p-20">
<div className="w-1/2">
<FormStep steps={STEPS} currentStep={currentStep} />
<div className="flex space-x-2 pt-4">
<button onClick={() => currentStep - 1 > 0 && setCurrentStep((old) => old - 1)}>Previous</button>
<button onClick={() => currentStep + 1 < STEPS + 1 && setCurrentStep((old) => old + 1)}>
Next
</button>
</div>
</div>
</div>
);
};
@@ -1,39 +0,0 @@
// Disabling until we figure out what is happening with the RouterMock.
import { ComponentMeta } from "@storybook/react";
import { HorizontalTabItemProps } from "@calcom/ui/v2/core/navigation/tabs/HorizontalTabItem";
import HorizontalTabs from "@calcom/ui/v2/core/navigation/tabs/HorizontalTabs";
export default {
title: "Horizontal Tabs",
component: HorizontalTabs,
} as ComponentMeta<typeof HorizontalTabs>;
const HorizontalTabsPropsDefault: HorizontalTabItemProps[] = [
{
name: "Tab One",
href: "/tab-one",
},
{
name: "Tab Two",
href: "/tab-two",
},
{
name: "Tab Disabled",
href: "/tab-disabled",
disabled: true,
},
];
export const Default = () => (
<div className="w-full p-4">
<HorizontalTabs tabs={HorizontalTabsPropsDefault} />
</div>
);
Default.parameters = {
nextRouter: {
path: "/[page]",
asPath: "/tab-one",
},
};
-75
View File
@@ -1,75 +0,0 @@
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Copy } from "react-feather";
import { TextAreaField, TextField, PasswordField } from "@calcom/ui/v2/core/form/fields";
import DatePicker from "@calcom/ui/v2/modules/booker/DatePicker";
export default {
title: "Inputs",
component: TextField,
argTypes: {
disabled: {
options: [false, true],
},
},
} as ComponentMeta<typeof TextField>;
const TextInputTemplate: ComponentStory<typeof TextField> = (args) => <TextField {...args} />;
// name="demo" label="Demo Label" hint="Hint text"
export const TextInput = TextInputTemplate.bind({});
TextInput.args = {
name: "demo",
label: "Demo Label",
hint: "Hint Text",
disabled: false,
};
export const TextInputPrefix = TextInputTemplate.bind({});
TextInputPrefix.args = {
name: "demo",
label: "Demo Label",
hint: "Hint Text",
addOnLeading: "https://",
disabled: false,
};
export const TextInputSuffix = TextInputTemplate.bind({});
TextInputSuffix.args = {
name: "demo",
label: "Demo Label",
hint: "Hint Text",
addOnSuffix: "Minutes",
disabled: false,
};
export const TextInputPrefixIcon = TextInputTemplate.bind({});
TextInputPrefixIcon.args = {
name: "demo",
label: "Demo Label",
hint: "Hint Text",
addOnFilled: false,
addOnSuffix: <Copy />,
disabled: false,
};
export const TextInputSuffixIcon = TextInputTemplate.bind({});
TextInputSuffixIcon.args = {
name: "demo",
label: "Demo Label",
hint: "Hint Text",
addOnFilled: false,
addOnLeading: <Copy />,
disabled: false,
};
export const TextAreaInput: ComponentStory<typeof TextAreaField> = () => (
<TextAreaField name="Text-area-input" label="Text Area" />
);
export const DatePickerInput: ComponentStory<typeof DatePicker> = () => <DatePicker date={new Date()} />;
export const PasswordInput: ComponentStory<typeof PasswordField> = () => (
<TooltipProvider>
<PasswordField />
</TooltipProvider>
);
-50
View File
@@ -1,50 +0,0 @@
import { useState } from "react";
import { Info } from "react-feather";
import { Button, Dialog, DialogContent, DialogTrigger, TextField } from "@calcom/ui/v2";
export default {
title: "pattern/Modal",
component: Dialog,
};
export const Creation = () => {
const [open, setOpen] = useState(false);
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button onClick={() => setOpen(true)}>Open Modal</Button>
</DialogTrigger>
<DialogContent
title="Header"
description="Optional Description"
type="creation"
actionText="Create"
actionOnClick={() => setOpen(false)}>
<TextField name="Label" />
<TextField name="Label" />
<TextField name="Label" />
<TextField name="Label" />
</DialogContent>
</Dialog>
);
};
export const Confirmation = () => {
const [open, setOpen] = useState(false);
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button>Open Modal</Button>
</DialogTrigger>
<DialogContent
title="Header"
description="Optional Description"
type="confirmation"
actionText="Confirm"
Icon={Info}
actionOnClick={() => setOpen(false)}
/>
</Dialog>
);
};
@@ -1,24 +0,0 @@
import { ComponentMeta } from "@storybook/react";
import { Toaster } from "react-hot-toast";
import { Button, showToast } from "@calcom/ui/v2";
export default {
title: "Notifcations-Toasts",
decorators: [
(Story) => (
<>
<Story />
<Toaster />
</>
),
],
} as ComponentMeta<typeof All>; // We have to fake this type as the story for this component isn't really a component.
export const All = () => (
<div className="flex space-x-2">
<Button onClick={() => showToast("This is a Neutral toast", "warning")}>Neutral</Button>
<Button onClick={() => showToast("This is a Success toast", "success")}>Success</Button>
<Button onClick={() => showToast("This is a Error toast", "error")}>Error</Button>
</div>
);
@@ -1,52 +0,0 @@
import { ComponentMeta } from "@storybook/react";
import { Search } from "react-feather";
import { Button, TextField } from "@calcom/ui/v2";
import PageHeader from "@calcom/ui/v2/core/PageHeader";
export default {
title: "pattern/Page Header",
component: PageHeader,
decorators: [
(Story) => (
<div className="">
<Story />
</div>
),
],
} as ComponentMeta<typeof PageHeader>;
export const Default = () => (
<PageHeader title="Title" description="Some description about the header above" />
);
export const ButtonRight = () => (
<PageHeader
title="Title"
description="Some description about the header above"
rightAlignedComponent={<Button>Button Text</Button>}
/>
);
export const ComingSoon = () => (
<PageHeader
title="Title"
description="Some description about the header above"
badgeText="Coming Soon"
badgeVariant="gray"
/>
);
export const SearchInstalledApps = () => (
<PageHeader
title="Search booking"
description="See upcoming and past events booking through your event type link"
rightAlignedComponent={
<TextField
containerClassName="h-9 max-h-9"
addOnLeading={<Search />}
name="search"
labelSrOnly
placeholder="WIP"
/>
}
/>
);
-19
View File
@@ -1,19 +0,0 @@
import * as Radio from "@calcom/ui/v2/core/form/radio-area/Radio";
import { RadioField } from "@calcom/ui/v2/core/form/radio-area/Radio";
export default {
title: "Radio",
component: RadioField,
};
export const RadioGroupDemo = () => {
return (
<form>
<Radio.Group aria-label="View density" defaultValue="default">
<RadioField label="Default" id="r1" value="1" />
<RadioField label="Next" id="r2" value="2" />
<RadioField label="Disabled" id="r3" disabled value="1" />
</Radio.Group>
</form>
);
};
-33
View File
@@ -1,33 +0,0 @@
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 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,
},
...[5, 10, 15, 20, 30, 45, 60, 90, 120].map((minutes) => ({
label: minutes + " " + "minutes",
value: minutes,
})),
];
export const Single = () => <Select options={singleOptions} />;
export const Multi = () => <MultiDropdownSelect options={multiOptions} />;
-15
View File
@@ -1,15 +0,0 @@
import { ComponentMeta } from "@storybook/react";
import { Switch } from "@calcom/ui/v2";
export default {
title: "Switch",
component: Switch,
} as ComponentMeta<typeof Switch>;
export const All = () => (
<div className="flex flex-col space-y-2">
<p>Checked works in app but storybook doesnt like it</p>
<Switch label="Default" />
</div>
);
@@ -1,67 +0,0 @@
// Disabling until we figure out what is happening with the RouterMock.
import { ComponentMeta } from "@storybook/react";
import { Link } from "react-feather";
import { VerticalTabItem } from "@calcom/ui/v2";
export default {
title: "VerticalTabItem",
component: VerticalTabItem,
} as ComponentMeta<typeof VerticalTabItem>;
const TabItemProps = {
name: "Event Setup",
icon: Link,
href: "/settings/event",
info: "60 mins, Zoom",
};
export const Default = () => (
<div className="h-20 w-full bg-gray-100 p-4">
<VerticalTabItem {...TabItemProps} />
</div>
);
export const Active = () => (
<div className="h-20 w-full bg-gray-100 p-4">
<VerticalTabItem {...TabItemProps} />
</div>
);
// Mocking next router to show active state
Active.parameters = {
nextRouter: {
path: "/settings/[tab]",
asPath: "/settings/event",
},
};
export const Disabled = () => (
<div className="h-20 w-full bg-gray-100 p-4">
<VerticalTabItem {...TabItemProps} disabled />
</div>
);
export const NoIconNoInfo = () => (
<div className="h-20 w-full bg-gray-100 p-4">
<VerticalTabItem name="Event Setup" href="/settings/event" />
</div>
);
export const IconNoInfo = () => (
<div className="h-20 w-full bg-gray-100 p-4">
<VerticalTabItem name="Event Setup" href="/settings/event" icon={Link} />
</div>
);
export const IconNoInfoActive = () => (
<div className="h-20 w-full bg-gray-100 p-4">
<VerticalTabItem name="Event Setup" href="/settings/events" icon={Link} />
</div>
);
IconNoInfoActive.paramaters = {
nextRouter: {
path: "/settings/[tab]",
asPath: "/settings/events",
},
};
@@ -1,85 +0,0 @@
// Disabling until we figure out what is happening with the RouterMock.
import { ComponentMeta } from "@storybook/react";
import { Calendar, Clock, Grid, Link, RefreshCw, User, Users } from "react-feather";
import VerticalTabs from "@calcom/ui/v2/core/navigation/tabs/VerticalTabs";
export default {
title: "Vertical Tabs",
component: VerticalTabs,
} as ComponentMeta<typeof VerticalTabs>;
const VerticalTabsPropsDefault = [
{
name: "Security",
icon: Link,
href: "/security",
},
{
name: "Profile",
icon: User,
href: "/profile",
},
{
name: "Calendar Sync",
icon: RefreshCw,
href: "/cal-sync",
},
{
name: "Teams",
icon: Users,
href: "/Teams",
},
];
export const Default = () => (
<div className="w-full p-4" style={{ backgroundColor: "#F9FAFB" }}>
<VerticalTabs tabs={VerticalTabsPropsDefault} />
</div>
);
Default.parameters = {
nextRouter: {
path: "/[page]",
asPath: "/profile",
},
};
const VerticalTabsPropsInfo = [
{
name: "Event Setup",
icon: Link,
href: "/events",
info: "60 Mins, Zoom",
},
{
name: "Availability",
icon: Calendar,
href: "/availability",
info: "Working Hours",
},
{
name: "Limits",
icon: Clock,
href: "/limits",
info: "Buffers, Limits & more...",
},
{
name: "Apps",
icon: Grid,
href: "/apps",
info: "3 apps - 0 active",
},
];
export const Info = () => (
<div className="w-full p-4" style={{ backgroundColor: "#F9FAFB" }}>
<VerticalTabs tabs={VerticalTabsPropsInfo} />
</div>
);
Info.parameters = {
nextRouter: {
path: "/[page]",
asPath: "/events",
},
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 KiB

+1 -10
View File
@@ -2,13 +2,4 @@
@tailwind components;
@tailwind utilities;
@import "../../../packages/ui/v2/core/form/date-range-picker/styles.css";
@import "../../../node_modules/@wojtekmaj/react-daterange-picker/dist/DateRangePicker.css";
:root {
--brand-color: #292929;
--brand-text-color: #ffffff;
--brand-color-dark-mode: #fafafa;
--brand-text-color-dark-mode: #292929;
font-family: 'Inter', sans-serif;
}
@import "../../../packages/ui/styles/shared-globals.css"
+189
View File
@@ -0,0 +1,189 @@
.sbdocs {
font-family: 'Inter var' !important;
padding: 0!important;
}
#docs-root {
overflow: hidden;
}
.sbdocs.sbdocs-h1 {
font-family: "Cal Sans", "sans-serif" !important;
font-weight: 600;
font-size: 36px;
margin: 0 0 82px 0;
}
.sbdocs.sbdocs-h2 {
font-size: 20px;
font-weight: 500;
border: none;
border-top: 2px solid rgba(0, 0, 0, 0.12);
padding-top: 12px!important;
padding-bottom: 12px!important;
margin: 82px 0 0 0;
}
.sbdocs.sbdocs-p {
max-width: 560px;
margin: 0 0 12px 0;
}
.sbdocs.sbdocs-content {
width: 1200px;
max-width: 100%;
}
/** Docs table **/
.custom-args-wrapper{
max-height: 600px;
overflow-y: scroll;
overflow-x: hidden;
margin-bottom: 1rem;
}
.docblock-argstable-body {
box-shadow: none!important;
font-size: 14px;
}
/* .docblock-argstable-head tr th, */
.docblock-argstable-body tr td {
/* padding-left: 0!important; */
padding: 20px !important;
}
/** Column titles **/
.docblock-argstable-body tr td > span,
.docblock-argstable-head tr th > span {
color: #525252 !important;
font-weight: 500 !important;
}
.docblock-argstable-body div p,
.docblock-argstable-body div span {
color: #8F8F8F !important;
}
/** Custom components **/
.story-title {
margin: 0 0 82px 0;
}
.story-title.offset {
margin-top: 100px;
}
.story-title h1 {
font-family: "Cal Sans", "sans-serif" !important;
font-weight: 600;
font-size: 36px;
}
.story-title h1 span {
color: #9CA3AF;
font-family: 'Inter var';
font-weight: normal;
display: inline-block;
margin-left: 8px;
}
.story-title p {
color: #111827;
font-size: 16px;
}
.examples {
background-color: #F9FAFB;
padding: 20px;
display: flex;
flex-direction: column;
margin-bottom: 20px;
}
.examples-content {
display: flex;
flex-wrap: wrap;
justify-content: center;
padding: 30px 0;
}
.examples-title {
color: #8F8F8F;
font-size: 14px;
margin-bottom: 30px;
}
.examples-item-title {
font-size: 12px;
color: #8F8F8F;
margin-bottom: 12px;
display: block;
}
.examples-item {
margin: 0 20px;
}
.examples-footnote p,
.examples-footnote ul,
.examples-footnote li {
color: #8F8F8F;
font-size: 14px;
}
.examples-footnote ul {
padding-left: 8px;
}
.examples-footnote li {
margin: 0!important;
}
.story-note {
background-color: #F9FAFB;
font-size: 14px;
padding: 20px;
margin-bottom: 12px;
border-radius: 8px;
}
.story-note > div {
max-width: 600px;
}
/** SB Docs Preview **/
.sbdocs-preview {
width: 100vw;
left: calc((100vw - 1200px) / -2);
box-shadow: none !important;
margin: 0 !important;
border: none !important;
border-radius: none!important;
}
.docs-story > div:first-child {
padding: 0!important;
margin: 0!important;
}
.docs-story .innerZoomElementWrapper > div {
border: none !important;
}
.sb-main-padded {
padding: 0!important;
}
@media screen and (max-width: 1200px) {
.sbdocs-preview {
left: -24px;
width: calc(100vw + 48px);
padding-left: 12px;
padding-right: 12px;
}
.sbdocs.sbdocs-content {
padding: 24px !important;
}
}
+5 -165
View File
@@ -1,167 +1,7 @@
const base = require("@calcom/config/tailwind-preset");
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["../../packages/ui/**/*.{js,ts,jsx,tsx}", "./stories/**/*.{js,ts,tsx,jsx}"],
darkMode: "class",
theme: {
fontFamily: {
cal: ["Cal Sans", "sans-serif"],
mono: ["Roboto Mono", "monospace"],
},
extend: {
colors: {
/* your primary brand color */
brandcontrast: "var(--brand-text-color)",
darkmodebrand: "var(--brand-color-dark-mode)",
darkmodebrandcontrast: "var(--brand-text-color-dark-mode)",
bookinglightest: "var(--booking-lightest-color)",
bookinglighter: "var(--booking-lighter-color)",
bookinglight: "var(--booking-light-color)",
bookingmedian: "var(--booking-median-color)",
bookingdark: "var(--booking-dark-color)",
bookingdarker: "var(--booking-darker-color)",
bookinghighlight: "var(--booking-highlight-color)",
black: "#111111",
brand: {
// Figure out a way to automate this for self hosted users
// Goto https://javisperez.github.io/tailwindcolorshades to generate your brand color
50: "#d1d5db",
100: "#9ca3af",
200: "#6b7280",
300: "#4b5563",
400: "#374151",
500: "#111827", // Brand color
600: "#0f1623",
700: "#0d121d",
800: "#0a0e17",
900: "#080c13",
DEFAULT: "#111827",
},
gray: {
50: "#F9FAFB",
100: "#F3F4F6",
200: "#E5E7EB",
300: "#D1D5DB",
400: "#9CA3AF",
500: "#6B7280",
600: "#4B5563",
700: "#374151",
800: "#1F2937",
900: "#111827",
},
neutral: {
50: "#F8F8F8",
100: "#F5F5F5",
200: "#E1E1E1",
300: "#CFCFCF",
400: "#ACACAC",
500: "#888888",
600: "#494949",
700: "#3E3E3E",
800: "#313131",
900: "#292929",
},
primary: {
50: "#F4F4F4",
100: "#E8E8E8",
200: "#C6C6C6",
300: "#A3A3A3",
400: "#5F5F5F",
500: "#1A1A1A",
600: "#171717",
700: "#141414",
800: "#101010",
900: "#0D0D0D",
},
secondary: {
50: "#F5F8F7",
100: "#EBF0F0",
200: "#CDDAD9",
300: "#AEC4C2",
400: "#729894",
500: "#356C66",
600: "#30615C",
700: "#28514D",
800: "#20413D",
900: "#223B41",
},
red: {
50: "#FFF5F5",
100: "#FFE3E2",
200: "#FFC9C9",
300: "#FEA8A8",
400: "#FF8787",
500: "#FF6B6B",
600: "#FA5352",
700: "#F03E3F",
800: "#E03130",
900: "#C92B2B",
},
orange: {
50: "#FFF4E5",
100: "#FFE8CC",
200: "#FFD8A8",
300: "#FFBF78",
400: "#FFA94E",
500: "#FF922B",
600: "#FD7E14",
700: "#F76706",
800: "#E8580C",
900: "#D94810",
},
green: {
50: "#EBFCEE",
100: "#D2F9D9",
200: "#B1F2BA",
300: "#8CE99A",
400: "#69DB7C",
500: "#51CF66",
600: "#40C057",
700: "#36B24D",
800: "#2F9E44",
900: "#2B8A3E",
},
blue: {
50: "#E7F5FF",
100: "#D0EBFF",
200: "#A4D8FF",
300: "#74C0FC",
400: "#4DABF7",
500: "#339AF0",
600: "#238BE6",
700: "#1C7ED7",
800: "#1971C2",
900: "#1763AB",
},
},
fontFamily: {
cal: ["inter", "sans-serif"],
mono: ["Roboto Mono", "monospace"],
},
maxHeight: (theme) => ({
0: "0",
97: "25rem",
...theme("spacing"),
full: "100%",
screen: "100vh",
}),
minHeight: (theme) => ({
0: "0",
...theme("spacing"),
full: "100%",
screen: "100vh",
}),
minWidth: (theme) => ({
0: "0",
...theme("spacing"),
full: "100%",
screen: "100vw",
}),
maxWidth: (theme, { breakpoints }) => ({
0: "0",
...theme("spacing"),
...breakpoints(theme("screens")),
full: "100%",
screen: "100vw",
}),
},
},
...base,
darkMode: ["class", '[data-mode="dark"]'],
content: [...base.content, "../../packages/ui/**/*.{js,ts,jsx,tsx,mdx}", "./stories/**/*.{js,ts,tsx,jsx}"],
};
Executable → Regular
View File
+8
View File
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
}
}
+2 -2
View File
@@ -14,7 +14,7 @@
"dependencies": {
"highlight.js": "^11.6.0",
"isarray": "2.0.5",
"next": "^12.2.5",
"next": "^12.3.1",
"openapi-snippet": "^0.13.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -23,7 +23,7 @@
"devDependencies": {
"@types/node": "16.9.1",
"@types/react": "^18.0.17",
"@types/react-dom": "18.0.4",
"@types/react-dom": "^18.0.6",
"typescript": "^4.7.4"
}
}
+3 -1
View File
@@ -67,4 +67,6 @@ tsconfig.tsbuildinfo
public/embed
# Copied app-store images
public/app-store
public/app-store
# Sentry
.sentryclirc
-119
View File
@@ -598,60 +598,6 @@ paths:
summary: Gets and stores the OAuth token
tags:
- Integrations
/api/user/profile:
patch:
description: Updates a user's profile.
summary: Updates a user's profile
tags:
- User
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
description:
type: string
avatar:
type: string
timeZone:
type: string
weekStart:
type: string
hideBranding:
type: string
theme:
type: string
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
message:
type: string
"401":
description: Unauthorized
content:
application/json:
schema:
type: object
properties:
message:
type: string
"500":
description: Internal Server Error
content:
application/json:
schema:
type: object
properties:
message:
type: string
/api/me:
get:
description: Gets current user's profile.
@@ -686,71 +632,6 @@ paths:
properties:
message:
type: string
/api/user/membership:
get:
description: Get a list of the teams the user has joined.
summary: Get the teams a user is joined to
tags:
- User
requestBody:
content:
application/json:
schema:
type: object
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
message:
type: string
"401":
description: Unauthorized
content:
application/json:
schema:
type: object
properties:
message:
type: string
"500":
description: Internal Server Error
content:
application/json:
schema:
type: object
properties:
message:
type: string
patch:
description: Accept team invitation
summary: Accept team invitation.
tags:
- User
requestBody:
content:
application/json:
schema:
type: object
properties:
teamId:
type: string
delete:
description: Leave team or decline membership invite of current user
summary: Leave team or decline team invite.
tags:
- User
requestBody:
content:
application/json:
schema:
type: object
properties:
teamId:
type: string
"/api/{team}":
delete:
description: Deletes a team
@@ -36,7 +36,7 @@ const ImageOption = (optionProps: OptionProps<{ [key: string]: string; type: App
const AdditionalCalendarSelector = ({ isLoading }: AdditionalCalendarSelectorProps): JSX.Element | null => {
const { t } = useLocale();
const query = trpc.useQuery(["viewer.integrations", { variant: "calendar", onlyInstalled: true }]);
const query = trpc.viewer.integrations.useQuery({ variant: "calendar", onlyInstalled: true });
return (
<QueryCell
-306
View File
@@ -1,306 +0,0 @@
import Link from "next/link";
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 { 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 { Button, SkeletonButton } from "@calcom/ui";
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,
logo,
body,
categories,
author,
price = 0,
commission,
isGlobal = false,
feeType,
docs,
website,
email,
tos,
privacy,
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");
},
onError: (error) => {
if (error instanceof Error) showToast(error.message || "App could not be installed", "error");
},
});
const priceInDollar = Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
useGrouping: false,
}).format(price);
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="-mx-4 md:-mx-8">
<div className="px-8">
<Link href="/apps">
<a className="mt-2 inline-flex px-1 py-2 text-sm text-gray-500 hover:bg-gray-100 hover:text-gray-800">
<Icon.FiChevronLeft className="h-5 w-5" /> {t("browse_apps")}
</a>
</Link>
<div className="items-center justify-between py-4 sm:flex sm:py-8">
<div className="flex">
<img className="h-16 w-16 rounded-sm" src={logo} alt={name} />
<header className="px-4 py-2">
<div className="flex items-center">
<h1 className="font-cal text-xl text-gray-900">{name}</h1>
</div>
<h2 className="text-sm text-gray-500">
<span className="capitalize">{categories[0]}</span> • {t("published_by", { author })}
</h2>
</header>
</div>
<div className="mt-4 sm:mt-0 sm:text-right">
{!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("globally_install")}
</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} data-testid="install-app-button">
{t("add_another")}
</Button>
);
}}
/>
)}
</div>
) : 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}
isProOnly={isProOnly}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
onClick: () => {
mutation.mutate({ type });
},
loading: mutation.isLoading,
};
}
return (
<Button data-testid="install-app-button" {...props}>
{t("install_app")}
</Button>
);
}}
/>
))
) : (
<SkeletonButton width="24" height="10" />
)}
{price !== 0 && (
<small className="block text-right">
{feeType === "usage-based" ? commission + "% + " + priceInDollar + "/booking" : priceInDollar}
{feeType === "monthly" && "/" + t("month")}
</small>
)}
</div>
</div>
</div>
<div className="justify-between px-8 py-10 md:flex">
<div className="prose-sm prose mb-6">{body}</div>
<div className="md:max-w-80 flex-1 md:ml-8">
<h4 className="font-medium text-gray-900 ">{t("categories")}</h4>
<div className="space-x-2">
{categories.map((category) => (
<Link href={"/apps/categories/" + category} key={category}>
<a>
<Badge variant="success">{category}</Badge>
</a>
</Link>
))}
</div>
<h4 className="mt-8 font-medium text-gray-900 ">{t("pricing")}</h4>
<small>
{price === 0 ? (
"Free"
) : (
<>
{Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
useGrouping: false,
}).format(price)}
{feeType === "monthly" && "/" + t("month")}
</>
)}
</small>
<h4 className="mt-8 mb-2 font-medium text-gray-900 ">{t("learn_more")}</h4>
<ul className="prose -ml-1 -mr-1 text-xs leading-5">
{docs && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-blue-500 no-underline hover:underline"
href={docs}>
<Icon.FiBookOpen className="mr-1 -mt-1 inline h-4 w-4" />
{t("documentation")}
</a>
</li>
)}
{website && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-blue-500 no-underline hover:underline"
href={website}>
<Icon.FiExternalLink className="mr-1 -mt-px inline h-4 w-4" />
{website.replace("https://", "")}
</a>
</li>
)}
{email && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-blue-500 no-underline hover:underline"
href={"mailto:" + email}>
<Icon.FiMail className="mr-1 -mt-px inline h-4 w-4" />
{email}
</a>
</li>
)}
{tos && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-blue-500 no-underline hover:underline"
href={tos}>
<Icon.FiFile className="mr-1 -mt-px inline h-4 w-4" />
{t("terms_of_service")}
</a>
</li>
)}
{privacy && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-blue-500 no-underline hover:underline"
href={privacy}>
<Icon.FiShield className="mr-1 -mt-px inline h-4 w-4" />
{t("privacy_policy")}
</a>
</li>
)}
</ul>
<hr className="my-6" />
<small className="leading-1 block 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.
</small>
<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>
</div>
);
};
export default function App(props: {
name: string;
type: AppType["type"];
isGlobal?: AppType["isGlobal"];
logo: string;
body: React.ReactNode;
categories: string[];
author: string;
pro?: boolean;
price?: number;
commission?: number;
feeType?: AppType["feeType"];
docs?: string;
website?: string;
email: string; // required
tos?: string;
privacy?: string;
licenseRequired: AppType["licenseRequired"];
isProOnly: AppType["isProOnly"];
}) {
return (
<Shell large isPublic>
{props.licenseRequired ? (
<LicenseRequired>
<Component {...props} />
</LicenseRequired>
) : (
<Component {...props} />
)}
</Shell>
);
}
@@ -23,7 +23,7 @@ const DestinationCalendarSelector = ({
destinationCalendar,
}: Props): JSX.Element | null => {
const { t } = useLocale();
const query = trpc.useQuery(["viewer.connectedCalendars"]);
const query = trpc.viewer.connectedCalendars.useQuery();
const [selectedOption, setSelectedOption] = useState<{ value: string; label: string } | null>(null);
// Extra styles to show prefixed text in react-select
+215 -209
View File
@@ -6,8 +6,9 @@ import { components, ControlProps } from "react-select";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Icon } from "@calcom/ui/Icon";
import { Button } from "@calcom/ui/components";
import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields";
import { Button, HorizontalTabs, showToast, Switch } from "@calcom/ui/v2";
import { 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";
@@ -56,10 +57,13 @@ const goto = (router: NextRouter, searchParams: Record<string, string>) => {
};
const removeQueryParams = (router: NextRouter, queryParams: string[]) => {
const params = new URLSearchParams(window.location.search);
queryParams.forEach((param) => {
delete router.query[param];
params.delete(param);
});
router.push(`${router.asPath.split("?")[0]}?${router.query.toString()}`);
router.push(`${router.asPath.split("?")[0]}?${params.toString()}`);
};
/**
@@ -797,245 +801,247 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
{embed.title}
</h3>
<hr className={classNames("mt-4", embedType === "element-click" ? "hidden" : "")} />
<div className={classNames("mt-4 font-medium", embedType === "element-click" ? "hidden" : "")}>
<Collapsible
open={isEmbedCustomizationOpen}
onOpenChange={() => setIsEmbedCustomizationOpen((val) => !val)}>
<CollapsibleTrigger
type="button"
className="flex w-full items-center text-base font-medium text-neutral-900">
<div>
{embedType === "inline"
? "Inline Embed Customization"
: embedType === "floating-popup"
? "Floating Popup Customization"
: "Element Click Customization"}
</div>
<Icon.FiChevronRight
className={`${
isEmbedCustomizationOpen ? "rotate-90 transform" : ""
} ml-auto h-5 w-5 text-neutral-500`}
/>
</CollapsibleTrigger>
<CollapsibleContent className="text-sm">
<div className={classNames("mt-6", embedType === "inline" ? "block" : "hidden")}>
{/*TODO: Add Auto/Fixed toggle from Figma */}
<div className="text-sm">Embed Window Sizing</div>
<div className="justify-left flex items-center">
<TextField
name="width"
labelProps={{ className: "hidden" }}
required
value={previewState.inline.width}
onChange={(e) => {
setPreviewState((previewState) => {
const width = e.target.value || "100%";
return {
...previewState,
inline: {
...previewState.inline,
width,
},
};
});
}}
addOnLeading={<InputLeading>W</InputLeading>}
/>
<span className="p-2">×</span>
<TextField
labelProps={{ className: "hidden" }}
name="height"
value={previewState.inline.height}
required
onChange={(e) => {
const height = e.target.value || "100%";
setPreviewState((previewState) => {
return {
...previewState,
inline: {
...previewState.inline,
height,
},
};
});
}}
addOnLeading={<InputLeading>H</InputLeading>}
/>
<div className="max-h-97 flex flex-col overflow-y-auto">
<div className={classNames("mt-4 font-medium", embedType === "element-click" ? "hidden" : "")}>
<Collapsible
open={isEmbedCustomizationOpen}
onOpenChange={() => setIsEmbedCustomizationOpen((val) => !val)}>
<CollapsibleTrigger
type="button"
className="flex w-full items-center text-base font-medium text-neutral-900">
<div>
{embedType === "inline"
? "Inline Embed Customization"
: embedType === "floating-popup"
? "Floating Popup Customization"
: "Element Click Customization"}
</div>
</div>
<div
className={classNames(
"mt-4 items-center justify-between",
embedType === "floating-popup" ? "" : "hidden"
)}>
<div className="mb-2 text-sm">Button Text</div>
{/* Default Values should come from preview iframe */}
<TextField
name="buttonText"
labelProps={{ className: "hidden" }}
onChange={(e) => {
setPreviewState((previewState) => {
return {
...previewState,
floatingPopup: {
...previewState.floatingPopup,
buttonText: e.target.value,
},
};
});
}}
defaultValue="Book my Cal"
required
<Icon.FiChevronRight
className={`${
isEmbedCustomizationOpen ? "rotate-90 transform" : ""
} ml-auto h-5 w-5 text-neutral-500`}
/>
</div>
<div
className={classNames(
"mt-4 flex items-center justify-start",
embedType === "floating-popup" ? "space-x-2" : "hidden"
)}>
<Switch
defaultChecked={true}
onCheckedChange={(checked) => {
setPreviewState((previewState) => {
return {
...previewState,
floatingPopup: {
...previewState.floatingPopup,
hideButtonIcon: !checked,
},
};
});
}}
/>
<div className="text-sm">Display Calendar Icon Button</div>
</div>
<div
className={classNames(
"mt-4 items-center justify-between",
embedType === "floating-popup" ? "" : "hidden"
)}>
<div className="mb-2">Position of Button</div>
<Select
onChange={(position) => {
setPreviewState((previewState) => {
return {
...previewState,
floatingPopup: {
...previewState.floatingPopup,
buttonPosition: position?.value,
},
};
});
}}
defaultValue={FloatingPopupPositionOptions[0]}
options={FloatingPopupPositionOptions}
/>
</div>
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
<div>Button Color</div>
<div className="w-full">
<ColorPicker
defaultValue="#000000"
onChange={(color) => {
</CollapsibleTrigger>
<CollapsibleContent className="text-sm">
<div className={classNames("mt-6", embedType === "inline" ? "block" : "hidden")}>
{/*TODO: Add Auto/Fixed toggle from Figma */}
<div className="text-sm">Embed Window Sizing</div>
<div className="justify-left flex items-center">
<TextField
name="width"
labelProps={{ className: "hidden" }}
required
value={previewState.inline.width}
onChange={(e) => {
setPreviewState((previewState) => {
const width = e.target.value || "100%";
return {
...previewState,
inline: {
...previewState.inline,
width,
},
};
});
}}
addOnLeading={<InputLeading>W</InputLeading>}
/>
<span className="p-2">×</span>
<TextField
labelProps={{ className: "hidden" }}
name="height"
value={previewState.inline.height}
required
onChange={(e) => {
const height = e.target.value || "100%";
setPreviewState((previewState) => {
return {
...previewState,
inline: {
...previewState.inline,
height,
},
};
});
}}
addOnLeading={<InputLeading>H</InputLeading>}
/>
</div>
</div>
<div
className={classNames(
"mt-4 items-center justify-between",
embedType === "floating-popup" ? "" : "hidden"
)}>
<div className="mb-2 text-sm">Button Text</div>
{/* Default Values should come from preview iframe */}
<TextField
name="buttonText"
labelProps={{ className: "hidden" }}
onChange={(e) => {
setPreviewState((previewState) => {
return {
...previewState,
floatingPopup: {
...previewState.floatingPopup,
buttonColor: color,
buttonText: e.target.value,
},
};
});
}}
defaultValue="Book my Cal"
required
/>
</div>
</div>
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
<div>Text Color</div>
<div className="w-full">
<ColorPicker
defaultValue="#000000"
onChange={(color) => {
<div
className={classNames(
"mt-4 flex items-center justify-start",
embedType === "floating-popup" ? "space-x-2" : "hidden"
)}>
<Switch
defaultChecked={true}
onCheckedChange={(checked) => {
setPreviewState((previewState) => {
return {
...previewState,
floatingPopup: {
...previewState.floatingPopup,
buttonTextColor: color,
hideButtonIcon: !checked,
},
};
});
}}
/>
<div className="text-sm">Display Calendar Icon Button</div>
</div>
</div>
</CollapsibleContent>
</Collapsible>
</div>
<hr className="mt-4" />
<div className="mt-4 font-medium">
<Collapsible
open={isBookingCustomizationOpen}
onOpenChange={() => setIsBookingCustomizationOpen((val) => !val)}>
<CollapsibleTrigger className="flex w-full" type="button">
<div className="text-base font-medium text-neutral-900">Cal Booking Customization</div>
<Icon.FiChevronRight
className={`${
isBookingCustomizationOpen ? "rotate-90 transform" : ""
} ml-auto h-5 w-5 text-neutral-500`}
/>
</CollapsibleTrigger>
<CollapsibleContent>
<div className="mt-6 text-sm">
<Label className="">
<div className="mb-2">Theme</div>
<div
className={classNames(
"mt-4 items-center justify-between",
embedType === "floating-popup" ? "" : "hidden"
)}>
<div className="mb-2">Position of Button</div>
<Select
className="w-full"
defaultValue={ThemeOptions[0]}
components={{
Control: ThemeSelectControl,
}}
onChange={(option) => {
if (!option) {
return;
}
onChange={(position) => {
setPreviewState((previewState) => {
return {
...previewState,
theme: option.value,
floatingPopup: {
...previewState.floatingPopup,
buttonPosition: position?.value,
},
};
});
}}
options={ThemeOptions}
defaultValue={FloatingPopupPositionOptions[0]}
options={FloatingPopupPositionOptions}
/>
</Label>
{[
{ name: "brandColor", title: "Brand Color" },
// { name: "lightColor", title: "Light Color" },
// { name: "lighterColor", title: "Lighter Color" },
// { name: "lightestColor", title: "Lightest Color" },
// { name: "highlightColor", title: "Highlight Color" },
// { name: "medianColor", title: "Median Color" },
].map((palette) => (
<Label key={palette.name} className="pb-4">
<div className="mb-2 pt-2">{palette.title}</div>
<div className="w-full">
<ColorPicker
defaultValue="#000000"
onChange={(color) => {
addToPalette({
[palette.name as keyof typeof previewState["palette"]]: color,
});
}}
/>
</div>
</div>
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
<div>Button Color</div>
<div className="w-full">
<ColorPicker
defaultValue="#000000"
onChange={(color) => {
setPreviewState((previewState) => {
return {
...previewState,
floatingPopup: {
...previewState.floatingPopup,
buttonColor: color,
},
};
});
}}
/>
</div>
</div>
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
<div>Text Color</div>
<div className="w-full">
<ColorPicker
defaultValue="#000000"
onChange={(color) => {
setPreviewState((previewState) => {
return {
...previewState,
floatingPopup: {
...previewState.floatingPopup,
buttonTextColor: color,
},
};
});
}}
/>
</div>
</div>
</CollapsibleContent>
</Collapsible>
</div>
<hr className="mt-4" />
<div className="mt-4 font-medium">
<Collapsible
open={isBookingCustomizationOpen}
onOpenChange={() => setIsBookingCustomizationOpen((val) => !val)}>
<CollapsibleTrigger className="flex w-full" type="button">
<div className="text-base font-medium text-neutral-900">Cal Booking Customization</div>
<Icon.FiChevronRight
className={`${
isBookingCustomizationOpen ? "rotate-90 transform" : ""
} ml-auto h-5 w-5 text-neutral-500`}
/>
</CollapsibleTrigger>
<CollapsibleContent>
<div className="mt-6 text-sm">
<Label className="">
<div className="mb-2">Theme</div>
<Select
className="w-full"
defaultValue={ThemeOptions[0]}
components={{
Control: ThemeSelectControl,
}}
onChange={(option) => {
if (!option) {
return;
}
setPreviewState((previewState) => {
return {
...previewState,
theme: option.value,
};
});
}}
options={ThemeOptions}
/>
</Label>
))}
</div>
</CollapsibleContent>
</Collapsible>
{[
{ name: "brandColor", title: "Brand Color" },
// { name: "lightColor", title: "Light Color" },
// { name: "lighterColor", title: "Lighter Color" },
// { name: "lightestColor", title: "Lightest Color" },
// { name: "highlightColor", title: "Highlight Color" },
// { name: "medianColor", title: "Median Color" },
].map((palette) => (
<Label key={palette.name} className="pb-4">
<div className="mb-2 pt-2">{palette.title}</div>
<div className="w-full">
<ColorPicker
defaultValue="#000000"
onChange={(color) => {
addToPalette({
[palette.name as keyof typeof previewState["palette"]]: color,
});
}}
/>
</div>
</Label>
))}
</div>
</CollapsibleContent>
</Collapsible>
</div>
</div>
</div>
<div className="flex w-2/3 flex-col p-8">
+5 -5
View File
@@ -11,14 +11,14 @@ export type GateState = {
type GateProps = {
children: React.ReactNode;
gates: Gate[];
metadata: JSONObject;
appData: JSONObject;
dispatch: Dispatch<Partial<GateState>>;
};
const RainbowGate = dynamic(() => import("@calcom/app-store/rainbow/components/RainbowKit"));
// To add a new Gate just add the gate logic to the switch statement
const Gates: React.FC<GateProps> = ({ children, gates, metadata, dispatch }) => {
const Gates: React.FC<GateProps> = ({ children, gates, appData, dispatch }) => {
const [rainbowToken, setRainbowToken] = useState<string>();
useEffect(() => {
@@ -31,12 +31,12 @@ const Gates: React.FC<GateProps> = ({ children, gates, metadata, dispatch }) =>
for (const gate of gates) {
switch (gate) {
case "rainbow":
if (metadata.blockchainId && metadata.smartContractAddress && !rainbowToken) {
if (appData.blockchainId && appData.smartContractAddress && !rainbowToken) {
gateWrappers = (
<RainbowGate
setToken={setRainbowToken}
chainId={metadata.blockchainId as number}
tokenAddress={metadata.smartContractAddress as string}>
chainId={appData.blockchainId as number}
tokenAddress={appData.smartContractAddress as string}>
{gateWrappers}
</RainbowGate>
);
+1 -1
View File
@@ -4,7 +4,7 @@ import { useEffect } from "react";
import { trpc } from "@calcom/trpc/react";
export function useViewerI18n() {
return trpc.useQuery(["viewer.public.i18n"], {
return trpc.viewer.public.i18n.useQuery(undefined, {
staleTime: Infinity,
/**
* i18n should never be clubbed with other queries, so that it's caching can be managed independently.
@@ -23,11 +23,11 @@ const ImageOption = (optionProps: OptionProps<{ [key: string]: string; type: App
type={data.type}
render={(installProps) => {
return (
<Button {...installProps} className="w-full" color="minimal">
<Button {...installProps} className="flex w-full align-top" 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>
<p className="text-left">{`${t("add")} ${data.label}`}</p>
</Button>
);
}}
@@ -35,14 +35,14 @@ const ImageOption = (optionProps: OptionProps<{ [key: string]: string; type: App
) : (
<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>
<p>{t("install_new_calendar_app")}</p>
</Button>
);
};
const AdditionalCalendarSelector = ({ isLoading }: AdditionalCalendarSelectorProps): JSX.Element | null => {
const { t } = useLocale();
const query = trpc.useQuery(["viewer.integrations", { variant: "calendar", onlyInstalled: true }]);
const query = trpc.viewer.integrations.useQuery({ variant: "calendar", onlyInstalled: true });
return (
<QueryCell
@@ -63,11 +63,45 @@ const AdditionalCalendarSelector = ({ isLoading }: AdditionalCalendarSelectorPro
return (
<Select
name="additionalCalendar"
placeholder={t("install_another")}
placeholder={
<div className="flex justify-start text-black">
<Icon.FiPlus className="-ml-1 h-4 w-4 ltr:mr-2 rtl:ml-2 rtl:-mr-1" />
<p>{t("add")}</p>
</div>
}
className="min-w-44"
options={options}
isSearchable={false}
isLoading={isLoading}
components={{ Option: ImageOption }}
styles={{
menu: (defaultStyles) => ({
...defaultStyles,
"@media only screen and (max-width: 640px)": {
left: "0",
},
width: "max-content",
right: "0",
}),
control: (defaultStyles) => ({
...defaultStyles,
padding: "0",
border: "0",
borderRadius: "inherit",
}),
dropdownIndicator: (defaultStyles) => ({
...defaultStyles,
display: "none",
}),
valueContainer: (defaultStyles) => ({
...defaultStyles,
padding: "0",
}),
placeholder: (defaultStyles) => ({
...defaultStyles,
margin: "0",
}),
}}
/>
);
}}
-19
View File
@@ -1,19 +0,0 @@
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { App } from "@calcom/types/App";
import AppCard from "./AppCard";
export default function AllApps({ apps }: { apps: App[] }) {
const { t } = useLocale();
return (
<div className="mb-16">
<h2 className="mb-2 text-lg font-semibold text-gray-900">{t("all_apps")}</h2>
<div className="grid-col-1 grid grid-cols-1 gap-3 md:grid-cols-3">
{apps.map((app) => (
<AppCard key={app.name} app={app} />
))}
</div>
</div>
);
}
@@ -10,10 +10,13 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { App as AppType } from "@calcom/types/App";
import { Icon } from "@calcom/ui/Icon";
import { Button } from "@calcom/ui/components";
import { showToast, SkeletonText } from "@calcom/ui/v2";
import { Button, SkeletonButton, Shell } from "@calcom/ui/v2";
import { SkeletonButton, Shell } from "@calcom/ui/v2";
import DisconnectIntegration from "@calcom/ui/v2/modules/integrations/DisconnectIntegration";
import HeadSeo from "@components/seo/head-seo";
const Component = ({
name,
type,
@@ -55,18 +58,21 @@ const Component = ({
}).format(price);
const [existingCredentials, setExistingCredentials] = useState<number[]>([]);
const appCredentials = trpc.useQuery(["viewer.appCredentialsByType", { appType: type }], {
onSuccess(data) {
setExistingCredentials(data);
},
});
const appCredentials = trpc.viewer.appCredentialsByType.useQuery(
{ appType: type },
{
onSuccess(data) {
setExistingCredentials(data);
},
}
);
const allowedMultipleInstalls = categories.indexOf("calendar") > -1;
return (
<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">
<div className="align-center mb-4 -ml-4 -mr-4 flex min-h-[450px] w-auto basis-3/5 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:justify-center lg:rounded-md">
{images ? (
images.map((img) => (
<img
@@ -83,7 +89,7 @@ const Component = ({
)}
<div
className={classNames(
"sticky top-0 -mt-4 max-w-xl flex-1 pb-12 text-sm lg:pb-0",
"sticky top-0 -mt-4 max-w-xl basis-2/5 pb-12 text-sm lg:pb-0",
hasImages && "lg:ml-8"
)}>
<div className="mb-8 flex pt-4">
@@ -140,6 +146,7 @@ const Component = ({
</div>
) : existingCredentials.length > 0 ? (
<DisconnectIntegration
buttonProps={{ color: "secondary" }}
label={t("disconnect")}
credentialId={existingCredentials[0]}
onSuccess={() => {
@@ -276,6 +283,7 @@ const Component = ({
export default function App(props: {
name: string;
description: AppType["description"];
type: AppType["type"];
isGlobal?: AppType["isGlobal"];
logo: string;
@@ -300,7 +308,16 @@ export default function App(props: {
const { t } = useLocale();
return (
<Shell large isPublic heading={t("app_store")} backPath="/apps">
<Shell large isPublic heading={t("app_store")} backPath="/apps" withoutSeo>
<HeadSeo
title={props.name}
description={props.description}
app={{ slug: props.logo, name: props.name, description: props.description }}
nextSeoProps={{
nofollow: true,
noindex: true,
}}
/>
{props.licenseRequired ? (
<LicenseRequired>
<Component {...props} />
-44
View File
@@ -1,44 +0,0 @@
import Link from "next/link";
import { trpc } from "@calcom/trpc/react";
import { App } from "@calcom/types/App";
import Badge from "@calcom/ui/Badge";
import Button from "@calcom/ui/Button";
interface AppCardProps {
app: App;
}
export default function AppCard(props: AppCardProps) {
const { data: user } = trpc.useQuery(["viewer.me"]);
return (
<Link href={"/apps/" + props.app.slug}>
<a
className="block h-full rounded-sm border border-gray-300 p-5 hover:bg-neutral-50"
data-testid={`app-store-app-card-${props.app.slug}`}>
<div className="flex">
{
// eslint-disable-next-line @next/next/no-img-element
<img src={props.app.logo} alt={props.app.name + " Logo"} className="mb-4 h-12 w-12 rounded-sm" />
}
<Button
color="secondary"
className="ml-auto flex self-start"
onClick={() => {
console.log("The magic is supposed to happen here");
}}>
Add
</Button>
</div>
<div className="flex items-center">
<h3 className="font-medium">{props.app.name}</h3>
</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" />
<span className="pl-1 text-gray-500">{props.reviews} reviews</span>
</div> */}
<p className="mt-2 truncate text-sm text-gray-500">{props.app.description}</p>
</a>
</Link>
);
}
@@ -3,24 +3,24 @@ import Link from "next/link";
import { Fragment } from "react";
import { InstallAppButton } from "@calcom/app-store/components";
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
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 { Button } from "@calcom/ui/components";
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 showToast from "@calcom/ui/v2/core/notifications";
import DisconnectIntegration from "@calcom/ui/v2/modules/integrations/DisconnectIntegration";
import { QueryCell } from "@lib/QueryCell";
import AdditionalCalendarSelector from "@components/apps/AdditionalCalendarSelector";
import IntegrationListItem from "@components/apps/IntegrationListItem";
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>;
@@ -77,7 +77,7 @@ function CalendarSwitch(props: {
},
{
async onSettled() {
await utils.invalidateQueries(["viewer.integrations"]);
await utils.viewer.integrations.invalidate();
},
onError() {
showToast(`Something went wrong when toggling "${props.title}""`, "error");
@@ -107,7 +107,7 @@ function CalendarSwitch(props: {
function CalendarList(props: Props) {
const { t } = useLocale();
const query = trpc.useQuery(["viewer.integrations", { variant: "calendar", onlyInstalled: false }]);
const query = trpc.viewer.integrations.useQuery({ variant: "calendar", onlyInstalled: false });
return (
<QueryCell
@@ -141,9 +141,12 @@ function CalendarList(props: Props) {
);
}
// todo: @hariom extract this into packages/apps-store as "GeneralAppSettings"
function ConnectedCalendarsList(props: Props) {
const { t } = useLocale();
const query = trpc.useQuery(["viewer.connectedCalendars"], { suspense: true });
const query = trpc.viewer.connectedCalendars.useQuery(undefined, {
suspense: true,
});
const { fromOnboarding } = props;
return (
<QueryCell
@@ -163,6 +166,7 @@ function ConnectedCalendarsList(props: Props) {
title={item.integration.title}
logo={item.integration.logo}
description={item.primary?.externalId || "No external Id"}
separate={true}
actions={
<div className="flex w-32 justify-end">
<DisconnectIntegration
@@ -173,23 +177,27 @@ function ConnectedCalendarsList(props: Props) {
/>
</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>
</>
)}
<div className="border-t border-gray-200">
{!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>
</>
)}
</div>
</IntegrationListItem>
) : (
<Alert
@@ -229,19 +237,19 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
const utils = trpc.useContext();
const onChanged = () =>
Promise.allSettled([
utils.invalidateQueries(["viewer.integrations", { variant: "calendar", onlyInstalled: true }], {
exact: true,
}),
utils.invalidateQueries(["viewer.connectedCalendars"]),
utils.viewer.integrations.invalidate(
{ variant: "calendar", onlyInstalled: true },
{
exact: true,
}
),
utils.viewer.connectedCalendars.invalidate(),
]);
const query = trpc.useQuery(["viewer.connectedCalendars"]);
const installedCalendars = trpc.useQuery([
"viewer.integrations",
{ variant: "calendar", onlyInstalled: true },
]);
const mutation = trpc.useMutation("viewer.setDestinationCalendar", {
const query = trpc.viewer.connectedCalendars.useQuery();
const installedCalendars = trpc.viewer.integrations.useQuery({ variant: "calendar", onlyInstalled: true });
const mutation = trpc.viewer.setDestinationCalendar.useMutation({
onSuccess: () => {
utils.invalidateQueries(["viewer.connectedCalendars"]);
utils.viewer.connectedCalendars.invalidate();
},
});
return (
@@ -258,11 +266,11 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
<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"
className="mb-0 flex flex-wrap items-center gap-4 sm:flex-nowrap 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">
<div className="flex items-center">
<AdditionalCalendarSelector isLoading={mutation.isLoading} />
</div>
)}
@@ -282,7 +290,7 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
<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">
<div className="justify-end md:w-6/12">
<DestinationCalendarSelector
onChange={mutation.mutate}
hidePlaceholder
-43
View File
@@ -1,43 +0,0 @@
import Image from "next/image";
import Link from "next/link";
import { useLocale } from "@calcom/lib/hooks/useLocale";
export default function AppStoreCategories({
categories,
}: {
categories: {
name: string;
count: number;
}[];
}) {
const { t } = useLocale();
return (
<div className="mb-16">
<h2 className="mb-2 text-lg font-semibold text-gray-900">{t("popular_categories")}</h2>
<div className="grid-col-1 grid w-full gap-3 overflow-scroll sm:grid-flow-col">
{categories.map((category) => (
<Link key={category.name} href={"/apps/categories/" + category.name}>
<a
data-testid={`app-store-category-${category.name}`}
className="relative flex rounded-sm bg-gray-100 px-6 py-4 sm:block">
<div className="min-w-24 -ml-5 text-center sm:ml-0">
<Image
alt={category.name}
width="352"
height="252"
layout="responsive"
src={"/app-store/" + category.name + ".svg"}
/>
</div>
<div className="self-center">
<h3 className="font-medium capitalize">{category.name}</h3>
<p className="text-sm text-gray-500">{category.count} apps</p>
</div>
</a>
</Link>
))}
</div>
</div>
);
}
@@ -23,7 +23,7 @@ const DestinationCalendarSelector = ({
destinationCalendar,
}: Props): JSX.Element | null => {
const { t } = useLocale();
const query = trpc.useQuery(["viewer.connectedCalendars"]);
const query = trpc.viewer.connectedCalendars.useQuery();
const [selectedOption, setSelectedOption] = useState<{ value: string; label: string } | null>(null);
// Extra styles to show prefixed text in react-select
@@ -2,6 +2,8 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { ReactNode, useEffect, useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Icon } from "@calcom/ui";
import { ListItem, ListItemText, ListItemTitle } from "@calcom/ui/v2/core/List";
import classNames from "@lib/classNames";
@@ -16,7 +18,10 @@ function IntegrationListItem(props: {
children?: ReactNode;
logo: string;
destination?: boolean;
separate?: boolean;
invalidCredential?: boolean;
}): JSX.Element {
const { t } = useLocale();
const router = useRouter();
const { hl } = router.query;
const [highlight, setHighlight] = useState(hl === props.slug);
@@ -32,7 +37,8 @@ function IntegrationListItem(props: {
<ListItem
expanded={!!props.children}
className={classNames(
"my-0 flex-col border transition-colors duration-500 first:rounded-t-md last:rounded-b-md",
props.separate ? "rounded-md" : "first:rounded-t-md last:rounded-b-md",
"my-0 flex-col border transition-colors duration-500 ",
highlight ? "bg-yellow-100" : ""
)}>
<div className={classNames("flex w-full flex-1 items-center space-x-2 p-4 rtl:space-x-reverse")}>
@@ -42,10 +48,19 @@ function IntegrationListItem(props: {
<Link href={"/apps/" + props.slug}>{props.name || title}</Link>
</ListItemTitle>
<ListItemText component="p">{props.description}</ListItemText>
{/* Alert error that key stopped working. */}
{props.invalidCredential && (
<div className="flex items-center space-x-2">
<Icon.FiAlertCircle className="w-8 text-red-500 sm:w-4" />
<ListItemText component="p" className="whitespace-pre-wrap text-red-500">
{t("invalid_credential")}
</ListItemText>
</div>
)}
</div>
<div>{props.actions}</div>
</div>
{props.children && <div className="w-full border-t border-gray-200">{props.children}</div>}
{props.children && <div className="w-full">{props.children}</div>}
</ListItem>
);
}
@@ -0,0 +1,63 @@
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
import { InstallAppButton } from "@calcom/app-store/components";
import { classNames } from "@calcom/lib";
import useApp from "@calcom/lib/hooks/useApp";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { Icon } from "@calcom/ui/Icon";
import { Button } from "@calcom/ui/components/button";
import { showToast } from "@calcom/ui/v2";
/**
* Use this component to allow installing an app from anywhere on the app.
* Use of this component requires you to remove custom InstallAppButtonComponent so that it can manage the redirection itself
*/
export default function OmniInstallAppButton({ appId, className }: { appId: string; className: string }) {
const { t } = useLocale();
const { data: app } = useApp(appId);
const utils = trpc.useContext();
const mutation = useAddAppMutation(null, {
onSuccess: () => {
//TODO: viewer.appById might be replaced with viewer.apps so that a single query needs to be invalidated.
utils.viewer.appById.invalidate({ appId });
utils.viewer.apps.invalidate({ extendsFeature: "EventType" });
showToast(t("app_successfully_installed"), "success");
},
onError: (error) => {
if (error instanceof Error) showToast(error.message || t("app_could_not_be_installed"), "error");
},
});
if (!app) {
return null;
}
return (
<InstallAppButton
type={app.type}
isProOnly={app.isProOnly}
wrapperClassName={classNames("[@media(max-width:260px)]:w-full", className)}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
onClick: () => {
mutation.mutate({ type: app.type, variant: app.variant, slug: app.slug, isOmniInstall: true });
},
};
}
return (
<Button
loading={mutation.isLoading}
color="secondary"
className="[@media(max-width:260px)]:w-full [@media(max-width:260px)]:justify-center"
StartIcon={Icon.FiPlus}
{...props}>
{t("install")}
</Button>
);
}}
/>
);
}
-79
View File
@@ -1,79 +0,0 @@
import Glide, { Options } from "@glidejs/glide";
import "@glidejs/glide/dist/css/glide.core.min.css";
import "@glidejs/glide/dist/css/glide.theme.min.css";
import { useEffect, useRef } from "react";
import { Icon } from "@calcom/ui/Icon";
const Slider = <T extends string | unknown>({
title = "",
className = "",
items,
itemKey = (item) => `${item}`,
renderItem,
options = {},
}: {
title?: string;
className?: string;
items: T[];
itemKey?: (item: T) => string;
renderItem?: (item: T) => JSX.Element;
options?: Options;
}) => {
const glide = useRef(null);
const slider = useRef<Glide.Properties | null>(null);
useEffect(() => {
if (glide.current) {
slider.current = new Glide(glide.current, {
type: "carousel",
...options,
}).mount();
}
return () => slider.current?.destroy();
}, [options]);
return (
<div className={`mb-2 ${className}`}>
<style jsx global>
{`
.glide__slide {
height: auto !important;
}
`}
</style>
<div className="glide" ref={glide}>
<div className="flex cursor-default">
{title && (
<div>
<h2 className="mt-0 mb-2 text-lg font-semibold text-gray-900">{title}</h2>
</div>
)}
<div className="glide__arrows ml-auto" data-glide-el="controls">
<button data-glide-dir="<" className="mr-4">
<Icon.FiArrowLeft className="h-5 w-5 text-gray-600 hover:text-black" />
</button>
<button data-glide-dir=">">
<Icon.FiArrowRight className="h-5 w-5 text-gray-600 hover:text-black" />
</button>
</div>
</div>
<div className="glide__track" data-glide-el="track">
<ul className="glide__slides">
{items.map((item) => {
if (typeof renderItem !== "function") return null;
return (
<li key={itemKey(item)} className="glide__slide h-auto pl-0">
{renderItem(item)}
</li>
);
})}
</ul>
</div>
</div>
</div>
);
};
export default Slider;
@@ -1,30 +0,0 @@
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { App } from "@calcom/types/App";
import AppCard from "./AppCard";
import Slider from "./Slider";
const TrendingAppsSlider = <T extends App>({ items }: { items: T[] }) => {
const { t } = useLocale();
return (
<Slider<T>
className="mb-16"
title={t("trending_apps")}
items={items.filter((app) => !!app.trending)}
itemKey={(app) => app.name}
options={{
perView: 3,
breakpoints: {
768 /* and below */: {
perView: 1,
},
},
}}
renderItem={(app) => <AppCard app={app} />}
/>
);
};
/** @deprecated use v2 instead, located at packages/ui/v2/core/apps */
export default TrendingAppsSlider;
-62
View File
@@ -1,62 +0,0 @@
import { signIn } from "next-auth/react";
import { Dispatch, SetStateAction } from "react";
import { useFormContext } from "react-hook-form";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { trpc } from "@calcom/trpc/react";
import Button from "@calcom/ui/Button";
import { useLocale } from "@lib/hooks/useLocale";
interface Props {
email: string;
samlTenantID: string;
samlProductID: string;
hostedCal: boolean;
setErrorMessage: Dispatch<SetStateAction<string | null>>;
}
export default function SAMLLogin(props: Props) {
const { t } = useLocale();
const methods = useFormContext();
const telemetry = useTelemetry();
const mutation = trpc.useMutation("viewer.public.samlTenantProduct", {
onSuccess: async (data) => {
await signIn("saml", {}, { tenant: data.tenant, product: data.product });
},
onError: (err) => {
props.setErrorMessage(err.message);
},
});
return (
<div className="mt-5">
<Button
color="secondary"
data-testid="saml"
className="flex w-full justify-center"
onClick={async (event) => {
event.preventDefault();
// track Google logins. Without personal data/payload
telemetry.event(telemetryEventTypes.googleLogin, collectPageParameters());
if (!props.hostedCal) {
await signIn("saml", {}, { tenant: props.samlTenantID, product: props.samlProductID });
} else {
if (props.email.length === 0) {
props.setErrorMessage(t("saml_email_required"));
return;
}
// hosted solution, fetch tenant and product from the backend
mutation.mutate({
email: methods.getValues("email"),
});
}
}}>
{t("signin_with_saml")}
</Button>
</div>
);
}
@@ -1,86 +0,0 @@
import { useRouter } from "next/router";
import { useForm } from "react-hook-form";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { trpc } from "@calcom/trpc/react";
import { Button } from "@calcom/ui";
import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { Form } from "@calcom/ui/form/fields";
import { HttpError } from "@lib/core/http/error";
export function NewScheduleButton({ name = "new-schedule" }: { name?: string }) {
const router = useRouter();
const { t } = useLocale();
const form = useForm<{
name: string;
}>();
const { register } = form;
const createMutation = trpc.useMutation("viewer.availability.schedule.create", {
onSuccess: async ({ schedule }) => {
await router.push("/availability/" + schedule.id);
showToast(t("schedule_created_successfully", { scheduleName: schedule.name }), "success");
},
onError: (err) => {
if (err instanceof HttpError) {
const message = `${err.statusCode}: ${err.message}`;
showToast(message, "error");
}
if (err.data?.code === "UNAUTHORIZED") {
const message = `${err.data.code}: You are not able to create this event`;
showToast(message, "error");
}
},
});
return (
<Dialog name={name} clearQueryParamsOnClose={["copy-schedule-id"]}>
<DialogTrigger asChild>
<Button data-testid={name} StartIcon={Icon.FiPlus}>
{t("new_schedule_btn")}
</Button>
</DialogTrigger>
<DialogContent>
<div className="mb-8">
<h3 className="text-lg font-bold leading-6 text-gray-900" id="modal-title">
{t("add_new_schedule")}
</h3>
</div>
<Form
form={form}
handleSubmit={(values) => {
createMutation.mutate(values);
}}>
<div className="mt-3 space-y-2">
<label htmlFor="label" className="block text-sm font-medium text-gray-700">
{t("name")}
</label>
<div className="mt-1">
<input
type="text"
id="name"
required
className="block w-full rounded-sm border-gray-300 text-sm"
placeholder={t("default_schedule_name")}
{...register("name")}
/>
</div>
</div>
<div className="mt-8 flex flex-row-reverse gap-x-2">
<Button type="submit" loading={createMutation.isLoading}>
{t("continue")}
</Button>
<DialogClose asChild>
<Button color="secondary">{t("cancel")}</Button>
</DialogClose>
</div>
</Form>
</DialogContent>
</Dialog>
);
}
@@ -1,350 +0,0 @@
/**
* @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";
import { GroupBase, Props } from "react-select";
import dayjs, { Dayjs, ConfigType } from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import Button from "@calcom/ui/Button";
import Dropdown, { DropdownMenuContent, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
import { defaultDayRange } from "@lib/availability";
import { weekdayNames } from "@lib/core/i18n/weekday";
import useMeQuery from "@lib/hooks/useMeQuery";
import { TimeRange } from "@lib/types/schedule";
import Select from "@components/ui/form/Select";
/** Begin Time Increments For Select */
const increment = 15;
type Option = {
readonly label: string;
readonly value: number;
};
/**
* Creates an array of times on a 15 minute interval from
* 00:00:00 (Start of day) to
* 23:45:00 (End of day with enough time for 15 min booking)
*/
const useOptions = () => {
// Get user so we can determine 12/24 hour format preferences
const query = useMeQuery();
const { timeFormat } = query.data || { timeFormat: null };
const [filteredOptions, setFilteredOptions] = useState<Option[]>([]);
const options = useMemo(() => {
const end = dayjs().utc().endOf("day");
let t: Dayjs = dayjs().utc().startOf("day");
const options: Option[] = [];
while (t.isBefore(end)) {
options.push({
value: t.toDate().valueOf(),
label: dayjs(t)
.utc()
.format(timeFormat === 12 ? "h:mma" : "HH:mm"),
});
t = t.add(increment, "minutes");
}
return options;
}, [timeFormat]);
const filter = useCallback(
({ offset, limit, current }: { offset?: ConfigType; limit?: ConfigType; current?: ConfigType }) => {
if (current) {
const currentOption = options.find((option) => option.value === dayjs(current).toDate().valueOf());
if (currentOption) setFilteredOptions([currentOption]);
} else
setFilteredOptions(
options.filter((option) => {
const time = dayjs(option.value);
return (!limit || time.isBefore(limit)) && (!offset || time.isAfter(offset));
})
);
},
[options]
);
return { options: filteredOptions, filter };
};
type TimeRangeFieldProps = {
name: string;
className?: string;
};
const LazySelect = ({
value,
min,
max,
...props
}: Omit<Props<Option, false, GroupBase<Option>>, "value"> & {
value: ConfigType;
min?: ConfigType;
max?: ConfigType;
}) => {
// Lazy-loaded options, otherwise adding a field has a noticable redraw delay.
const { options, filter } = useOptions();
useEffect(() => {
filter({ current: value });
}, [filter, value]);
return (
<Select
options={options}
onMenuOpen={() => {
if (min) filter({ offset: min });
if (max) filter({ limit: max });
}}
value={options.find((option) => option.value === dayjs(value).toDate().valueOf())}
onMenuClose={() => filter({ current: value })}
{...props}
/>
);
};
const TimeRangeField = ({ name, className }: TimeRangeFieldProps) => {
const { watch } = useFormContext();
const minEnd = watch(`${name}.start`);
const maxStart = watch(`${name}.end`);
return (
<div className={classNames("flex flex-grow items-center space-x-3", className)}>
<Controller
name={`${name}.start`}
render={({ field: { onChange, value } }) => {
return (
<LazySelect
className="w-[120px]"
value={value}
max={maxStart}
onChange={(option) => {
onChange(new Date(option?.value as number));
}}
/>
);
}}
/>
<span>-</span>
<Controller
name={`${name}.end`}
render={({ field: { onChange, value } }) => (
<LazySelect
className="flex-grow sm:w-[120px]"
value={value}
min={minEnd}
onChange={(option) => {
onChange(new Date(option?.value as number));
}}
/>
)}
/>
</div>
);
};
type ScheduleBlockProps = {
day: number;
weekday: string;
name: string;
};
const CopyTimes = ({ disabled, onApply }: { disabled: number[]; onApply: (selected: number[]) => void }) => {
const [selected, setSelected] = useState<number[]>([]);
const { i18n, t } = useLocale();
return (
<div className="m-4 space-y-2 py-4">
<p className="h6 text-xs font-medium uppercase text-neutral-400">Copy times to</p>
<ol className="space-y-2">
{weekdayNames(i18n.language).map((weekday, num) => (
<li key={weekday}>
<label className="flex w-full items-center justify-between">
<span>{weekday}</span>
<input
value={num}
defaultChecked={disabled.includes(num)}
disabled={disabled.includes(num)}
onChange={(e) => {
if (e.target.checked && !selected.includes(num)) {
setSelected(selected.concat([num]));
} else if (!e.target.checked && selected.includes(num)) {
setSelected(selected.slice(selected.indexOf(num), 1));
}
}}
type="checkbox"
className="inline-block rounded-sm border-gray-300 text-neutral-900 focus:ring-neutral-500 disabled:text-neutral-400"
/>
</label>
</li>
))}
</ol>
<div className="pt-2">
<Button className="w-full justify-center" color="primary" onClick={() => onApply(selected)}>
{t("apply")}
</Button>
</div>
</div>
);
};
/**
* @deprecated
* use Component in "/packages/features/schedules/components/Schedule";
**/
export const DayRanges = ({
name,
defaultValue = [defaultDayRange],
}: {
name: string;
defaultValue?: TimeRange[];
}) => {
const { setValue, watch } = useFormContext();
// XXX: Hack to make copying times work; `fields` is out of date until save.
const watcher = watch(name);
const { t } = useLocale();
const { fields, replace, append, remove } = useFieldArray({
name,
});
useEffect(() => {
if (defaultValue.length && !fields.length) {
replace(defaultValue);
}
}, [replace, defaultValue, fields.length]);
const handleAppend = () => {
// FIXME: Fix type-inference, can't get this to work. @see https://github.com/react-hook-form/react-hook-form/issues/4499
const nextRangeStart = dayjs((fields[fields.length - 1] as unknown as TimeRange).end);
const nextRangeEnd = dayjs(nextRangeStart).add(1, "hour");
if (nextRangeEnd.isBefore(nextRangeStart.endOf("day"))) {
return append({
start: nextRangeStart.toDate(),
end: nextRangeEnd.toDate(),
});
}
};
return (
<div className="space-y-2">
{fields.map((field, index) => (
<div key={field.id} className="flex items-center rtl:space-x-reverse">
<div className="flex flex-grow space-x-1 sm:flex-grow-0">
<TimeRangeField name={`${name}.${index}`} />
<Button
type="button"
size="icon"
color="minimal"
StartIcon={Icon.FiTrash}
onClick={() => remove(index)}
/>
</div>
{index === 0 && (
<div className="absolute top-2 right-0 text-right sm:relative sm:top-0 sm:flex-grow">
<Tooltip content={t("add_time_availability") as string}>
<Button
className="text-neutral-400"
type="button"
color="minimal"
size="icon"
StartIcon={Icon.FiPlus}
onClick={handleAppend}
/>
</Tooltip>
<Dropdown>
<Tooltip content={t("duplicate") as string}>
<DropdownMenuTrigger asChild>
<Button
type="button"
color="minimal"
size="icon"
StartIcon={Icon.FiCopy}
onClick={handleAppend}
/>
</DropdownMenuTrigger>
</Tooltip>
<DropdownMenuContent>
<CopyTimes
disabled={[parseInt(name.substring(name.lastIndexOf(".") + 1), 10)]}
onApply={(selected) =>
selected.forEach((day) => {
// TODO: Figure out why this is different?
// console.log(watcher, fields);
setValue(name.substring(0, name.lastIndexOf(".") + 1) + day, watcher);
})
}
/>
</DropdownMenuContent>
</Dropdown>
</div>
)}
</div>
))}
</div>
);
};
const ScheduleBlock = ({ name, day, weekday }: ScheduleBlockProps) => {
const { t } = useLocale();
const form = useFormContext();
const watchAvailable = form.watch(`${name}.${day}`, []);
return (
<fieldset className="relative flex flex-col justify-between space-y-2 py-5 sm:flex-row sm:space-y-0">
<label
className={classNames(
"flex space-x-2 rtl:space-x-reverse",
!watchAvailable.length ? "w-full" : "w-1/3"
)}>
<div className={classNames(!watchAvailable.length ? "w-1/3" : "w-full")}>
<input
type="checkbox"
checked={watchAvailable.length}
onChange={(e) => {
form.setValue(`${name}.${day}`, e.target.checked ? [defaultDayRange] : []);
}}
className="inline-block rounded-sm border-gray-300 text-neutral-900 focus:ring-neutral-500"
/>
<span className="ml-2 inline-block text-sm capitalize">{weekday}</span>
</div>
{!watchAvailable.length && (
<div className="flex-grow text-right text-sm text-gray-500 sm:flex-shrink">
{t("no_availability")}
</div>
)}
</label>
{!!watchAvailable.length && (
<div className="flex-grow">
<DayRanges name={`${name}.${day}`} defaultValue={[]} />
</div>
)}
</fieldset>
);
};
/**
* @deprecated
* use Component in "/packages/features/schedules/components/Schedule";
**/
const Schedule = ({ name }: { name: string }) => {
const { i18n } = useLocale();
return (
<fieldset className="divide-y divide-gray-200">
{weekdayNames(i18n.language).map((weekday, num) => (
<ScheduleBlock key={num} name={name} weekday={weekday} day={num} />
))}
</fieldset>
);
};
export default Schedule;
@@ -1,83 +0,0 @@
/**
* @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";
import { availabilityAsString } from "@calcom/lib/availability";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Availability } from "@calcom/prisma/client";
import { inferQueryOutput } from "@calcom/trpc/react";
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,
isDeleting = false,
}: {
schedule: inferQueryOutput<"viewer.availability.list">["schedules"][number];
deleteFunction: ({ scheduleId }: { scheduleId: number }) => void;
isDeleting: boolean;
}) {
const { t, i18n } = useLocale();
return (
<li key={schedule.id}>
<div className="flex items-center justify-between py-5 hover:bg-neutral-50 ltr:pl-4 rtl:pr-4 sm:ltr:pl-0 sm:rtl:pr-0">
<div className="group flex w-full items-center justify-between hover:bg-neutral-50 sm:px-6">
<Link href={"/availability/" + schedule.id}>
<a className="flex-grow truncate text-sm" title={schedule.name}>
<div>
<span className="truncate font-medium text-neutral-900">{schedule.name}</span>
{schedule.isDefault && (
<span className="ml-2 inline items-center rounded-sm bg-yellow-100 px-1.5 py-0.5 text-xs font-medium text-yellow-800">
{t("default")}
</span>
)}
</div>
<p className="mt-1 text-xs text-neutral-500">
{schedule.availability.map((availability: Availability) => (
<Fragment key={availability.id}>
{availabilityAsString(availability, { locale: i18n.language })}
<br />
</Fragment>
))}
</p>
</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" />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<Button
disabled={isDeleting}
onClick={() => {
deleteFunction({
scheduleId: schedule.id,
});
}}
type="button"
color="warn"
className="w-full font-normal"
StartIcon={isDeleting ? undefined : Icon.FiTrash}
loading={isDeleting}>
{isDeleting ? t("deleting") : t("delete_schedule")}
</Button>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</div>
</li>
);
}
@@ -1,8 +1,9 @@
import React from "react";
import { SkeletonText } from "@calcom/ui";
import { SkeletonText } from "@calcom/ui/v2";
import classNames from "@lib/classNames";
/** @deprecated Use `apps/web/components/v2/availability/SkeletonLoader.tsx` */
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">
@@ -20,26 +21,30 @@ function SkeletonItem() {
<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 width="32" height="4" />
<SkeletonText width="32" height="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 width="12" height="6" />
<SkeletonText className="h-6 w-12" />
</div>
</div>
</li>
);
}
export const AvailabilitySelectSkeletonLoader = () => {
export const SelectSkeletonLoader = ({ className }: { className?: string }) => {
return (
<li className="group flex w-full items-center justify-between rounded-sm border border-gray-200 px-[10px] py-3">
<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 width="32" height="4" />
<SkeletonText width="4" height="4" />
<SkeletonText className="h-4 w-32" />
<SkeletonText className="h-4 w-4" />
</div>
</div>
</li>
@@ -1,33 +1,34 @@
import { getEventLocationType, locationKeyToString } from "@calcom/app-store/locations";
import { classNames } from "@calcom/lib";
import Tooltip from "@calcom/ui/v2/core/Tooltip";
import { Props } from "./pages/AvailabilityPage";
export function AvailableEventLocations({ locations }: { locations: Props["eventType"]["locations"] }) {
return locations.length ? (
<div>
<div className="dark:text-darkgray-600 mr-6 flex w-full flex-col space-y-2 break-words text-sm text-gray-600">
{locations.map((location) => {
const eventLocationType = getEventLocationType(location.type);
if (!eventLocationType) {
// It's possible that the location app got uninstalled
return null;
}
return (
<div key={location.type} className="flex flex-row items-center text-sm font-medium">
<img
src={eventLocationType.iconUrl}
className={classNames(
"mr-[10px] ml-[2px] h-4 w-4 opacity-70 dark:opacity-100 ",
!eventLocationType.iconUrl?.includes("api") ? "dark:invert" : ""
)}
alt={`${eventLocationType.label} icon`}
/>
<span key={location.type}>{locationKeyToString(location)} </span>
</div>
);
})}
</div>
<div className="dark:text-darkgray-600 mr-6 flex w-full flex-col space-y-2 break-words text-sm text-gray-600">
{locations.map((location) => {
const eventLocationType = getEventLocationType(location.type);
if (!eventLocationType) {
// It's possible that the location app got uninstalled
return null;
}
return (
<div key={location.type} className="flex flex-row items-center text-sm font-medium">
<img
src={eventLocationType.iconUrl}
className={classNames(
"mr-[10px] ml-[2px] h-4 w-4 opacity-70 dark:opacity-100 ",
!eventLocationType.iconUrl?.includes("api") ? "dark:invert" : ""
)}
alt={`${eventLocationType.label} icon`}
/>
<Tooltip content={locationKeyToString(location)}>
<p className="truncate">{locationKeyToString(location)}</p>
</Tooltip>
</div>
);
})}
</div>
) : (
<></>
+31 -16
View File
@@ -4,15 +4,18 @@ import { FC, useEffect, useState } from "react";
import dayjs, { Dayjs } from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { TimeFormat } from "@calcom/lib/timeFormat";
import { nameOfDay } from "@calcom/lib/weekday";
import type { Slot } from "@calcom/trpc/server/routers/viewer/slots";
import { SkeletonContainer, SkeletonText } from "@calcom/ui";
import { SkeletonContainer, SkeletonText } from "@calcom/ui/v2";
import { ToggleGroup } from "@calcom/ui/v2/core/form/ToggleGroup";
import classNames from "@lib/classNames";
import { timeZone } from "@lib/clock";
type AvailableTimesProps = {
timeFormat: string;
timeFormat: TimeFormat;
onTimeFormatChange: (is24Hour: boolean) => void;
eventTypeId: number;
recurringCount: number | undefined;
eventTypeSlug: string;
@@ -31,6 +34,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
eventTypeSlug,
recurringCount,
timeFormat,
onTimeFormatChange,
seatsPerTimeSlot,
ethSignature,
}) => {
@@ -48,16 +52,27 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
return (
<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")))}
</span>
<span className="text-bookinglight font-medium">
{date.format(", D ")}
{date.toDate().toLocaleString(i18n.language, { month: "long" })}
</span>
<div className="mb-6 flex items-center text-left text-base">
<div className="mr-4">
<span className="text-bookingdarker dark:text-darkgray-800 font-semibold text-gray-900">
{nameOfDay(i18n.language, Number(date.format("d")), "short")}
</span>
<span className="text-bookinglight font-medium">
, {date.toDate().toLocaleString(i18n.language, { month: "long" })} {date.format(" D ")}
</span>
</div>
<div className="ml-auto">
<ToggleGroup
onValueChange={(timeFormat) => onTimeFormatChange(timeFormat === "24")}
defaultValue={timeFormat === TimeFormat.TWELVE_HOUR ? "12" : "24"}
options={[
{ value: "12", label: t("12_hour_short") },
{ value: "24", label: t("24_hour_short") },
]}
/>
</div>
</div>
<div className="grid flex-grow grid-cols-1 gap-x-2 overflow-y-auto sm:block md:h-[364px]">
<div className="-mb-5 grid flex-grow grid-cols-1 gap-x-2 overflow-y-auto sm:block md:h-[364px]">
{slots.length > 0 &&
slots.map((slot) => {
type BookingURL = {
@@ -65,10 +80,10 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
query: Record<string, string | number | string[] | undefined>;
};
const bookingUrl: BookingURL = {
pathname: "book",
pathname: router.pathname.endsWith("/embed") ? "../book" : "book",
query: {
...router.query,
date: dayjs(slot.time).format(),
date: dayjs.utc(slot.time).tz(timeZone()).format(),
type: eventTypeId,
slug: eventTypeSlug,
/** Treat as recurring only when a count exist and it's not a rescheduling workflow */
@@ -137,13 +152,13 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
{isLoading && !slots.length && (
<>
<SkeletonContainer className="mb-2">
<SkeletonText width="full" height="20" />
<SkeletonText className="h-5 w-full" />
</SkeletonContainer>
<SkeletonContainer className="mb-2">
<SkeletonText width="full" height="20" />
<SkeletonText className="h-5 w-full" />
</SkeletonContainer>
<SkeletonContainer className="mb-2">
<SkeletonText width="full" height="20" />
<SkeletonText className="h-5 w-full" />
</SkeletonContainer>
</>
)}
@@ -0,0 +1,107 @@
import { SchedulingType } from "@prisma/client";
import { FC, ReactNode } from "react";
import { classNames } from "@calcom/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Icon } from "@calcom/ui/Icon";
import { UserAvatars } from "@components/booking/UserAvatars";
import EventTypeDescriptionSafeHTML from "@components/eventtype/EventTypeDescriptionSafeHTML";
import type { AvailabilityPageProps } from "../../pages/[user]/[type]";
import type { BookPageProps } from "../../pages/[user]/book";
import type { DynamicAvailabilityPageProps } from "../../pages/d/[link]/[slug]";
import type { HashLinkPageProps } from "../../pages/d/[link]/book";
import type { AvailabilityTeamPageProps } from "../../pages/team/[slug]/[type]";
import type { TeamBookingPageProps } from "../../pages/team/[slug]/book";
import { AvailableEventLocations } from "./AvailableEventLocations";
interface Props {
profile:
| AvailabilityPageProps["profile"]
| HashLinkPageProps["profile"]
| TeamBookingPageProps["profile"]
| BookPageProps["profile"]
| AvailabilityTeamPageProps["profile"]
| DynamicAvailabilityPageProps["profile"];
eventType:
| AvailabilityPageProps["eventType"]
| HashLinkPageProps["eventType"]
| TeamBookingPageProps["eventType"]
| BookPageProps["eventType"]
| AvailabilityTeamPageProps["eventType"]
| DynamicAvailabilityPageProps["eventType"];
isBookingPage?: boolean;
children: ReactNode;
isMobile?: boolean;
rescheduleUid?: string;
}
const BookingDescription: FC<Props> = (props) => {
const { profile, eventType, isBookingPage = false, children } = props;
const { t } = useLocale();
return (
<>
<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={classNames(
"flex",
isBookingPage && "dark:text-darkgray-600 text-sm font-medium text-gray-600"
)}>
<div>
<Icon.FiInfo
className={classNames(
"mr-[10px] ml-[2px] inline-block h-4 w-4",
isBookingPage && "dark:text-darkgray-600 -mt-1 text-gray-500"
)}
/>
</div>
<div className="max-w-[calc(100%_-_2rem)] flex-shrink break-words">
<EventTypeDescriptionSafeHTML eventType={eventType} />
</div>
</div>
)}
{eventType?.requiresConfirmation && (
<div
className={classNames(
"flex items-center",
isBookingPage && "dark:text-darkgray-600 text-sm font-medium text-gray-600"
)}>
<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 as AvailabilityPageProps["eventType"]["locations"]}
/>
<p
className={classNames(
"text-sm font-medium",
isBookingPage && "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")}
</p>
{children}
</div>
</>
);
};
export default BookingDescription;
+135 -127
View File
@@ -1,55 +1,60 @@
import { BookingStatus } from "@prisma/client";
import { useRouter } from "next/router";
import { useState } from "react";
import { useState, useMemo } from "react";
import { EventLocationType, getEventLocationType } from "@calcom/app-store/locations";
import dayjs from "@calcom/dayjs";
import classNames from "@calcom/lib/classNames";
import { formatTime } from "@calcom/lib/date-fns";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
import { inferQueryInput, inferQueryOutput, trpc } from "@calcom/trpc/react";
import { RouterInputs, RouterOutputs, trpc } from "@calcom/trpc/react";
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
import { Badge } from "@calcom/ui/components/badge";
import { Button } from "@calcom/ui/components/button";
import { TextArea } from "@calcom/ui/form/fields";
import Badge from "@calcom/ui/v2/core/Badge";
import Button from "@calcom/ui/v2/core/Button";
import MeetingTimeInTimezones from "@calcom/ui/v2/core/MeetingTimeInTimezones";
import Tooltip from "@calcom/ui/v2/core/Tooltip";
import showToast from "@calcom/ui/v2/core/notifications";
import useMeQuery from "@lib/hooks/useMeQuery";
import { extractRecurringDates } from "@lib/parseDate";
import { EditLocationDialog } from "@components/dialog/EditLocationDialog";
import { RescheduleDialog } from "@components/dialog/RescheduleDialog";
import TableActions, { ActionType } from "@components/ui/TableActions";
type BookingListingStatus = inferQueryInput<"viewer.bookings">["status"];
type BookingListingStatus = RouterInputs["viewer"]["bookings"]["get"]["status"];
type BookingItem = inferQueryOutput<"viewer.bookings">["bookings"][number];
type BookingItem = RouterOutputs["viewer"]["bookings"]["get"]["bookings"][number];
type BookingItemProps = BookingItem & {
listingStatus: BookingListingStatus;
recurringBookings: inferQueryOutput<"viewer.bookings">["recurringInfo"];
recurringInfo: RouterOutputs["viewer"]["bookings"]["get"]["recurringInfo"][number] | undefined;
};
function BookingListItem(booking: BookingItemProps) {
// Get user so we can determine 12/24 hour format preferences
const query = useMeQuery();
const user = query.data;
const { t, i18n } = useLocale();
const { t } = useLocale();
const utils = trpc.useContext();
const router = useRouter();
const [rejectionReason, setRejectionReason] = useState<string>("");
const [rejectionDialogIsOpen, setRejectionDialogIsOpen] = useState(false);
const mutation = trpc.useMutation(["viewer.bookings.confirm"], {
onSuccess: () => {
setRejectionDialogIsOpen(false);
showToast(t("booking_confirmation_success"), "success");
utils.invalidateQueries("viewer.bookings");
const mutation = trpc.viewer.bookings.confirm.useMutation({
onSuccess: (data) => {
if (data.status === BookingStatus.REJECTED) {
setRejectionDialogIsOpen(false);
showToast(t("booking_rejection_success"), "success");
} else {
showToast(t("booking_confirmation_success"), "success");
}
utils.viewer.bookings.invalidate();
},
onError: () => {
showToast(t("booking_confirmation_failed"), "error");
utils.invalidateQueries("viewer.bookings");
utils.viewer.bookings.invalidate();
},
});
@@ -107,7 +112,9 @@ function BookingListItem(booking: BookingItemProps) {
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}${isTabRecurring && isRecurring ? "?allRemainingBookings=true" : ""}`,
href: `/success?uid=${booking.uid}&cancel=true${
isTabRecurring && isRecurring ? "&allRemainingBookings=true" : ""
}`,
icon: Icon.FiX,
},
{
@@ -157,11 +164,11 @@ function BookingListItem(booking: BookingItemProps) {
const startTime = dayjs(booking.startTime).format(isUpcoming ? "ddd, D MMM" : "D MMMM YYYY");
const [isOpenRescheduleDialog, setIsOpenRescheduleDialog] = useState(false);
const [isOpenSetLocationDialog, setIsOpenLocationDialog] = useState(false);
const setLocationMutation = trpc.useMutation("viewer.bookings.editLocation", {
const setLocationMutation = trpc.viewer.bookings.editLocation.useMutation({
onSuccess: () => {
showToast(t("location_updated"), "success");
setIsOpenLocationDialog(false);
utils.invalidateQueries("viewer.bookings");
utils.viewer.bookings.invalidate();
},
});
@@ -174,37 +181,24 @@ function BookingListItem(booking: BookingItemProps) {
setLocationMutation.mutate({ bookingId: booking.id, newLocation });
};
// Calculate the booking date(s) and setup recurring event data to show
let recurringStrings: string[] = [];
let recurringDates: Date[] = [];
// Getting accepted recurring dates to show
const recurringDates = booking.recurringInfo?.bookings[BookingStatus.ACCEPTED]
.concat(booking.recurringInfo?.bookings[BookingStatus.CANCELLED])
.concat(booking.recurringInfo?.bookings[BookingStatus.PENDING])
.sort((date1: Date, date2: Date) => date1.getTime() - date2.getTime());
if (booking.recurringBookings !== undefined && booking.eventType.recurringEvent?.freq !== undefined) {
[recurringStrings, recurringDates] = extractRecurringDates(booking, user?.timeZone, i18n);
}
const location = booking.location || "";
const onClick = () => {
const onClickTableData = () => {
router.push({
pathname: "/success",
query: {
date: booking.startTime,
// TODO: Booking when fetched should have id 0 already(for Dynamic Events).
type: booking.eventType.id || 0,
eventSlug: booking.eventType.slug,
user: user?.username || "",
name: booking.attendees[0] ? booking.attendees[0].name : undefined,
email: booking.attendees[0] ? booking.attendees[0].email : undefined,
location: location,
eventName: booking.eventType.eventName || "",
bookingId: booking.id,
recur: booking.recurringEventId,
reschedule: isConfirmed,
uid: booking.uid,
allRemainingBookings: isTabRecurring,
listingStatus: booking.listingStatus,
status: booking.status,
email: booking.attendees[0] ? booking.attendees[0].email : undefined,
},
});
};
return (
<>
<RescheduleDialog
@@ -253,20 +247,27 @@ function BookingListItem(booking: BookingItemProps) {
</Dialog>
<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}>
<td
className="hidden align-top ltr:pl-6 rtl:pr-6 sm:table-cell sm:min-w-[12rem]"
onClick={onClickTableData}>
<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")}
{formatTime(booking.startTime, user?.timeFormat, user?.timeZone)} -{" "}
{formatTime(booking.endTime, user?.timeFormat, user?.timeZone)}
<MeetingTimeInTimezones
timeFormat={user?.timeFormat}
userTimezone={user?.timeZone}
startTime={booking.startTime}
endTime={booking.endTime}
attendees={booking.attendees}
/>
</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}
@@ -277,24 +278,28 @@ function BookingListItem(booking: BookingItemProps) {
{t("pending_payment")}
</Badge>
)}
<div className="mt-2 text-sm text-gray-400">
<RecurringBookingsTooltip
booking={booking}
recurringStrings={recurringStrings}
recurringDates={recurringDates}
/>
</div>
{recurringDates !== undefined && (
<div className="mt-2 text-sm text-gray-400">
<RecurringBookingsTooltip booking={booking} recurringDates={recurringDates} />
</div>
)}
</div>
</td>
<td className={"w-full px-4" + (isRejected ? " line-through" : "")} onClick={onClick}>
<td className={"w-full px-4" + (isRejected ? " line-through" : "")} onClick={onClickTableData}>
{/* 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")}
{formatTime(booking.startTime, user?.timeFormat, user?.timeZone)} -{" "}
{formatTime(booking.endTime, user?.timeFormat, user?.timeZone)}
<MeetingTimeInTimezones
timeFormat={user?.timeFormat}
userTimezone={user?.timeZone}
startTime={booking.startTime}
endTime={booking.endTime}
attendees={booking.attendees}
/>
</div>
</div>
@@ -313,13 +318,11 @@ function BookingListItem(booking: BookingItemProps) {
{t("pending_payment")}
</Badge>
)}
<div className="text-sm text-gray-400 sm:hidden">
<RecurringBookingsTooltip
booking={booking}
recurringStrings={recurringStrings}
recurringDates={recurringDates}
/>
</div>
{recurringDates !== undefined && (
<div className="text-sm text-gray-400 sm:hidden">
<RecurringBookingsTooltip booking={booking} recurringDates={recurringDates} />
</div>
)}
</div>
<div className="cursor-pointer py-4">
@@ -338,7 +341,7 @@ function BookingListItem(booking: BookingItemProps) {
</div>
{booking.description && (
<div
className="max-w-52 md:max-w-96 truncate text-sm text-gray-600"
className="max-w-10/12 sm:max-w-40 md:max-w-56 xl:max-w-80 lg:max-w-64 truncate text-sm text-gray-600"
title={booking.description}>
&quot;{booking.description}&quot;
</div>
@@ -379,20 +382,26 @@ function BookingListItem(booking: BookingItemProps) {
interface RecurringBookingsTooltipProps {
booking: BookingItemProps;
recurringStrings: string[];
recurringDates: Date[];
}
const RecurringBookingsTooltip = ({
booking,
recurringStrings,
recurringDates,
}: RecurringBookingsTooltipProps) => {
const RecurringBookingsTooltip = ({ booking, recurringDates }: RecurringBookingsTooltipProps) => {
// Get user so we can determine 12/24 hour format preferences
const query = useMeQuery();
const user = query.data;
const { t } = useLocale();
const now = new Date();
const recurringCount = recurringDates.filter((date) => {
return (
date >= now &&
!booking.recurringInfo?.bookings[BookingStatus.CANCELLED]
.map((date) => date.toDateString())
.includes(date.toDateString())
);
}).length;
return (
(booking.recurringBookings &&
(booking.recurringInfo &&
booking.eventType?.recurringEvent?.freq &&
(booking.listingStatus === "recurring" ||
booking.listingStatus === "unconfirmed" ||
@@ -400,11 +409,20 @@ const RecurringBookingsTooltip = ({
<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>
))}>
content={recurringDates.map((aDate, key) => {
const pastOrCancelled =
aDate < now ||
booking.recurringInfo?.bookings[BookingStatus.CANCELLED]
.map((date) => date.toDateString())
.includes(aDate.toDateString());
return (
<p key={key} className={classNames(pastOrCancelled && "line-through")}>
{formatTime(aDate, user?.timeFormat, user?.timeZone)}
{" - "}
{dayjs(aDate).format("D MMMM YYYY")}
</p>
);
})}>
<div className="text-gray-600 dark:text-white">
<Icon.FiRefreshCcw
strokeWidth="3"
@@ -413,14 +431,12 @@ const RecurringBookingsTooltip = ({
<p className="mt-1 pl-5 text-xs">
{booking.status === BookingStatus.ACCEPTED
? `${t("event_remaining", {
count: recurringDates.length,
count: recurringCount,
})}`
: getEveryFreqFor({
t,
recurringEvent: booking.eventType.recurringEvent,
recurringCount: recurringDates.filter((date) => {
return date >= now;
}).length,
recurringCount: booking.recurringInfo.count,
})}
</p>
</div>
@@ -445,8 +461,9 @@ const FirstAttendee = ({
user: UserProps;
currentEmail: string | null | undefined;
}) => {
const { t } = useLocale();
return user.email === currentEmail ? (
<div className="inline-block">You</div>
<div className="inline-block">{t("you")}</div>
) : (
<a
key={user.email}
@@ -458,19 +475,19 @@ const FirstAttendee = ({
);
};
const Attendee: React.FC<{ email: string; children: React.ReactNode }> = ({ email, children }) => {
type AttendeeProps = {
name?: string;
email: string;
};
const Attendee = ({ email, name }: AttendeeProps) => {
return (
<a className=" hover:text-blue-500" href={"mailto:" + email} onClick={(e) => e.stopPropagation()}>
{children}
<a className="hover:text-blue-500" href={"mailto:" + email} onClick={(e) => e.stopPropagation()}>
{name || email}
</a>
);
};
interface AttendeeProps {
name: string;
email: string;
}
const DisplayAttendees = ({
attendees,
user,
@@ -478,42 +495,33 @@ const DisplayAttendees = ({
}: {
attendees: AttendeeProps[];
user: UserProps | null;
currentEmail: string | null | undefined;
currentEmail?: string | null;
}) => {
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 { t } = useLocale();
return (
<div className="text-sm text-gray-900">
{user && <FirstAttendee user={user} currentEmail={currentEmail} />}
{attendees.length > 1 ? <span>,&nbsp;</span> : <span>&nbsp;{t("and")}&nbsp;</span>}
<Attendee {...attendees[0]} />
{attendees.length > 1 && (
<>
<div className="inline-block text-sm text-gray-900">&nbsp;{t("and")}&nbsp;</div>
{attendees.length > 2 ? (
<Tooltip
content={attendees.slice(1).map((attendee) => (
<p key={attendee.email}>
<Attendee {...attendee} />
</p>
))}>
<div className="inline-block">{t("plus_more", { count: attendees.length - 1 })}</div>
</Tooltip>
) : (
<Attendee {...attendees[1]} />
)}
</>
)}
</div>
);
};
const Tag = ({ children, className = "" }: React.PropsWithChildren<{ className?: string }>) => {
+18 -22
View File
@@ -5,13 +5,14 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import useTheme from "@calcom/lib/hooks/useTheme";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import type { RecurringEvent } from "@calcom/types/Calendar";
import { Button } from "@calcom/ui/Button";
import { Icon } from "@calcom/ui/Icon";
import { Button, TextArea } from "@calcom/ui/components";
type Props = {
booking: {
title?: string;
uid?: string;
id?: number;
};
profile: {
name: string | null;
@@ -21,13 +22,14 @@ type Props = {
team?: string | null;
setIsCancellationMode: (value: boolean) => void;
theme: string | null;
allRemainingBookings: boolean;
};
export default function CancelBooking(props: Props) {
const [cancellationReason, setCancellationReason] = useState<string>("");
const { t } = useLocale();
const router = useRouter();
const { booking, profile, team } = props;
const { booking, profile, team, allRemainingBookings } = props;
const [loading, setLoading] = useState(false);
const telemetry = useTelemetry();
const [error, setError] = useState<string | null>(booking ? null : t("booking_already_cancelled"));
@@ -50,37 +52,31 @@ export default function CancelBooking(props: Props) {
{!error && (
<div className="mt-5 sm:mt-6">
<label className="text-bookingdark font-medium dark:text-white">{t("cancellation_reason")}</label>
<textarea
<TextArea
placeholder={t("cancellation_reason_placeholder")}
value={cancellationReason}
onChange={(e) => setCancellationReason(e.target.value)}
className="mt-2 mb-3 w-full dark:border-gray-900 dark:bg-gray-700 dark:text-white sm:mb-3 "
className="mt-2 mb-4 w-full dark:border-gray-900 dark:bg-gray-700 dark:text-white "
rows={3}
/>
<div className=" flex flex-col-reverse rtl:space-x-reverse sm:flex-row">
{!props.recurringEvent && (
<div className=" border-bookinglightest mt-5 flex w-full justify-center border-t pt-3 sm:mt-0 sm:justify-start sm:border-0 sm:pt-0">
<Button
color="secondary"
className="border-0 sm:border"
onClick={() => router.push("/reschedule/" + booking?.uid)}>
{t("reschedule_this")}
</Button>
</div>
)}
<div className="mb-2 flex w-full space-x-2 text-right sm:mb-0 ">
<Button color="secondary" onClick={() => props.setIsCancellationMode(false)}>
<div className="flex flex-col-reverse rtl:space-x-reverse ">
<div className="ml-auto flex w-full space-x-4 ">
<Button
className="ml-auto"
color="secondary"
onClick={() => props.setIsCancellationMode(false)}>
{t("nevermind")}
</Button>
<Button
className="flex grow justify-center"
className="flex justify-center"
data-testid="cancel"
onClick={async () => {
setLoading(true);
const payload = {
uid: booking?.uid,
reason: cancellationReason,
id: booking?.id,
cancellationReason: cancellationReason,
allRemainingBookings,
};
telemetry.event(telemetryEventTypes.bookingCancelled, collectPageParameters());
@@ -97,7 +93,7 @@ export default function CancelBooking(props: Props) {
await router.push(
`/cancel/success?name=${props.profile.name}&title=${booking?.title}&eventPage=${
profile.slug
}&team=${team ? 1 : 0}&recurring=${!!props.recurringEvent}`
}&team=${team ? 1 : 0}&allRemainingBookings=${allRemainingBookings}`
);
} else {
setLoading(false);
@@ -109,7 +105,7 @@ export default function CancelBooking(props: Props) {
}
}}
loading={loading}>
{t("cancel_event")}
{props.allRemainingBookings ? t("cancel_all_remaining") : t("cancel_event")}
</Button>
</div>
</div>
@@ -1,8 +1,7 @@
import React from "react";
import { SkeletonText } from "@calcom/ui";
import { SkeletonText } from "@calcom/ui/v2";
/** @deprecated Use `apps/web/components/v2/bookings/SkeletonLoader.tsx` */
function SkeletonLoader() {
return (
<ul className="animate-pulse divide-y divide-neutral-200 rounded-md border border-gray-200 bg-white sm:overflow-hidden">
@@ -21,15 +20,15 @@ function SkeletonItem() {
<div className="flex-grow truncate text-sm">
<div className="flex">
<div className="flex flex-col space-y-2">
<SkeletonText width="16" height="5" />
<SkeletonText width="32" height="4" />
<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 width="16" height="6" />
<SkeletonText width="32" height="6" />
<SkeletonText className="h-6 w-16" />
<SkeletonText className="h-6 w-32" />
</div>
</div>
</li>
+2 -25
View File
@@ -1,25 +1,19 @@
import { FC, useEffect, useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Switch } from "@calcom/ui/v2";
import TimezoneSelect, { ITimezoneOption } from "@calcom/ui/v2/core/TimezoneSelect";
import { is24h, timeZone } from "../../lib/clock";
import { timeZone } from "../../lib/clock";
type Props = {
onSelectTimeZone: (selectedTimeZone: string) => void;
onToggle24hClock: (is24hClock: boolean) => void;
timeFormat: string;
hideTimeFormatToggle?: boolean;
};
const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone, timeFormat, hideTimeFormatToggle }) => {
const TimeOptions: FC<Props> = ({ onSelectTimeZone }) => {
const [selectedTimeZone, setSelectedTimeZone] = useState("");
const [is24hClock, setIs24hClock] = useState(timeFormat === "HH:mm" && true);
const { t } = useLocale();
useEffect(() => {
setIs24hClock(is24h());
setSelectedTimeZone(timeZone());
}, []);
@@ -28,28 +22,11 @@ const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone, timeFormat
onSelectTimeZone(timeZone(selectedTimeZone));
}
}, [selectedTimeZone, onSelectTimeZone]);
const handle24hClockToggle = (is24hClock: boolean) => {
setIs24hClock(is24hClock);
onToggle24hClock(is24h(is24hClock));
};
return selectedTimeZone !== "" ? (
<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>
{!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"

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