Compare commits

..
311 Commits
Author SHA1 Message Date
zomars 3ae4655449 v1.8.4 2022-08-01 17:42:06 -06:00
zomars 299f69aa7b Fixes successRedirectUrl 2022-08-01 17:20:06 -06:00
c1896fc833 Feat/onboarding username (#3580)
* fix: remove duplicate button in textusernamefield

* feat: adds username if not present in getting-started first onboarding step

* fix: credentials

* fix: typo

* fix: user type

* fix: type errors

* Validates successRedirectUrl earlier with zod

* Removes deprecated api endpoint

* Reduces the data needed for the frontend on onboarding

Co-authored-by: Agusti Fernandez Pardo <git@agusti.me>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2022-08-01 15:44:08 -06:00
zomars 54acf6d28f Sorts root scripts and adds prisma alias 2022-08-01 13:50:30 -06:00
Joe Au-Yeung 7e8f13148e Fix app id when creating credential (#3629) 2022-08-01 13:25:04 -06:00
b42f0ffadd Fixes unique constraint error of batchId when scheduling emails (#3542)
* make batchid unique + return response status 200

* use correct batchId

* change for loop and updateMany

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-08-01 10:20:21 +00:00
Hariom Balharaandkodiakhq[bot] 9346ae644f Use non shallow mode (#3620)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-08-01 09:40:38 +02:00
d6fd9b5df3 Hide dropdown under modal dialog (#3607)
Co-authored-by: Adam Garbowski <adamgarbowski90@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-31 21:48:17 +02:00
github-actions[bot]andCrowdin Bot 529adc4137 New Crowdin translations by Github Action (#3609)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-31 10:27:04 +02:00
Omar Lópezandkodiakhq[bot] c9d7412a43 Refactors twilio provider (#3604)
Prevents multiple twilio redeclarations

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-30 16:02:34 +00:00
Carina WollendorferandCarinaWolli dba48ab852 fix design issue (#3606)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-07-30 15:49:01 +00:00
0bca141b74 Fix last day of month handling logic in tests (#3605)
* Fix last day of month handling logic

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

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

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

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

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2022-07-30 09:33:32 -06:00
a4e91a0666 Improved German locale (#3521)
Improved and corrected the grammar and wording.

Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-30 16:56:43 +02:00
mohammed hussam 8efb98698d change prerequistic of node.js from min 14.x to 15.x (#3587) 2022-07-30 16:54:42 +02:00
Peer Richelsen 1403a567ec nit change to readme 2022-07-30 15:25:00 +02:00
Peer Richelsen bd6a84e986 added figma design system and help wanted shields to readme 2022-07-30 15:24:33 +02:00
Peer Richelsen bd41d9d5f0 added help wanted project board 2022-07-30 15:15:42 +02:00
github-actions[bot]andCrowdin Bot 7e7938a4d9 New Crowdin translations by Github Action (#3569)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-30 10:15:04 +02:00
zomars 2f03f18447 Fixes missing import 2022-07-29 22:34:57 -06:00
zomars 2b054862df Uses ISR on setup pages 2022-07-29 22:04:59 -06:00
zomars 382f11f740 Update [status].tsx 2022-07-29 21:52:43 -06:00
zomars 920050022a Update WebhookListContainer.tsx 2022-07-29 21:52:43 -06:00
zomars 95a0496f32 Type fixes 2022-07-29 21:52:43 -06:00
zomars 1ac36882ac Update Kbar.tsx 2022-07-29 21:52:43 -06:00
zomars a4b8cc8391 Fixes dotenv replacements 2022-07-29 19:30:02 -06:00
zomars e7a91d4594 Squashed commit of the following:
commit e84ca3c1fcb625f8cde160b31c1f2b5def78e0bf
Author: zomars <zomars@me.com>
Date:   Thu Jul 28 14:10:57 2022 -0600

    fix

commit 29025a1104c2853597b5f3abd0d26aaa27a76aa2
Author: zomars <zomars@me.com>
Date:   Thu Jul 28 14:10:04 2022 -0600

    Force vercel deploy

commit b070a6460cf871488340a22d71c14962f37e7023
Author: Hariom Balhara <hariombalhara@gmail.com>
Date:   Thu Jul 28 19:03:07 2022 +0530

    Avoid global variables leak by making the entire code IIFE (#3543) (#3568)

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

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

commit f05c7c40829947aefb0c67b931420bca18b79b29
Author: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Date:   Wed Jul 27 16:19:59 2022 -0400

    Add axiom to next config (#3556)

commit c3d05e86862277c270557bab98e170cc8b051afe
Author: Hariom Balhara <hariombalhara@gmail.com>
Date:   Wed Jul 27 14:12:09 2022 +0530

    Potential fix, broken workflow (#3545)

commit 36e1c9272fa9b86cbe9e53e03e83799820674f17
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 18:27:57 2022 -0600

    Update [schedule].tsx

commit f39535feea22a11a857d179f3506803602a95728
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 18:07:30 2022 -0600

    Update [status].tsx

commit c3ac1bcc8c7ff8dc7ebf178d23a6e5c7ea223749
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 17:53:43 2022 -0600

    Update [schedule].tsx

commit 39364d8476b3c582d4a7fe3f8d139c5d254e3ecf
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 17:43:13 2022 -0600

    Debugging prod

commit 4b428d22dec734dab19096b4fe07741f18e5bc5d
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 17:20:44 2022 -0600

    Update checkLicense.ts

commit 4212e6d61908face52d647bb661d71db959cf864
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 17:11:37 2022 -0600

    Booking fixes attempt

commit e3160027ea0ce40d129b445daeb3bb17711ac640
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 13:52:35 2022 -0600

    migration conflict fixes
2022-07-29 19:28:54 -06:00
Sagar 54c8c294fc Fix Deploy with Vercel and add note and build commands and root directory (#3590) 2022-07-29 18:49:15 -06:00
zomars 49f584a956 Fixes tailwind for EE code 2022-07-29 16:26:31 -06:00
Adam Garbowski 67f72223bf Fix broken getLocaleFromHeaders import (#3592) 2022-07-29 08:28:53 -06:00
Peer Richelsen 534efe53fe added status.cal.com badge 2022-07-29 15:47:44 +02:00
Leo Giovanetti 5437005c7e UI tweaks (#3576) 2022-07-29 15:23:54 +02:00
zomars b931cf2d7c Submodule sync 2022-07-28 14:25:38 -06:00
Omar Lópezandkodiakhq[bot] 464343f5ab Refactors EE code (#3490)
* WIP

* WIP

* Type and migration fixes

* Adds missing default import

* Fixes import

* Fixes tRPC imports in App Store

* Migrate stripe helpers

* WIP

* Type fixes

* Type fix?

* WIP

* WIP

* Update index.ts

* Fixes

* Update workflow.tsx

* Moved queries to lib

* Moves QueryCell

* Migrates MultiSelectCheckboxes

* WIP

* CryptoSection type fixes

* WIP

* Import fixes

* Build fixes

* Update app-providers.tsx

* Build fixes

* Upgrades hookform zod resolvers

* Build fixes

* Cleanup

* Build fixes

* Relocates QueryCell to ui

* Moved List and SkeletonLoader

* Revert QueryCell migration

* Can't use QueryCell here

* oops

* CryptoSection cleanup

* Update app-providers.tsx

* Moved ee to features

* ee to features/ee

* Removes @calcom/ee

* Adds possible feature locations

* Build fixes

* Migrates stripe to app-store lib

* Colocates stripe imports

* Update subscription.ts

* Submodule sync

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-28 13:58:26 -06:00
Bailey Pumfleet 84b4bb397f Improve help dialog strings (#3570) 2022-07-28 15:54:48 +00:00
Muhammad AliandBailey Pumfleet b90cf96ab9 Fix: Doc typo fix in coding styling (Developer docs) (#3533)
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-07-28 15:56:42 +01:00
github-actions[bot]andCrowdin Bot 5d61d2d79b New Crowdin translations by Github Action (#3539)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-28 11:56:43 +01:00
Hariom Balharaandkodiakhq[bot] 1be5510c5e Embedded Routing Forms - Part1 (#3530)
* Support dark theme in routing form

* Fix Embed detection

* Add routing form embed example

* Better rendering support in dark mode for react-select

* Fix more theme issues

* Added test for Routing Form Embed

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-28 10:50:25 +00:00
Hariom Balhara e871781079 Fix getSchedule tests (#3560) 2022-07-28 16:07:00 +05:30
bfa70dcc83 Feat/onboarding admin (#3486)
* WIP

* API and step done fallback

* Finishing up tweaks

* Inline comment

* Translations

* Update apps/web/pages/api/auth/setup.ts

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

* Update apps/web/pages/api/auth/setup.ts

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

* Update apps/web/pages/api/auth/setup.ts

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

* Update apps/web/pages/api/auth/setup.ts

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

* Linting fixes

* Update apps/web/pages/auth/setup.tsx

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

* Linting fix

* Moving to v2

* Translations

Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-27 17:28:21 -06:00
sean-brydonandkodiakhq[bot] b3bd8c1a58 Few updates to V2 components (#3532)
* Fix breadcrumb colors

* HorizontalTabs

* Team List Item WIP

* Horizontal Tabs

* Cards

* Remove team list item WIP

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-27 23:12:27 +01:00
Joe Au-Yeung 2c23949cea Add axiom to next config (#3556) 2022-07-27 16:19:59 -04:00
sean-brydon 6e3825d95a Fixes depth (#3555) 2022-07-27 13:50:10 -06:00
525ce0d075 Fix/office365 api call (#3534)
* WIP fixes for handling office365 api call

* First working version microsoft api call handling

* Remove logs

* Clean up and improve function names

* Clean up

* Remove log

* Fix function missing its correct name

* Fix direct return of statement

* Cleanup

* Simplify with private fetcher

* Update CalendarService.ts

* Update getOfficeAppKeys.ts

Co-authored-by: zomars <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-27 19:12:42 +00:00
zomars 7537ea5b91 Fixes button appearance on Safari 2022-07-27 12:58:24 -06:00
Hariom Balharaandkodiakhq[bot] 47468798fd Avoid global variables leak by making the entire code IIFE (#3543)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-27 15:58:51 +00:00
sean-brydon 587526186f Updates build DIR/command (#3550) 2022-07-27 12:02:20 +01:00
sean-brydon 7582d93d6a Fix chormatic Action (#3548) 2022-07-27 11:33:10 +01:00
Peer Richelsen b9645b4de5 feather icons and inter (#3366) 2022-07-26 20:24:00 -06:00
zomars a1fcdcae44 v1.8.3 2022-07-26 18:46:14 -06:00
zomars cf5912335e hotfix: bookings and availability pages
commit 36e1c9272fa9b86cbe9e53e03e83799820674f17
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 18:27:57 2022 -0600

    Update [schedule].tsx

commit f39535feea22a11a857d179f3506803602a95728
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 18:07:30 2022 -0600

    Update [status].tsx

commit c3ac1bcc8c7ff8dc7ebf178d23a6e5c7ea223749
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 17:53:43 2022 -0600

    Update [schedule].tsx

commit 39364d8476b3c582d4a7fe3f8d139c5d254e3ecf
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 17:43:13 2022 -0600

    Debugging prod

commit 4b428d22dec734dab19096b4fe07741f18e5bc5d
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 17:20:44 2022 -0600

    Update checkLicense.ts

commit 4212e6d61908face52d647bb661d71db959cf864
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 17:11:37 2022 -0600

    Booking fixes attempt

commit e3160027ea0ce40d129b445daeb3bb17711ac640
Author: zomars <zomars@me.com>
Date:   Tue Jul 26 13:52:35 2022 -0600

    migration conflict fixes
2022-07-26 18:45:10 -06:00
zomars 5ad25a692f v1.8.2 2022-07-26 13:03:19 -06:00
3bdf4a84f8 Prevent team members from creating events (#3498)
* Delete CreateEventTypeBtn.tsx

* Adding Permission to creation router

* Disable button if membership role is member

* Fix linting error

* Update explicit role selection

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

* Type fix

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
2022-07-26 18:25:42 +00:00
Joe Au-Yeung 7a306900ab Fix input on trial accounts (#3522)
* Fix input on trial accounts

* Refactor conditional
2022-07-26 12:04:20 -06:00
35259b75f6 Display correct time format on availability page (#3441)
* Pass time format

* Write 24hr time format

* Remove console.log

* Change regex

* Add console.log

* Remove console.log

* Remove console.log

* Update TimeOptions.tsx

* Write time format to localstorage on user create / edit

* Grab and set from local storage

* On user create grab timeformat from browser

* Update timeFormat in DB

* Fix typo

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2022-07-26 12:02:44 -06:00
zomars 538c5dff43 Playwright should not live on deps 2022-07-26 10:54:25 -06:00
zomars 590ec3bf23 Update check-if-ui-has-changed.yml 2022-07-26 10:19:53 -06:00
zomars 53befd7c5c Build fixes
handleErrorsJson signature changed which triggered errors here
2022-07-26 10:00:59 -06:00
Alex van Andelandsean-brydon 047d4a0773 Some optimisations to the storybook (#3526)
* Some optimisations to the storybook

* Fixed merge artifact

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-07-26 15:05:11 +01:00
ef6ed6050b Feat: Add next-themes for theme handling (#3263)
* chore: add next-themes

* feat: add next theme provider

* fix: modify useTheme hook to use next-themes

* fix: use the new hook for theming

* remove conditional rendering with isReady boolean flag

* remove Theme component returned from useTheme hook

* Update missing useTheme usage

* Fix theme switching as per system. Also, ensure that booking and non booking pages can maintain their own requirements of theme simulatenously

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2022-07-26 08:27:57 +00:00
Carina WollendorferandCarinaWolli 2b5759e8bb fix translation key (#3527)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-07-25 19:38:04 -06:00
33e79c8ad5 Improve phone number input (#3472)
* add country code once country selected

* add changes also for when editing workflows

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-25 19:37:32 -06:00
629131b7f9 Disallow search engine indexing of hidden event types (#3478)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-07-26 00:17:15 +00:00
Alex van Andel a156a784a1 Fixes Atlantic/Azores blank results (#3516) 2022-07-25 15:45:34 -06:00
Carina WollendorferandCarinaWolli d8b2fc94b6 fix cron job for reminders (#3488)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-07-25 15:04:08 -06:00
Leo Giovanettiandsean-brydon e21e622096 Storybook router issue fix (#3525)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-07-25 22:02:33 +01:00
Omar Lópezandkodiakhq[bot] 160ccc3502 Fixes/bookings 500 (#3523)
* Fixes an annoying 500 error in bookings

* Fixes back to bookings link

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-25 20:22:56 +00:00
Leo Giovanettiandkodiakhq[bot] 82d8db5f70 HubSpot app improvement: create contacts (#3502)
* WIP

* Last changes

* Create non-existing contacts from attendees

* Adding list of features

* Removing unneeded code

* Missing error when creating contacts

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-25 16:13:52 -03:00
sean-brydon 43a6e3f5ce Fix V1 Colors (#3520) 2022-07-25 12:57:16 +01:00
Omar Lópezandkodiakhq[bot] c03ca5549f Wipe my cal e2e fixes (#3509)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-24 20:18:36 +00:00
Leo GiovanettiandPeer Richelsen e395c9297d Fixing already installed apps & link to them (#3500)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-23 00:59:30 +00:00
277b0c4c92 Feat/design system (#3051)
* Storybook Boilerplate setup

* Inital Setup

* First story

* Color Design System

* Badge Story + Comp

* Checkbox UI + Stories

* Update Red colors + Button Group

* Switch+Stories / Default brand color

* Update Version + Button Group combined

* Compact Butotn Group

* Tidy up Selectors

* Adds Tooltip to Button

* TextInput

* Update SB

* Prefix Input

* Match text area styles

* Prefix Controls

* Update spacing on text area

* Text Input Suffix

* Color Picker

* Update storybook

* Icon Suffix/Prefix

* Datepicker + move components to monorepo

* Text color on labels

* Move Radio over to monorepo

* Move CustomBranding to calcom/ib

* Radio

* IconBadge Component

* Update radio indicator background

* Disabled radio state

* Delete yarn.lock

* Revert "Delete yarn.lock"

This reverts commit 9b99d244b70872153a16bec1f1f3bc651e94be7a.

* Fix webhook test

* Replace old toast location

* Update radio path

* Empty State

* Update Badge.tsx

* Update Badge.tsx

* Banner Component+story

* Creation Modal

* Creation Dialog updated

* Button hover dialog

* Confirmation Modal

* Datepicker (Booking)

* PageHeader

* Fix border width

* PageHeader update search bar

* Fix input height

* Fix button group size

* Add spacing between badges - font smoothing

* Update button position on banner

* Banner update

* Fixing focus state on suffix/prefix inputs

* Implement A11y addon

* Add aria label

* error && "text-red-800"

* Fix button hover

* Change colors

* Generate snapshot tests for on hover button

* Revert colors to demo

* Change colors

* Fix Linear Issues

* Form Stepper component

* Add padding back to input

* Move ui to UI_V2

* Use V2

* Update imports for v1

* Update imports for v1

* Upgrade to nextjs in storybook root

* Update website submodule

* Avatar Groups

* Fix webpack again

* Vertical Tab Item

[WIP] - active state on small item is not working currently

* Vertical Tab Group

* Add Github action

* Fix website submodule

* Fix GH action

* Rename Workflow

* Adds lint report for CI

* Lint report fixes

* NavigationItem comments

* VerticalTabItem type fixes

* Fix avatar blur

* Fix comments

* Adding isEmbed to window object

* Disable components that use router mock.

* Load inter via google fonts

* Started select

* Adding base Breadcrumb

* Update readme

* Formatting

* Fixes

* Dependencies matching

* Linting

* Update FormStep.stories.tsx

* Linting

* Update MultiSelectCheckboxes.tsx

Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-22 18:39:50 -06:00
zomars dd9adff32a Adds missing migration 2022-07-22 18:25:46 -06:00
github-actions[bot]andCrowdin Bot b8a807da59 New Crowdin translations by Github Action (#3501)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-22 23:05:42 +02:00
Pavel ShapelandPeer Richelsen 342efaed31 Refactor: use EditableHeading in event-types/[type].tsx (#3468)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-22 20:51:38 +00:00
Joe Au-Yeung 982ca3a783 Add verification json (#3505) 2022-07-22 21:25:55 +02:00
Omar Lópezandkodiakhq[bot] 9447f16b82 Migrates all tRPC code to a monorepo package (#3484)
* WIP

* WIP

* Type and migration fixes

* Adds missing default import

* Fixes import

* Fixes tRPC imports in App Store

* Migrate stripe helpers

* WIP

* Type fixes

* Type fix?

* Test fixes

* Adds missing stripe packages

* Moved queries to lib instead

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-22 11:27:06 -06:00
e82bcb6dba fixes #3336 - Avatar on New Event Type (#3465)
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-22 10:04:40 -06:00
Hariom Balhara c0c0bbccf7 Add session info to Beacon (#3497) 2022-07-22 14:38:02 +00:00
Hariom Balhara e04cb8fc61 Riverside,whereby doesnt have videoApi handle that (#3494) 2022-07-22 11:52:17 +00:00
Hariom Balhara 9b5d311136 Hotfix: Embed ModalBox - Hidden Content (#3491) 2022-07-22 12:59:13 +05:30
github-actions[bot]andCrowdin Bot c2d6c30221 New Crowdin translations by Github Action (#3483)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-21 18:00:21 -06:00
Carina WollendorferandCarinaWolli 9709ee4e71 fix reschedule translation (#3487)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-07-21 16:39:39 -06:00
zomars da29b8893a Adds missing migration 2022-07-21 16:12:10 -06:00
zomars b94f78f6e1 Update labeler.yml 2022-07-21 16:12:01 -06:00
Omar López 0a125b6900 Revert "Self-hosted onboarding first admin wizard (#3393)" (#3485)
This reverts commit ee14423f4c.
2022-07-21 15:05:52 -06:00
Leo Giovanetti ee14423f4c Self-hosted onboarding first admin wizard (#3393)
* WIP

* API and step done fallback

* Finishing up tweaks

* Inline comment

* Translations
2022-07-21 19:29:24 +00:00
c93e238e15 Fixes bug that new line is not working in workflow reminder emails (#3452)
* add html format to email template

* remove workflow reminder email

* fix that text was used instead of html

* fixes that email subject was used instead of body

* remove \n\n from text template

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-21 18:56:20 +00:00
d3fcb8bf7d Team Impersonation (#3450)
* Team Impersonation

* Refactor - Disable Imeprsonate button

* Change copy

* Add .env toggle

* Fix eslint

* Update role selection

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

* New 'admin' seed user, improve flow ImpersonationProvider

* Fix impersonation string

* Adds fullstop

Co-authored-by: Sean Brydon <seanbrydon.me@gmail.ocm>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-21 17:02:20 +00:00
Hariom Balharaandzomars 471420c1d4 Add getSchedule tests (#3233)
* Add getSchedule tests

* Add first integration test

* Update turbo.json

* Make sure unit tests run asap

* Worker threads

* Improve team event test

* Remove unrelated changes

* Improve tests readability

* Update CalendarManager.ts

* Add README

* Debug tests

* Temporarily disabled build errors

* Fix failing tests

* Remove unncessary logs

Co-authored-by: zomars <zomars@me.com>
2022-07-21 10:44:23 -06:00
2eedf74eb7 Apply suggestions from code review (#3480)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peer@cal.com>
2022-07-21 16:05:13 +00:00
github-actions[bot]andCrowdin Bot 5a9ca309d3 New Crowdin translations by Github Action (#3461)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-21 17:41:57 +02:00
sean-brydonandAlex van Andel 12c33ea88e Hide button if readonly (#3471)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-07-21 15:25:10 +02:00
Hariom Balhara faac59fb8e Fix TS errors and weird instances of undefined error due to prisma upgrade and turbo cache (#3475)
* Fix TS errors

* Always reexecute prisma postinstall as prisma version might change after yarn
2022-07-21 09:59:37 +00:00
Carina Wollendorfer c500952dca show error message when wrong workflow id (#3474) 2022-07-20 18:33:35 -06:00
Carina Wollendorfer 0a336df162 fix setting away status (#3473) 2022-07-20 18:32:46 -06:00
alannncandOmar López 5a0cef7fb9 fix/error-handling-with-proper-status (#3453)
* Add conditional on error handling

* Fix return type for handleError

Co-authored-by: Omar López <zomars@me.com>
2022-07-20 14:02:00 -06:00
13d13c71c6 Implementing additional feedback from Workflows PR (#3456)
* implement feedback

* put formSchema outside of react component

* don't show new workflow button for free Users

* code clean up

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
2022-07-20 19:48:40 +00:00
zomars 3ff43520eb Bump prisma to v4.1 2022-07-20 13:17:23 -06:00
Omar López 4874a6e34c Upgrades prisma to v4 (#3179)
* Upgrades prisma to v4

* Upgrades prisma to v4

* Upgrades prisma to v4

* Removes preview feature as it's on stable now

* Adds missing owner relationship

* Adds missing onCascades to prevent orphans

* TODO

* Adds default date to feedback

* Adds missing foreign keys

* Update schema.prisma

* Update console

* Fixes build error

* Update console

* Delete migration.sql
2022-07-20 12:49:53 -06:00
5b7cd476a7 Feature: Routing Forms Webhook for Form Responses (#3444)
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-20 18:30:57 +00:00
alannnc b47c75304e Fixed action button display while responsive and fix translations (#3469) 2022-07-20 18:12:18 +00:00
zomars be799e35d5 v1.8.1 2022-07-20 10:15:32 -06:00
zomars e35b9155d2 Submodule sync 2022-07-20 10:14:58 -06:00
Pavel ShapelandPeer Richelsen d1d467d28d Fix a typo in the server lib (#3439)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-20 13:34:17 +00:00
Ciarán Hanrahanandkodiakhq[bot] b2e5ac4cda subtle adjustment to the position of the search icon (#3440)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-20 14:47:33 +05:30
Hariom Balhara 4d2f134dc7 Hide useless windows scrollbar when they are not event required (#3458) 2022-07-20 09:57:24 +02:00
Leo Giovanettiandzomars aa166190e9 Hotfix: Cancelling recurring events follow-up (#3454)
* Other fixes and applying feedback

* Adds defaultResponder to handle zod errors

Co-authored-by: zomars <zomars@me.com>
2022-07-19 17:29:52 -06:00
0e9d754f64 Refactor cancel integrations logic (#3416)
* Refactor cancel integrations logic

* Add additional comments

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-19 16:00:01 -06:00
github-actions[bot]andCrowdin Bot 3b7f18cb5d New Crowdin translations by Github Action (#3398)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-19 21:54:38 +02:00
Leo Giovanettiandkodiakhq[bot] 63660a37f9 Cancelling recurring vs single event (#3448)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-19 19:06:23 +00:00
Hariom Balharaandkodiakhq[bot] 805419d58b Import ee from package instead of as @ee as that isnt available to api (#3446)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-19 18:48:28 +00:00
Hariom Balharaandkodiakhq[bot] 0923b83eaf Fix routing (#3445)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-19 12:36:39 -06:00
f40b035ff8 Hotfix: Unrelated bookings marked as cancelled due to recurring event (#3427)
* Fix updatedAt not updating

* Fix accidental cacnellation of all bookings if recurringEventId is null

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

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

* Fix linting

* Linting warnings

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2022-07-19 11:06:43 -06:00
Hariom Balharaandkodiakhq[bot] 2b7f7a06ac Use nodeJS 14+ compatible method for replacing (#3429)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-19 18:24:19 +05:30
sean-brydon baab6ecb2e Remove Add Another for global - fix spacing (#3432) 2022-07-19 11:33:29 +02:00
alannnc 9861156edb Added optional to password to zoom response (#3422) 2022-07-18 13:02:40 -06:00
Omar López df3e3e8237 Disables batching for i18n query (#3181)
* Disables batching for i18n query

* Makes i18n batch skip explicit
2022-07-18 12:59:51 -06:00
Peer Richelsen ef36a93a55 moved impersonating alert to the top (#3420) 2022-07-18 12:08:48 -06:00
6fc3e17b47 When rescheduling update specific calendar (#3375)
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-18 11:37:47 -04:00
Hariom Balharaandkodiakhq[bot] a9ad9a6a39 Ensure / is used for paths in import (#3411)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-18 13:30:05 +00:00
Peer Richelsen 1b09b7efd4 added title to forms, removed shadow-sm (#3412) 2022-07-18 12:52:50 +00:00
sean-brydon f43bfd308b Remove shallow routing (#3401) 2022-07-17 15:42:13 +05:30
Taha 4dec3fb97c Fix the path for "schema.prisma" file in README.md (#3386) 2022-07-16 13:30:58 -06:00
Syed Ali Shahbaz ec07f84d1e HOTFIX: fixes soft failing on settings/profile routes (#3394)
* fixes soft failing on settings/profile routes

* Fixed auto changed code

* Fixed auto changed code

Probably down to some random windows incompatibility with the parser/beautifier at commit time
2022-07-16 09:45:44 +02:00
martincollignonandkodiakhq[bot] 07965b2a32 Edited typo (#3391)
"Workflows enable you to automate sending reminders and notifcations." --> "Workflows enable you to automate sending reminders and notifications."

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-15 23:00:59 +00:00
alannnc 4c2f6df94d Fix possible 404 when obtaining calendar busy lists (#3345) 2022-07-15 20:28:24 +00:00
Carina WollendorferandCarinaWolli 48916cf85e fix bug with disabled save button (#3390)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-07-15 13:54:20 -06:00
github-actions[bot]andCrowdin Bot 5e81d9fb56 New Crowdin translations by Github Action (#3387)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-15 21:15:08 +02:00
zomars 77bf376c94 v1.8.0 2022-07-15 11:20:40 -06:00
zomars 200a704756 Submodule sync 2022-07-15 11:20:12 -06:00
6dd63ee3da Bug(header): profile dropdown flickering on mobile (#3071)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2022-07-15 11:17:54 -06:00
sean-brydonandOmar López e99b018d3b Handle fake video apps (#3232)
Co-authored-by: Omar López <zomars@me.com>
2022-07-15 19:13:31 +02:00
github-actions[bot]andCrowdin Bot 8f020bf1d0 New Crowdin translations by Github Action (#3385)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-15 11:00:37 -06:00
Peer Richelsenandkodiakhq[bot] 78e2db6d70 extracted upgrade to pro strings (#3383)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-15 10:55:59 -06:00
ff6a7bf2a6 Fixes broken avatar slug link in availability page : OG image (#3255)
* fixed broken avatar slug link

* alternate fix

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-15 10:54:14 -06:00
0433b0a69a New Crowdin translations by Github Action (#3382)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-15 18:40:39 +02:00
Peer RichelsenandOmar López ee552d8d64 purged shadow-sm (#3381)
Co-authored-by: Omar López <zomars@me.com>
2022-07-15 18:39:11 +02:00
Carina Wollendorfer dda5750bbc Add workflow setup information to README.md (#3379) 2022-07-15 09:38:09 -06:00
18389f459d minor improvements to routing forms (#3380)
* minor improvements to routing forms

* UX improvements and text changes

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-15 11:46:05 +00:00
github-actions[bot]andCrowdin Bot fd16d734fb New Crowdin translations by Github Action (#3364)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-15 12:42:58 +02:00
Hariom BalharaandOmar López 55152141ee Routing Forms - Avoid Flicker, Fix preload of Cal Font (#3368)
* Fix flicker due to Shell on Routing Form pages. Fix font preload(unrealted to routing forms)

* Update _app.tsx

Co-authored-by: Omar López <zomars@me.com>
2022-07-15 05:47:37 +00:00
75b817531e Fixes timezone in reminder templates (workflows) (#3376)
* fix time zone in reminder template

* fix timezone in email subject

* add missing timezone

* fix date in sms reminder template

* use organizer time zone if message send to host

* fix build error

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2022-07-14 19:05:01 -04:00
zomars 9162d90504 Restores old CI config 2022-07-14 15:47:09 -06:00
Carina WollendorferandOmar López bf2843c17e Create cron-scheduleSMSReminders.yml (#3373)
Co-authored-by: Omar López <zomars@me.com>
2022-07-14 15:22:40 -06:00
Carina Wollendorfer 6a28396499 create cron-scheduleEmailReminders (#3374) 2022-07-14 15:12:18 -06:00
zomars 1a0a318a55 Revert "Adds custom rewrite logic for hosted cal.com"
This reverts commit 9e5b1f121f.

# Conflicts:
#	apps/web/next.config.js
2022-07-14 14:56:00 -06:00
zomars 99b821edcd Update LicenseRequired.tsx 2022-07-14 14:48:50 -06:00
Peer Richelsen ded0b1410a Update LicenseRequired.tsx 2022-07-14 14:49:52 +02:00
58d1c28e9d Routing Forms (#2785)
* Add Routing logic to Query builder

* Make a working redirect

* Make it an app

* Move pages and components to App

* Integrate all pages in the app

* Integrate prisma everywhere

* Fix Routing Link

* Add routing preview

* Fixes

* Get deplouyed on preview with ts disabled

* Fix case

* add reordering for routes

* Move away from react DnD

* Add sidebar

* Add sidebar support and select support

* Various fixes and improvements

* Ignore eslint temporarly

* Route might be falsy

* Make CalNumber support required validation

* Loader improvements

* Add SSR support

* Fix few typescript issues

* More typesafety, download csv, bug fiees

* Add seo friendly link

* Avoid seding credebtials to frontend

* Self review fixes

* Improvements in app-store

* Cahnge Form layout

* Add scaffolding for app tests

* Add playwright tests and add user check in serving data

* Add CI tests

* Add route builder test

* Styling

* Apply suggestions from code review

Co-authored-by: Agusti Fernandez Pardo <6601142+agustif@users.noreply.github.com>

* Changes as per loom feedback

* Increase time for tests

* Fix PR suggestions

* Import CSS only in the module

* Fix codacy issues

* Move the codebbase to ee and add PRO and license check

* Add Badge

* Avoid lodash import

* Fix TS error

* Fix lint errors

* Fix bug to merge conflicts resolution - me query shouldnt cause the Shell to go in loading state

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Agusti Fernandez Pardo <6601142+agustif@users.noreply.github.com>
2022-07-14 12:40:53 +00:00
Peer Richelsen 7ec5f01647 kbar follow up (#3361)
* added more routes to kbar

* added right direction for tooltip, moved search icon next to logo, added keyboard shortcuts to command bar

* added right direction for tooltip, moved search icon next to logo, added keyboard shortcuts to command bar

* fixed search icon for tablet

* fixed search icon for mobile

* hide keyboard shortcut legend on mobile

* extracted strings
2022-07-14 11:32:28 +00:00
github-actions[bot]andCrowdin Bot 6f073762fa New Crowdin translations by Github Action (#3362)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-14 13:12:56 +02:00
Hariom Balhara ebd4750f2d Improves UI stability of availability page. No CLS now. (#3347) 2022-07-14 10:28:46 +00:00
b1e4c23adc Fixes label to set Riverside link (#3358)
* fix label for riverside link

* add missing space

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-14 07:14:55 +00:00
github-actions[bot]andCrowdin Bot 03b8643d1e New Crowdin translations by Github Action (#3360)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-14 09:03:59 +02:00
318ec316d5 Adds a fully extensible command(ctrl)+k interface for Cal (#3346)
* init

* action function added

* kbar trigger placed

* UI improvements to KBar, added command + K tooltip

* renamed quick find to commandbar

* replaced window router with nextjs router

* keyboard up n down nav shows up

Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-14 06:45:07 +00:00
bf7c86ffe5 Fix Caldav not valid destination calendar and only display calendars that user can edit (#3326)
* Default readOnly to false

* Display only calendars users can edit

* Remove console.log

* Remove console.log

Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-13 18:16:24 -06:00
33e36e5320 Only make web3 call if credential exists (#3323)
* Add has web3 credential

* Address feedback

* Address feedback

* Fix e2e test

* Remove unused code

* Remove unused import

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-13 18:14:39 -06:00
39199e515e Workflows (#3236)
* build basic database structure and basic design

* create simple workflow list

* add editing dots to list

* add mutation to create workflows

* add createMutation on submit + redirect to editing page

* redirect to edit page when clicking on row

* add functionality to delete workflow

* add timeUnit + input validation

* add empty screen view

* add time before it triggers to description

* add multi select with checkboxes

* remove getServerSideProps

* set default time period to 24

* fetch eventypes and display in dropdown

* add functionality to update workflows + many-to-many relationship

* fix all checked event types

* add SMS reminders

* fix bug with trigger + relocate sms template

* clean code

* add model for unscheduled reminders

* fix selected eventTypes

* fixing value to show how many event types selected

* fix plural of event types in select

* add onDelete cascade for all relations

* fix errors

* add functionality to send SMS to specific number

* fix type error for timeUnit

* set default value for time unit + fix type issues

* remove console.logs

* fix error in checking if scheduled date is more than 1h in advance

* fix build errors

* add migration for workflows

* add basic UI for editing workflow steps

* add formSchema

* improve functionality to update a step

* remove console logs

* fix issue with active event types

* allow null value for time and timeUnit

* sort steps asc step number

* add action to workflow (frontend)

* add  phone number input for SMS to specific number

* use PhoneInput for number input + input validation

* improve invalid input for phone number

* improve UI of phoneInput

* Improve design and validation

* fix undefined error

* set default action when adding action

* include all team event types

* fix phone number input for editing steps

* fix update muation to add steps

* remove console logs

* fix order of steps

* functionality to delete steps

* add trigger when event is cancelled

* add custom email body

* sms and email reminder updates

* add custom emails

* add custom email subject

* send reminder email to all attendees

* update migration

* fix default value for time and timeUnit

* save email reminders to database

* clean code

* add custom template to SMS actions

* schedule emails with sendgrid

* clean code

* add workflow templates

* keep custom template saved when changing templates

* create reminder template for email

* add dot at the end of sentace for email template

* fix merge error

* fix issue that template was not saved

* include sending emails for when event is cancelled

* fix bug that email was always sent

* add templates to sms reminders

* add info that sending sms to attendees won't trigger for already exisitng bookings

* only schedule sms for attendees when smsReminderNumber exists

* only schedule sms for attendees when smsReminderNumber exists

* set scheduled of workflow reminder to false when longer than 72 hours

* add cron for email scheduling + fixes for for sms an email scheduling

* adjust step number when deleting a step

* cast to boolean with !!

* update cron job for email reminders

* update sms template

* send reminder email not to guests

* remove sendTo from workflow reminder

* fixes sending sms without name + removing sendTo everywhere

* fix undefined name in sms template

* set user name to undefined for sending sms to a specific number

* fix singular and plural for time unit

* set to edit mode when changing action and custom template is selected

* delete reminders when booking cancelled or not active anymore

* fix type errors

* fix error that deleted reminders twice

* create booking reminders for existing bookings when eventType is set active

* improve email and sms templates

* use BookingInfo type instead of calendarEvent for reminder emails

* schedule emails for already existing bookings

* add and remove reminders for new active event types and cancelled events

* connect add action button with last step

* fix step container width for mobile view

* helper functions that return options for select

* fix typo and remove comment

* clean code

* add/improve error messages for forms

* fix typo

* clean code

* improve email template

* clean code

* fix missing prop

* save reference id when scheduling reminder

* fix step not added because of changed id for new steps

* small fixes + code cleanup

* code cleanup

* show error message when number is invalid

* fix typo

* fix phone number input when location is already phone

* set multi select checkbox to read only

* change email scheduling in cron job from 7 days to 72 hours

* show active event types in workflow list

* fix trigger information for workflow list

* improve layout for small screens in workflow list

* remove optional from zod type for workflow name

* order workflows by id

* use link icon to show active event types

* fix plural and add translation for showing nr of active eventtypes

* fix text for sms reminder template

* add reminders for added steps

* remove optional for activeOn

* improve reminder templates

* improve design of custom input fields

* set edit mode to false when phone number isn't needed anymore

* set sendTo in workflow step only for SMS_NUMBER action

* set email body and subject only when custom template

* only delete reminders that belong to workflow steps

* improve text for new event book trigger

* move reminders folder to workflows

* fix issue that save button was sometimes enabled in edit mode

* fix form issues for send to

* delete all scheduled reminders when workflow is deleted

* use enum for method

* fix imports for workflow methods

* add missing import

* fix edit mode

* create reminders when event is confirmed

* add reminderScheduler to reduce duplicate code

* make workflow enterprise and pro only feature

* move all files to /ee/ folder

* move package.json change to /ee/ folder

* add pro badge to shell

* set to edit mode to true if email subject is missing when action changes

* fix loading bug

* add migration

* fix old imports

* don't schedule reminders for opt-ins

* fix style of email body

* code clean up

* Update yarn.lock

* fix isLoading for active on dropdown

* update import for prisma

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

* update imports

* remove console

* use session to check if user has valid license

* use defaultHandler

* clean up code

* Create db-staging-snapshot.yml

* move LisenceRequired inside shell

* update import for FormValues

* fix phone input design

* fix disabled save button for edit mode

* squah all migration into a single one

* use isAfter and isBefore instead of isBetween

* import dayjs from @calcom

* validate phone number for sms reminders when booking event

* Allows auto approvals for crowdin

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2022-07-14 00:10:45 +00:00
alannnc 44b8cedef0 Fix/main zoom schema parsing (#3275)
* zod zoom parsing only required fields

* Change logger for console.error
2022-07-13 18:01:00 -06:00
zomars c7d220e998 Preventing technical debt to break linting 2022-07-13 17:57:54 -06:00
zomars 78f0b095ae Update submodule-sync.yml 2022-07-13 16:17:28 -06:00
zomars 9a0b1832c3 Update submodule-sync.yml 2022-07-13 16:13:29 -06:00
github-actions[bot]andCrowdin Bot c8ef235916 New Crowdin translations by Github Action (#3356)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-13 23:57:20 +02:00
zomars 9760a2606e Submodule sync 2022-07-13 15:15:40 -06:00
zomars e916c67415 Linting fixes 2022-07-13 15:14:16 -06:00
zomars f6684c001c Fixes lint:fix script 2022-07-13 15:11:40 -06:00
zomars 9e5b1f121f Adds custom rewrite logic for hosted cal.com 2022-07-13 15:03:09 -06:00
zomars 179da11f4b Submodule sync 2022-07-12 16:32:31 -06:00
zomars 4bbca06bea Linting 2022-07-12 16:31:09 -06:00
zomars c5263efce2 Adds missing linting scripts 2022-07-12 16:31:09 -06:00
zomars 578c3d64ef Enables react/self-closing-comp lint rule 2022-07-12 16:31:09 -06:00
sean-brydon dbca46d677 Fixing 500 on slack OAuth (#3342) 2022-07-12 20:33:46 +00:00
zomars 1f953c5ca0 Update lint.yml 2022-07-12 14:15:06 -06:00
zomars a677ffb2a5 Update lint.yml 2022-07-12 14:11:29 -06:00
zomars 0ff6d4557a Fixing lint annotations 2022-07-12 14:05:15 -06:00
zomars 7bcc5c4548 eslint react/jsx-curly-brace-presence 2022-07-12 11:52:13 -06:00
zomars dbfc1477aa Formatting 2022-07-12 11:52:13 -06:00
Leo Giovanettiandkodiakhq[bot] b1d637d1a6 Upgrading nextjs and collect (#3252)
* Upgrading nextjs and collect

* Fixing suggestions + yarn.lock

* Updating submodules

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-12 11:43:53 -06:00
Peer Richelsen c3c7f82841 hide back button for embeds (#3339) 2022-07-12 14:33:12 +00:00
8d40e7cfd2 HotFix - Embed Flicker for newly converted static availability page (#3277)
* Add missing embed fingerprint

* Fix embed flicker for Availability page

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-12 14:18:53 +00:00
46266781b5 Update calendar when new attendees are added to bookings w/ seats (#3257)
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-07-12 09:16:34 -04:00
zomars 5fa336c195 v1.7.7 2022-07-11 19:22:45 -06:00
alannnc 0b53cd6ffc fix/zoom schema parsing (#3274) 2022-07-11 19:22:31 -06:00
github-actions[bot]andCrowdin Bot 9ad4cba8cf New Crowdin translations by Github Action (#3324)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-11 19:15:11 -06:00
zomars 9ff4d7e024 Allows auto approvals for crowdin 2022-07-11 18:49:40 -06:00
zomars 012af52f1b Removes names for required-checks 2022-07-11 18:34:38 -06:00
zomars 6e1c165b31 Removes redundant matrix for now 2022-07-11 18:27:24 -06:00
zomars 83d2c924ca Removes matrix from required checks 2022-07-11 18:24:22 -06:00
alannnc ebc098186d Fix conditional for constant IS_SELF_HOSTED (#3329) 2022-07-11 17:46:47 -06:00
Omar López c7288bb822 Update and rename bug-hunting.yml to add-to-project.yml 2022-07-11 17:41:55 -06:00
5587867598 RFC: Custom eslint plugin (#3272)
* WIP

* Renamed pacakge

* Update yarn.lock

* Lint fixes

* Moved afterBufferTime to busyTimes & heavily simplify busy check (#3258)

* New Crowdin translations by Github Action (#3312)

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

* Fix shell flicker (#3314)

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

* Add recommended config to plugin

* Linting

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2022-07-11 17:35:50 -06:00
github-actions[bot]andCrowdin Bot 62f6373102 New Crowdin translations by Github Action (#3320)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-11 21:23:02 +02:00
github-actions[bot]andCrowdin Bot 61f29d4a86 New Crowdin translations by Github Action (#3317)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-11 17:53:01 +02:00
Hariom BalharaandPeer Richelsen 93ab0cfd09 Fix shell flicker (#3314)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-11 16:37:20 +02:00
github-actions[bot]andCrowdin Bot 8f495f7b67 New Crowdin translations by Github Action (#3312)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-11 16:35:34 +02:00
Alex van Andel c9d5af6591 Moved afterBufferTime to busyTimes & heavily simplify busy check (#3258) 2022-07-11 13:00:08 +02:00
github-actions[bot]andCrowdin Bot adffb00817 New Crowdin translations by Github Action (#3269)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-09 09:17:05 +02:00
Omar López afe730e502 Update README.md 2022-07-08 13:04:34 -06:00
Matt 4cd43087e9 Update google.mdx (#3268)
https://calendso.slack.com/archives/C01V37YT188/p1657289355306489
2022-07-08 08:47:15 -06:00
github-actions[bot]andCrowdin Bot 371fd48c15 New Crowdin translations by Github Action (#3265)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-08 15:25:52 +02:00
github-actions[bot]andCrowdin Bot 657cf7e180 New Crowdin translations by Github Action (#3253)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-08 15:21:50 +02:00
6b0ac96b38 Fix request reschedule organiser email (#3227)
* Fix request reschedule organizer email

* Updated book a new time link and removed manage or cancel action on user owner email request reschedule

* Fix let with const for a variable and change env var to a constant from lib

Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-07 16:25:05 -06:00
alannnc 2c30976e99 WIP stripe customer metedada sync (#3153) 2022-07-07 15:24:42 -06:00
zomars a7212660a4 Update db-staging-snapshot.yml 2022-07-07 12:05:31 -06:00
zomars 5e4e98e002 Update db-staging-snapshot.yml 2022-07-07 11:30:48 -06:00
zomars 7bbfde607a Update db-staging-snapshot.yml 2022-07-07 11:25:47 -06:00
zomars f179c5b818 Update db-staging-snapshot.yml 2022-07-07 11:10:45 -06:00
zomars 03c27ebd62 Create db-staging-snapshot.yml 2022-07-07 11:06:35 -06:00
Carina Wollendorfer db2886d0d4 left align cancel for recurring events (#3245) 2022-07-07 17:12:13 +01:00
Hariom Balhara 5db762e987 Add logs (#3256) 2022-07-07 09:26:22 -06:00
Hariom Balhara f9cc71d120 ... doesnt seem to run dependencies first. So, ensure that using pipeline (#3254) 2022-07-07 00:00:48 -06:00
Hariom Balhara 6db129e3bf Fix Around location update (#3229) 2022-07-06 19:22:56 -06:00
sean-brydon c6b1d99270 Edit around placeholder (#3230) 2022-07-06 19:22:11 -06:00
noobyogi0010 34cd41be67 fixes #3142 - Huddle01 installation (no success message and no redirect) issue (#3231) 2022-07-06 19:21:16 -06:00
Agusti Fernandez Pardo 8ca627040b fix: adds new constants fallbacks for railway and heroku apps (#3217) 2022-07-06 19:13:02 -06:00
Bailey Pumfleet 13528ada2e Fix awaiting approval translation (#3234) 2022-07-06 19:09:28 -06:00
Alex van Andel 3c7380c3a9 Contains multiple fixes centered around Zoom (#3238) 2022-07-06 19:08:38 -06:00
zomars 9d951a5802 Formats schema 2022-07-06 16:55:29 -06:00
zomars b70550fc83 Update yarn.lock 2022-07-06 16:25:29 -06:00
zomars 6a12ff7984 Update yarn.lock 2022-07-06 16:19:54 -06:00
zomars 279d9766d8 Update website 2022-07-06 15:28:11 -06:00
85b61bed01 New Crowdin translations by Github Action (#3244)
* New Crowdin translations by Github Action

* Delete common.json

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-06 22:39:00 +02:00
zomars c2ca848f0a Transpiling fixes 2022-07-06 14:34:49 -06:00
c890e8d06d feature/settings-username-update (#2306)
* WIP feature/settings-username-update

* WIP username change

* WIP downgrade stripe

* stripe downgrade and prorate preview

* new UI for username premium component

* Fix server side props

* Remove migration, changed field to metadata user

* WIP for update subscriptions

* WIP intent username table

* WIP saving and updating username via hooks

* WIP saving working username sub update

* WIP, update html to work with tests

* Added stripe test for username update go to stripe

* WIP username change test

* Working test for username change

* Fix timeout for flaky test

* Review changes, remove logs

* Move input username as a self contained component

* Self review changes

* Removing unnecesary arrow function

* Removed intentUsername table and now using user metadata

* Update website

* Update turbo.json

* Update e2e.yml

* Update yarn.lock

* Fixes for self host username update

* Revert yarn lock from main branch

* E2E fixes

* Centralizes username check

* Improvements

* WIP separate logic between premium and save username button

* WIP refactor username premium update

* Saving WIP

* WIP redo of username check

* WIP obtain action normal, update or downgrade

* Update username change components

* Fix test for change-username self host or cal server

* Fix user type for premiumTextfield

* Using now a global unique const to know if is selfhosted, css fixes

* Remove unused import

* Using dynamic import for username textfield, prevent submit on enter

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-06 13:31:07 -06:00
Carina WollendorferandCarinaWolli 7d6a6bf812 add error message for invalid phone number (#3249)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-07-06 13:01:16 -06:00
Omar López a4684042a6 Matches phone input darkmode to the rest of inputs (#3247) 2022-07-06 12:19:48 -06:00
zomars 56da9c7c5f Removed forward slashes 2022-07-06 11:33:36 -06:00
zomars 7967c6e46b updates GH workflow 2022-07-06 11:31:34 -06:00
zomars 8d96b79a5b Update GH workflows 2022-07-06 09:28:11 -06:00
sean-brydon d0325c13d1 Split on slug to get profile page (#3228)
* Split on slug to get profile page

* Fix teams slug

* Remove console.log

* Remove unneeded diff
2022-07-06 08:47:22 -06:00
Hariom Balhara 5ea08c237a Fix ordering of credentials (#3243) 2022-07-06 08:43:57 -06:00
Hariom Balhara b3f7e8e322 Booking Module Improvements (#3221)
* Fix momentary render of dates before skeleton

* Fix Hydration issue - We shouldnt guess timezone on server as it would be different from client causing hydration error

* Fix wrong trpcQuery result when clicking on a particular date. Also, fix last day of the month being never available

* Fix as per PR Feedback
2022-07-06 08:25:41 -06:00
zomars b7d4e3885e Update bug-hunting.yml 2022-07-05 10:06:41 -06:00
zomars 3c324eb776 Update labeler.yml 2022-07-04 20:10:56 -06:00
zomars c16e71aa01 Update required-checks.yml 2022-07-04 19:59:38 -06:00
zomars 5fa3f45fed Update required-checks.yml 2022-07-04 19:49:51 -06:00
zomars 450d0e7f05 Adds timeout to checks 2022-07-04 19:48:15 -06:00
zomars b5745fd0e5 Update required-checks.yml 2022-07-04 19:44:23 -06:00
Omar López bac12d0cbd Update CONTRIBUTING.md (#3226)
* Update CONTRIBUTING.md

* Don't run e2e for md files

* Debugging why is not ignoring e2e test
2022-07-04 19:41:22 -06:00
zomars f17833552e Test 2022-07-04 19:29:07 -06:00
zomars 15bdcaff88 Debugging why is not ignoring e2e test 2022-07-04 19:22:38 -06:00
zomars 29507557d6 Update required-checks.yml 2022-07-04 19:19:01 -06:00
zomars 30c1dd036d Update lint.yml 2022-07-04 19:13:51 -06:00
zomars 8f24b92666 Update required-checks.yml 2022-07-04 18:57:06 -06:00
zomars 7c99004225 Update required-checks.yml 2022-07-04 18:50:49 -06:00
zomars 8bce6055c7 Update required-checks.yml 2022-07-04 18:48:08 -06:00
zomars 049e433781 Path fixes 2022-07-04 18:34:39 -06:00
zomars 3ac8caecbf Don't run e2e for md files 2022-07-04 18:30:44 -06:00
zomars e394cfdcd8 Update checks 2022-07-04 18:26:27 -06:00
zomars be99035f49 GH workflow name collision fixes 2022-07-04 17:36:38 -06:00
zomars 6c8e624496 Update required-checks.yml 2022-07-04 15:31:28 -06:00
zomars 5369d4123e Prevent name conflicts for e2e 2022-07-04 15:27:22 -06:00
zomars 3a055018f6 Testing required checks 2022-07-04 14:29:20 -06:00
zomars 6ac670b366 Fixes bug hunting file location 2022-07-04 14:19:05 -06:00
zomars db35a7c68f Update bug-hunting.yml 2022-07-04 13:58:32 -06:00
zomars a1d8fb0b46 Auto adds bugs to bug hunting view 2022-07-04 13:57:05 -06:00
zomars 7170fadff8 Adds pull request labeler 2022-07-04 13:16:01 -06:00
zomars fa5e6b42de v1.7.6 2022-07-04 10:51:26 -06:00
zomars 1f02a5c718 Update yarn.lock 2022-07-04 10:48:21 -06:00
zomars c55b17bf58 Update console 2022-07-04 10:48:16 -06:00
zomars 4975493090 Matches zod version to the rest 2022-07-04 10:47:12 -06:00
zomars 785a014f3c Submodule sync 2022-07-04 10:29:33 -06:00
depfu[bot] dd941e62ef Update next-auth to version 4.5.0 (#3138) 2022-07-04 14:36:20 +00:00
sean-brydonandPeer Richelsen eec2d8ceed Implement profile back button (#3165)
* Implement profile backbutton

* update slug to use username

* Back Button Improvments

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-04 08:26:22 -06:00
Alex van Andel b546d9e33e Some tweaks to slots to fix server time effecting logic (#3218) 2022-07-02 10:13:39 -06:00
Joe Au-Yeung 201d17264c Attach credential id to destinationCalendar records (#3197) 2022-07-01 21:55:27 +01:00
2cbf46a323 Seats events send email to attendees and organizer (#3206)
* Add attendees to email notfications

* New seat title

* Change organizer email

* Clean console.log

* Fix type errors

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

Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-07-01 19:19:23 +00:00
Leo Giovanetti 833791bc9a Team event type scroll, manage team invite from mobile (#3171) 2022-07-01 12:29:59 -06:00
00c9df7602 There is lot of hardcoding links in application (#3130)
* There is lot of hardcoding links in application

* Apply suggestions from code review

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

Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2022-07-01 11:19:52 -06:00
Omar LópezandPeer Richelsen 669199e273 Removes unused files (#3208)
* Adds next-unused

* Cleanup

* Update next-unused

* Removes next-unused

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-07-01 10:02:44 -06:00
93e2246b18 Wrong color for Timezone selector on booking page (#3187)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-07-01 12:28:17 +01:00
James VansteenkisteandBailey Pumfleet f639405c20 fix: event creation on external office calendar (#3152)
* fix: event creation on external office calendar

* chore: simplify fix implementation

Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-07-01 12:26:20 +01:00
zomars 240fc67d72 v1.7.5 2022-06-30 10:32:55 -06:00
Hariom Balhara eef405ef41 If booking doesnt exist its 404 page but if it exists and eventType doesnt exist, log it for now and continue with the flow (#3202) 2022-06-30 09:09:15 -06:00
Hariom BalharaandLeo Giovanetti 1469b856a6 Fix hubspot callback (#3195)
Co-authored-by: Leo Giovanetti <hello@leog.me>
2022-06-30 07:01:07 +00:00
Alex van Andel 53bbbf793d Fixes the slow performance due to the Dayjs timezone plugin (#3201)
* Fixes the slow performance due to the Dayjs timezone plugin

* Bye.

* Parses busyTime in UTC
2022-06-29 18:33:19 -06:00
Alex van Andel a91f251b98 Remove '{}' to prevent overwriting .env.appStore value (#3200) 2022-06-29 16:56:31 -06:00
github-actions[bot]andCrowdin Bot d95c3a1459 New Crowdin translations by Github Action (#3196)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-06-29 22:57:58 +02:00
Omar López 92bfb3e5b9 Updates snaplet transform script (#3199)
* Update .gitignore

* Fixes local dev env checks

* Update transform.ts
2022-06-29 13:03:13 -06:00
Hariom Balharaandkodiakhq[bot] fbc4eb5457 Set theme initially so that the gneerated JS code knows the correct theme already (#3193)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-06-29 12:50:46 -06:00
Carina WollendorferandCarinaWolli 4a74abcb34 change meeting url to a link (#3198)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-06-29 12:22:07 -06:00
Hariom Balhara 09c8f55682 Make i18n a non-batch request and non-cacheable (#3188) 2022-06-29 10:50:32 -06:00
alannnc 3f3ffa2ba8 fix/availability-tz-offset (#3185) 2022-06-29 09:01:30 +00:00
Peer Richelsen 654cfbb653 updated readme, new gif, added roadmap banner 2022-06-29 10:31:08 +02:00
Hariom Balhara ad35d3d567 Make the height of textarea as per container height but consider the text above it (#3189) 2022-06-29 10:06:36 +02:00
zomars 70a31874f0 v1.7.4 2022-06-28 17:12:56 -06:00
zomars fe8b0fc5f8 Possible timeout fix take 3 2022-06-28 16:53:14 -06:00
zomars 2983fb4c12 Fixes month name 2022-06-28 16:24:53 -06:00
zomars 03cc4855a8 Update package.json 2022-06-28 16:01:50 -06:00
zomars c82a9853d2 Submodule sync 2022-06-28 15:58:14 -06:00
zomars d732a984c2 Merge branch 'main' of https://github.com/calcom/cal.com 2022-06-28 15:46:17 -06:00
zomars 826583f404 Second potential fix for timeout 2022-06-28 15:46:07 -06:00
zomars b6dd0d5804 Remove SSG prefetch 2022-06-28 15:32:40 -06:00
Leo Giovanetti df2c3c42bf Recurring config missing from event (#3182) 2022-06-28 18:04:44 -03:00
zomars 7122a4a91d Consolidates dayjs in a single package 2022-06-28 14:48:24 -06:00
zomars 5be669a479 Update zod-utils.ts 2022-06-28 13:33:55 -06:00
zomars 8629d92a64 Merge branch 'main' of https://github.com/calcom/cal.com 2022-06-28 13:18:53 -06:00
zomars 840b6c498a Potential fix for availability timeout 2022-06-28 13:18:47 -06:00
zomars 7a8b9e5f32 Potential fix for availability timeout 2022-06-28 13:17:43 -06:00
zomars fe50f04075 Only stale SSR requests 2022-06-28 11:37:01 -06:00
Leo Giovanetti e1e369de9e Hotfix: Fixing app types variants (fixes Hubspot) (#3176)
* Fixing Hubspot

* Comment
2022-06-28 16:36:19 +00:00
9b2ad3f350 Negative days in trial license period (#3133)
* Negative days in trial license period

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

Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2022-06-28 10:03:31 -06:00
GlitchWitch 83dfafb8a1 Create SECURITY.md (#3174)
* Create SECURITY.md

Creates a SECURITY.md file based on https://cal.com/security

* Update markdown formatting
2022-06-28 17:24:11 +02:00
2cd7ddcdbe New Crowdin translations by Github Action (#3173)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-06-28 15:27:29 +02:00
Esteban Dalel R 2fa9ea4501 Fix broken link (#3172) 2022-06-28 11:48:16 +01:00
Peer Richelsen 6fff27b5ad Update README.md 2022-06-28 09:54:37 +02:00
zomars 0794224288 Prevents undefined classNames in Skeletons 2022-06-27 21:04:10 -06:00
alannnc e3e1de3c11 Fix saving price and currency (#3168) 2022-06-27 20:31:25 -06:00
zomars 52759333fd Skeleton fixes 2022-06-27 20:25:06 -06:00
zomars e27a2bafcd AvailabilityPage cleanup 2022-06-27 20:17:23 -06:00
zomars 87befdbdf4 Adds perf metrics for each tRPC endpoint 2022-06-27 19:19:33 -06:00
787 changed files with 29796 additions and 14510 deletions
+2 -1
View File
@@ -21,7 +21,8 @@ DAILY_SCALE_PLAN=''
# - GOOGLE CALENDAR/MEET/LOGIN
# Needed to enable Google Calendar integration and Login with Google
# @see https://github.com/calendso/calendso#obtaining-the-google-api-credentials
GOOGLE_API_CREDENTIALS='{}'
GOOGLE_API_CREDENTIALS=
# To enable Login with Google you need to:
# 1. Set `GOOGLE_API_CREDENTIALS` above
# 2. Set `GOOGLE_LOGIN_ENABLED` to `true`
+17 -1
View File
@@ -77,16 +77,29 @@ NEXT_PUBLIC_HELPSCOUT_KEY=
# Inbox to send user feedback
SEND_FEEDBACK_EMAIL=
# Sengrid
# Used for email reminders in workflows
SENDGRID_API_KEY=
SENDGRID_EMAIL=
# Twilio
# Used to send SMS reminders in workflows
TWILIO_SID=
TWILIO_TOKEN=
TWILIO_MESSAGING_SID=
# This is used so we can bypass emails in auth flows for E2E testing
# Set it to "1" if you need to run E2E tests locally
NEXT_PUBLIC_IS_E2E=
# Used for internal billing system
NEXT_PUBLIC_STRIPE_PRO_PLAN_PRODUCT=
NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE=
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE=
NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRO_PLAN_PRODUCT_ID=
STRIPE_PREMIUM_PLAN_PRODUCT_ID=
STRIPE_FREE_PLAN_PRODUCT_ID=
# Use for internal Public API Keys and optional
API_KEY_PREFIX=cal_
@@ -114,3 +127,6 @@ EMAIL_SERVER_PASSWORD='<office365_password>'
## @see https://support.google.com/accounts/answer/185833
# EMAIL_SERVER_PASSWORD='<gmail_app_password>'
# **********************************************************************************************************
# Set the following value to true if you wish to enable Team Impersonation
NEXT_PUBLIC_TEAM_IMPERSONATION=false
+5
View File
@@ -0,0 +1,5 @@
"♻️ autoupdate":
- apps/web/public/static/locales/**/*.json
automerge:
- apps/web/public/static/locales/**/*.json
+22
View File
@@ -0,0 +1,22 @@
name: Add bugs to Bug Hunting
on:
issues:
types:
- opened
- labeled
pull_request:
types:
- opened
- labeled
jobs:
add-to-project:
name: Add issue 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 }}
labeled: 🐛 bug, 🎨 needs design, 🙋🏻‍♂️help wanted, 🚨 urgent
@@ -0,0 +1,36 @@
# .github/workflows/chromatic.yml
# Workflow name
name: 'Chromatic'
# Event for the workflow
on:
pull_request_target: # So we can test on forks
branches:
- main
paths:
- apps/storybook/**
- packages/ui/**
workflow_dispatch:
# List of jobs
jobs:
chromatic-deployment:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }} # So we can test on forks
fetch-depth: 0
- name: Install dependencies
run: yarn
# 👇 Adds Chromatic as a step in the workflow
- name: Publish to Chromatic
uses: chromaui/action@v1
# Options required to the GitHub Chromatic Action
with:
# 👇 Chromatic projectToken, refer to the manage page to obtain it.
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
workingDir: "apps/storybook"
buildScriptName: "build"
+6 -10
View File
@@ -3,15 +3,11 @@ on:
pull_request_target:
branches:
- main
paths:
- "**.tsx?"
jobs:
types:
name: Check types
strategy:
matrix:
node: ["16.x"]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
check-types:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v2
@@ -19,10 +15,10 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 2
- name: Use Node ${{ matrix.node }}
- name: Use Node 16.x
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
node-version: 16.x
cache: "yarn"
- run: yarn --frozen-lockfile
- run: yarn type-check
+1 -1
View File
@@ -7,7 +7,7 @@ on:
# Runs “At minute 0, 15, 30, and 45.” (see https://crontab.guru)
- cron: "0,15,30,45 * * * *"
jobs:
cron:
cron-bookingReminder:
env:
APP_URL: ${{ secrets.APP_URL }}
CRON_API_KEY: ${{ secrets.CRON_API_KEY }}
+1 -1
View File
@@ -7,7 +7,7 @@ on:
# Runs “At minute 0, 15, 30, and 45.” (see https://crontab.guru)
- cron: "0,15,30,45 * * * *"
jobs:
cron:
cron-downgradeUsers:
env:
APP_URL: ${{ secrets.APP_URL }}
CRON_API_KEY: ${{ secrets.CRON_API_KEY }}
@@ -0,0 +1,23 @@
name: Cron - scheduleEmailReminders
on:
# "Scheduled workflows run on the latest commit on the default or base branch."
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
schedule:
# Runs “At minute 0, 15, 30, and 45.” (see https://crontab.guru)
- cron: "0,15,30,45 * * * *"
jobs:
cron-scheduleEmailReminders:
env:
APP_URL: ${{ secrets.APP_URL }}
CRON_API_KEY: ${{ secrets.CRON_API_KEY }}
runs-on: ubuntu-latest
steps:
- name: cURL request
if: ${{ env.APP_URL && env.CRON_API_KEY }}
run: |
curl ${{ secrets.APP_URL }}/api/cron/workflows/scheduleEmailReminders \
-X POST \
-H 'content-type: application/json' \
-H 'authorization: ${{ secrets.CRON_API_KEY }}' \
--fail
@@ -0,0 +1,23 @@
name: Cron - scheduleSMSReminders
on:
# "Scheduled workflows run on the latest commit on the default or base branch."
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
schedule:
# Runs “At minute 0, 15, 30, and 45.” (see https://crontab.guru)
- cron: "0,15,30,45 * * * *"
jobs:
cron-scheduleSMSReminders:
env:
APP_URL: ${{ secrets.APP_URL }}
CRON_API_KEY: ${{ secrets.CRON_API_KEY }}
runs-on: ubuntu-latest
steps:
- name: cURL request
if: ${{ env.APP_URL && env.CRON_API_KEY }}
run: |
curl ${{ secrets.APP_URL }}/api/cron/workflows/scheduleSMSReminders \
-X POST \
-H 'content-type: application/json' \
-H 'authorization: ${{ secrets.CRON_API_KEY }}' \
--fail
+18
View File
@@ -0,0 +1,18 @@
name: Staging DB Snapshot
on:
schedule:
- cron: "0 4 * * 1-5" # At 04:00 on every day-of-week from Monday through Friday.
workflow_dispatch:
jobs:
db-staging-snapshot:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Snaplet CLI
run: curl -sL https://app.snaplet.dev/get-cli/ | bash
- name: Restore Snapshot
run: snaplet snapshot restore -y
env:
SNAPLET_DATABASE_URL: ${{ secrets.SNAPLET_DATA_TARGET_DB_URL }}
SNAPLET_ACCESS_TOKEN: ${{ secrets.SNAPLET_ACCESS_TOKEN }}
SNAPLET_DATABASE_ID: ${{ secrets.SNAPLET_DATABASE_ID }}
+99
View File
@@ -0,0 +1,99 @@
name: E2E App-Store Apps
on:
push:
branches: [ feature/event-routing ]
pull_request_target: # So we can test on forks
branches:
- main
paths-ignore:
- apps/api/**
- apps/console/**
- apps/docs/**
- apps/swagger/**
- apps/website/**
- apps/web/public/**
jobs:
test:
timeout-minutes: 20
name: E2E App-Store Apps
strategy:
matrix:
node: ["16.x"]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
env:
DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
NEXT_PUBLIC_WEBAPP_URL: http://localhost:3000
NEXT_PUBLIC_WEBSITE_URL: http://localhost:3000
NEXTAUTH_SECRET: secret
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
GOOGLE_LOGIN_ENABLED: true
# CRON_API_KEY: xxx
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
PAYMENT_FEE_PERCENTAGE: 0.005
PAYMENT_FEE_FIXED: 10
SAML_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
SAML_ADMINS: pro@example.com
NEXTAUTH_URL: http://localhost:3000/api/auth
NEXT_PUBLIC_IS_E2E: 1
# EMAIL_FROM: e2e@cal.com
# EMAIL_SERVER_HOST: ${{ secrets.CI_EMAIL_SERVER_HOST }}
# EMAIL_SERVER_PORT: ${{ secrets.CI_EMAIL_SERVER_PORT }}
# EMAIL_SERVER_USER: ${{ secrets.CI_EMAIL_SERVER_USER }}
# EMAIL_SERVER_PASSWORD: ${{ secrets.CI_EMAIL_SERVER_PASSWORD }}
# MS_GRAPH_CLIENT_ID: xxx
# MS_GRAPH_CLIENT_SECRET: xxx
# ZOOM_CLIENT_ID: xxx
# ZOOM_CLIENT_SECRET: xxx
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
services:
postgres:
image: postgres:12.1
env:
POSTGRES_USER: postgres
POSTGRES_DB: calendso
ports:
- 5432:5432
steps:
- name: Checkout repo
uses: actions/checkout@v2
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
- name: Use Node ${{ matrix.node }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
cache: "yarn"
- name: Cache playwright binaries
uses: actions/cache@v2
id: playwright-cache
with:
path: |
~/Library/Caches/ms-playwright
~/.cache/ms-playwright
${{ github.workspace }}/node_modules/playwright
key: cache-playwright-${{ hashFiles('**/yarn.lock') }}
restore-keys: cache-playwright-
- run: yarn --frozen-lockfile
- name: Install playwright deps
# if: steps.playwright-cache.outputs.cache-hit != 'true'
run: yarn playwright install --with-deps
- name: Run Tests
run: yarn app-e2e-quick
- name: Upload Test Results
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name: test-results-core
path: packages/app-store/**/playwright/results
+11 -19
View File
@@ -1,27 +1,16 @@
name: E2E tests Embed
name: E2E Embed tests and booking flow(for non-embed as well)
on:
pull_request_target: # So we can test on forks
branches:
- main
# Embed e2e - tests verify booking flow which is applicable to non-embed case also. So, don't ignore apps/web changes.
paths-ignore:
- apps/api/**
- apps/console/**
- apps/docs/**
- apps/swagger/**
- apps/website/**
- apps/web/public/**
- tests/**
- playwright/**
paths:
- apps/web/**
- packages/embeds/**
jobs:
test:
e2e-embed:
timeout-minutes: 20
name: Embed and booking flow(for non-embed as well)
strategy:
matrix:
node: ["16.x"]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
runs-on: ubuntu-latest
env:
DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
@@ -33,6 +22,9 @@ jobs:
# CRON_API_KEY: xxx
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_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 }}
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE }}
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
@@ -69,10 +61,10 @@ jobs:
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
- name: Use Node ${{ matrix.node }}
- name: Use Node 16.x
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
node-version: 16.x
cache: "yarn"
- name: Cache playwright binaries
+13 -11
View File
@@ -3,17 +3,13 @@ on:
pull_request_target: # So we can test on forks
branches:
- main
paths-ignore:
- apps/web/public/static/locales/**
paths:
- apps/web/**
- packages/**
jobs:
test:
e2e:
timeout-minutes: 20
name: Testing ${{ matrix.node }} and ${{ matrix.os }}
strategy:
matrix:
node: ["16.x"]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
runs-on: ubuntu-latest
env:
DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
@@ -25,9 +21,15 @@ jobs:
# CRON_API_KEY: xxx
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_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 }}
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE }}
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
STRIPE_PRO_PLAN_PRODUCT_ID: ${{ secrets.CI_STRIPE_PRO_PLAN_PRODUCT_ID }}
STRIPE_PREMIUM_PLAN_PRODUCT_ID: ${{ secrets.CI_STRIPE_PREMIUM_PLAN_PRODUCT_ID }}
STRIPE_FREE_PLAN_PRODUCT_ID: ${{ secrets.CI_STRIPE_FREE_PLAN_PRODUCT_ID }}
PAYMENT_FEE_PERCENTAGE: 0.005
PAYMENT_FEE_FIXED: 10
SAML_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
@@ -61,10 +63,10 @@ jobs:
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
- name: Use Node ${{ matrix.node }}
- name: Use Node 16.x
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
node-version: 16.x
cache: "yarn"
- name: Cache playwright binaries
+17
View File
@@ -0,0 +1,17 @@
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
labeler:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
# Checking the actor will prevent your Action run failing on non-Dependabot
# PRs but also ensures that it only does work for Dependabot PRs.
if: ${{ github.actor == 'github-actions[bot]' }}
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
+9 -9
View File
@@ -3,13 +3,12 @@ on:
pull_request_target:
branches:
- main
paths:
- "**.tsx?"
- "**.jsx?"
jobs:
lint:
strategy:
matrix:
node: ["16.x"]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
runs-on: ubuntu-latest
steps:
- name: Checkout repo
@@ -18,10 +17,10 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 2
- name: Use Node ${{ matrix.node }}
- name: Use Node 16.x
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
node-version: 16.x
cache: "yarn"
- name: Install deps
@@ -36,10 +35,11 @@ jobs:
run: jq -s '[.[]]|flatten' lint-results/*.json &> lint-results/eslint_report.json
- name: Annotate Code Linting Results
uses: ataylorme/eslint-annotate-action@1.2.0
uses: ataylorme/eslint-annotate-action@v2
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
repo-token: ${{ secrets.GITHUB_TOKEN }}
report-json: "lint-results/eslint_report.json"
only-pr-files: false
- name: Upload ESLint report
if: ${{ always() }}
+36
View File
@@ -0,0 +1,36 @@
name: "Meta Workflow: Require Conditional Status Checks"
on:
pull_request:
branches:
- main
jobs:
required-checks:
runs-on: ubuntu-latest
steps:
- name: Ensure All Conditional Checks Have Passed
uses: blend/require-conditional-status-checks@2022.02.04
with:
timeout: 15m
github-token: ${{ github.token }}
interval: 20s
# Question marks aren't working
# @see https://github.com/blend/require-conditional-status-checks/pull/11
checks-yaml: |
- job: lint
paths:
- /**/*.jsx
- /**/*.js
- /**/*.tsx
- /**/*.ts
- job: check-types
paths:
- /**/*.tsx
- /**/*.ts
- job: e2e
paths:
- /apps/web/**
- /packages/**
- job: e2e-embed
paths:
- /apps/web/**
- /packages/embeds/**
+7 -8
View File
@@ -3,7 +3,6 @@ on:
schedule:
- cron: "15 */4 * * *"
workflow_dispatch: ~
jobs:
submodule-sync:
name: Submodule update
@@ -11,11 +10,11 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: run action
uses: releasehub-com/github-action-create-pr-parent-submodule@v1
with:
github_token: ${{ secrets.GH_ACCESS_TOKEN }}
parent_repository: "calcom/cal.com"
checkout_branch: "main"
pr_against_branch: "main"
owner: "calcom"
submodules: recursive
token: ${{ secrets.GH_ACCESS_TOKEN }}
- name: Commit
run: |
git config user.email "actions@github.com"
git config user.name "github-actions"
git commit -am "Auto updated submodule references" && git push || echo "No changes to commit"
+3
View File
@@ -73,6 +73,9 @@ dist
# Linting
lint-results
#Storybook
apps/storybook/build-storybook.log
# Snaplet
.snaplet/snapshots
.snaplet/structure.d.ts
+1 -2
View File
@@ -4,5 +4,4 @@ version = 1
autoupdate_label = "♻️ autoupdate"
[approve]
auto_approve_usernames = ["dependabot"]
auto_approve_usernames = ["dependabot", "github-actions"]
+32 -32
View File
@@ -1,7 +1,7 @@
// This transform config was generated by Snaplet.
// Snaplet found fields that may contain personally identifiable information (PII)
// and used that to populate this file.
import { copycat } from "@snaplet/copycat";
import { copycat as c } from "@snaplet/copycat";
import type { Transform } from "./structure";
@@ -11,13 +11,13 @@ function hasStringProp<T extends string>(x: unknown, key: T): x is { [key in T]:
function replaceKeyIfExists<T extends string>(x: object, key: T) {
if (hasStringProp(x, key)) {
return { ...x, [key]: copycat.uuid(x[key]) };
return { ...x, [key]: c.uuid(x[key]) };
}
return x;
}
function generateSlug(x: string) {
return copycat.words(x, { max: 3 }).split(" ").join("-");
return c.words(x, { max: 3 }).split(" ").join("-");
}
function replaceSensitiveKeys(record: object) {
@@ -39,57 +39,57 @@ function replaceSensitiveKeys(record: object) {
};
}
const generateUsername = (x: string) => `${c.firstName(x)}-${c.lastName(x)}${c.int(x, { min: 2, max: 99 })}`;
const config: Transform = () => ({
public: {
_user_eventtype: ({ row }) => ({
A: copycat.int(row.A),
B: copycat.int(row.B),
}),
ApiKey: ({ row }) => ({
note: copycat.sentence(row.note),
hashedKey: c.uuid(row.hashedKey),
}),
App: ({ row }) => ({
keys: replaceSensitiveKeys(row.keys),
}),
Attendee: ({ row }) => ({
email: copycat.email(row.email),
name: copycat.fullName(row.name),
timeZone: copycat.timezone(row.timeZone),
}),
Availability: ({ row }) => ({
// date: copycat.fullName(row.date),
email: c.email(row.email),
name: c.fullName(row.name),
timeZone: c.timezone(row.timeZone),
}),
Credential: ({ row }) => ({
key: typeof row.key === "string" ? copycat.uuid(row.key) : replaceSensitiveKeys(row.key),
key: typeof row.key === "string" ? c.uuid(row.key) : replaceSensitiveKeys(row.key),
}),
EventType: ({ row }) => ({
slug: generateSlug(row.slug),
timeZone: copycat.timezone(row.timeZone),
}),
Feedback: ({ row }) => ({
// date: copycat.dateString(row.date),
timeZone: c.timezone(row.timeZone),
eventName: c.words(row.eventName, { max: 3 }),
}),
ResetPasswordRequest: ({ row }) => ({
email: copycat.email(row.email),
email: c.email(row.email),
}),
Schedule: ({ row }) => ({
timeZone: copycat.timezone(row.timeZone),
timeZone: c.timezone(row.timeZone),
}),
Team: ({ row }) => ({
bio: copycat.sentence(row.bio),
name: copycat.words(row.name, { max: 2 }),
bio: c.sentence(row.bio),
name: c.words(row.name, { max: 2 }),
slug: generateSlug(row.slug),
}),
users: ({ row }) => ({
bio: copycat.sentence(row.bio),
email: copycat.email(row.email),
name: copycat.fullName(row.name),
password: copycat.password(row.password),
timeZone: copycat.timezone(row.timeZone),
username: generateSlug(row.username),
}),
users: ({ row }) =>
row.role !== "ADMIN"
? {
bio: c.sentence(row.bio),
email: c.email(row.email),
name: c.fullName(row.name),
password: c.password(row.password),
timeZone: c.timezone(row.timeZone),
username: generateUsername(row.username),
}
: row,
VerificationToken: ({ row }) => ({
token: copycat.uuid(row.token),
token: c.uuid(row.token),
}),
Account: ({ row }) => ({
access_token: c.uuid(row.access_token),
refresh_token: c.uuid(row.refresh_token),
}),
},
});
-4
View File
@@ -22,10 +22,6 @@
"group": {
"kind": "build",
"isDefault": true
},
// Try start the task on folder open
"runOptions": {
"runOn": "folderOpen"
}
},
{
+1 -1
View File
@@ -1,6 +1,6 @@
# Contributing to Cal.com
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
- Before jumping into a PR be sure to search [existing PRs](https://github.com/calcom/cal.com/pulls) or [issues](https://github.com/calcom/cal.com/issues) for an open or closed item that relates to your submission.
+43 -7
View File
@@ -26,6 +26,7 @@
<p align="center">
<a href="https://cal.com/slack"><img src="https://img.shields.io/badge/Slack-calendso.slack.com-%234A154B" alt="Join Cal.com Slack"></a>
<a href="https://www.producthunt.com/posts/calendso"><img src="https://img.shields.io/badge/Product%20Hunt-%231%20Product%20of%20the%20Month-%23DA552E" alt="Product Hunt"></a>
<a href="https://status.cal.com"><img src="https://betteruptime.com/status-badges/v1/monitor/a9kf.svg" alt="Uptime"></a>
<a href="https://github.com/calcom/cal.com/stargazers"><img src="https://img.shields.io/github/stars/calcom/cal.com" alt="Github Stars"></a>
<a href="https://news.ycombinator.com/item?id=26817795"><img src="https://img.shields.io/badge/Hacker%20News-311-%23FF6600" alt="Hacker News"></a>
<a href="https://github.com/calcom/cal.com/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-AGPLv3-purple" alt="License"></a>
@@ -36,6 +37,8 @@
<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://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>
</p>
@@ -44,7 +47,7 @@
## About The Project
<img width="100%" alt="booking-screen" src="https://user-images.githubusercontent.com/8019099/134363898-4b29e18f-3e61-42b7-95bc-10891056249d.gif">
<img width="100%" alt="booking-screen" src="https://user-images.githubusercontent.com/8019099/176390354-f1bc7069-0341-437a-9bb8-eb41092b4016.gif">
# Scheduling infrastructure for absolutely everyone
@@ -84,7 +87,7 @@ To get a local copy up and running, please follow these simple steps.
Here is what you need to be able to run Cal.
- Node.js (Version: >=14.x <17)
- Node.js (Version: >=15.x <17)
- PostgreSQL
- Yarn _(recommended)_
@@ -94,7 +97,7 @@ Here is what you need to be able to run Cal.
### Setup
1. Clone the repo into a public GitHub repository (to comply with AGPLv3. To clone in a private repository, [acquire a commercial license](https://cal.com/sales))
1. Clone the repo into a public GitHub repository (or fork https://github.com/calcom/cal.com/fork). If you plan to distribute the code, keep the source code public to comply with [AGPLv3](https://github.com/calcom/cal.com/blob/main/LICENSE). To clone in a private repository, [acquire a commercial license](https://cal.com/sales))
```sh
git clone https://github.com/calcom/cal.com.git
@@ -167,7 +170,7 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env
</details>
1. Set a 32 character random string in your .env file for the `CALENDSO_ENCRYPTION_KEY` (You can use a command like `openssl rand -base64 24` to generate one).
1. Set up the database using the Prisma schema (found in `apps/web/prisma/schema.prisma`)
1. Set up the database using the Prisma schema (found in `packages/prisma/schema.prisma`)
```sh
yarn workspace @calcom/prisma db-deploy
@@ -189,7 +192,7 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env
1. Click on the `User` model to add a new user record.
1. Fill out the fields `email`, `username`, `password`, and set `metadata` to empty `{}` (remembering to encrypt your password with [BCrypt](https://bcrypt-generator.com/)) and click `Save 1 Record` to create your first user.
> New users are set on a `TRIAL` plan by default. You might want to adjust this behavior to your needs in the `apps/web/prisma/schema.prisma` file.
> New users are set on a `TRIAL` plan by default. You might want to adjust this behavior to your needs in the `packages/prisma/schema.prisma` file.
1. Open a browser to [http://localhost:3000](http://localhost:3000) and login with your just created, first user.
### E2E-Testing
@@ -278,11 +281,19 @@ The Docker configuration can be found [in our docker repository](https://github.
You can deploy Cal on [Railway](https://railway.app/) using the button above. The team at Railway also have a [detailed blog post](https://blog.railway.app/p/calendso) on deploying Cal on their platform.
### Vercel
Currently Vercel Pro Plan is required to be able to Deploy this application with Vercel, due to limitations on the number of serverless functions on the free plan.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fcalcom%2Fcal.com&env=DATABASE_URL,NEXT_PUBLIC_WEBAPP_URL,NEXTAUTH_URL,NEXTAUTH_SECRET,CRON_API_KEY,CALENDSO_ENCRYPTION_KEY,NEXT_PUBLIC_LICENSE_CONSENT&envDescription=See%20all%20available%20env%20vars&envLink=https%3A%2F%2Fgithub.com%2Fcalcom%2Fcal.com%2Fblob%2Fmain%2F.env.example&project-name=cal&repo-name=cal.com&build-command=cd%20../..%20%26%26%20yarn%20build&root-directory=apps%2Fweb%2F)
<!-- ROADMAP -->
## Roadmap
See the [roadmap project](https://github.com/orgs/calcom/projects/1) for a list of proposed features (and known issues). You can change the view to see planned tagged releases.
<a href="https://cal.com/roadmap"><img src="https://user-images.githubusercontent.com/8019099/176388295-25081ca4-207e-4468-8079-37b195fa8e59.png" alt="Cal.com Roadmap" /></a>
See the [roadmap project](https://cal.com/roadmap) for a list of proposed features (and known issues). You can change the view to see planned tagged releases.
<!-- CONTRIBUTING -->
@@ -292,7 +303,7 @@ Please see our [contributing guide](/CONTRIBUTING.md).
### Good First Issues
We have a list of [good first issues](https://github.com/calcom/cal.com/labels/✅%20good%20first%20issue) that contain bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process.
We have a list of [help wanted](https://github.com/orgs/calcom/projects/1/views/25) that contain small features and bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process.
## Integrations
@@ -418,6 +429,31 @@ Next make sure you have your app running `yarn dx`. Then in the slack chat type
1. Select all events for the webhook you interested, e.g. `sleep_created`
1. Copy the webhook secret (`sec...`) to `VITAL_WEBHOOK_SECRET` in the .env.appStore file.
## Workflows
### Setting up SendGrid for Email reminders
1. Create a SendGrid account (https://signup.sendgrid.com/)
2. Go to Settings -> API keys and create an API key
3. Copy API key to your .env file into the SENDGRID_API_KEY field
4. Go to Settings -> Sender Authentication and verify a single sender
5. Copy the verified E-Mail to your .env file into the SENDGRID_EMAIL field
### Setting up Twilio for SMS reminders
1. Create a Twilio account (https://www.twilio.com/try-twilio)
2. Click ‘Get a Twilio phone number’
3. Copy Account SID to your .env file into the TWILIO_SID field
4. Copy Auth Token to your .env file into the TWILIO_TOKEN field
5. Create a messaging service (Develop -> Messaging -> Services)
6. Choose any name for the messaging service
7. Click 'Add Senders'
8. Choose phone number as sender type
9. Add the listed phone number
10. Leave all other fields as they are
11. Complete setup and click ‘View my new Messaging Service’
12. Copy Messaging Service SID to your .env file into the TWILIO_MESSAGING_SID field
<!-- LICENSE -->
## License
+39
View File
@@ -0,0 +1,39 @@
# Security
Contact: security@cal.com
Based on [https://supabase.com/.well-known/security.txt](https://supabase.com/.well-known/security.txt)
At Cal.com, we consider the security of our systems a top priority. But no matter how much effort we put into system security, there can still be vulnerabilities present.
If you discover a vulnerability, we would like to know about it so we can take steps to address it as quickly as possible. We would like to ask you to help us better protect our clients and our systems.
## Out of scope vulnerabilities:
- Clickjacking on pages with no sensitive actions.
- Unauthenticated/logout/login CSRF.
- Attacks requiring MITM or physical access to a user's device.
- Any activity that could lead to the disruption of our service (DoS).
- Content spoofing and text injection issues without showing an attack vector/without being able to modify HTML/CSS.
- Email spoofing
- Missing DNSSEC, CAA, CSP headers
- Lack of Secure or HTTP only flag on non-sensitive cookies
- Deadlinks
## Please do the following:
- E-mail your findings to [security@cal.com](mailto:security@cal.com).
- Do not run automated scanners on our infrastructure or dashboard. If you wish to do this, contact us and we will set up a sandbox for you.
- Do not take advantage of the vulnerability or problem you have discovered, for example by downloading more data than necessary to demonstrate the vulnerability or deleting or modifying other people's data,
- Do not reveal the problem to others until it has been resolved,
- Do not use attacks on physical security, social engineering, distributed denial of service, spam or applications of third parties,
- Do provide sufficient information to reproduce the problem, so we will be able to resolve it as quickly as possible. Usually, the IP address or the URL of the affected system and a description of the vulnerability will be sufficient, but complex vulnerabilities may require further explanation.
## What we promise:
- We will respond to your report within 3 business days with our evaluation of the report and an expected resolution date,
- If you have followed the instructions above, we will not take any legal action against you in regard to the report,
- We will handle your report with strict confidentiality, and not pass on your personal details to third parties without your permission,
- We will keep you informed of the progress towards resolving the problem,
- In the public information concerning the problem reported, we will give your name as the discoverer of the problem (unless you desire otherwise), and
- We strive to resolve all problems as quickly as possible, and we would like to play an active role in the ultimate publication on the problem after it is resolved.
+4 -3
View File
@@ -4,19 +4,20 @@
"description": "",
"main": "index.js",
"scripts": {
"build": "next build",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
"dev": "PORT=4000 next",
"lint": "next lint",
"type-check": "tsc --pretty --noEmit",
"lint:fix": "eslint . --ext .ts,.js,.tsx,.jsx --fix",
"lint:report": "eslint . --format json --output-file ../../lint-results/docs.json",
"start": "PORT=4000 next start",
"build": "next build"
"type-check": "tsc --pretty --noEmit"
},
"author": "Cal.com, Inc.",
"license": "MIT",
"dependencies": {
"iframe-resizer-react": "^1.1.0",
"next": "^12.1.6",
"next": "^12.2.0",
"nextra": "^1.1.0",
"nextra-theme-docs": "^1.2.2",
"react": "^18.1.0",
+2 -2
View File
@@ -10,10 +10,10 @@ We use a number of style guides written by other amazing companies, simply becau
We don't expect you to study every single rule of each style guide, but these are great reference points as to how your code should be styled. We may reject a pull request if your code style significantly differs from these style guides however.
## ESLint & Prettier
Calendso uses the ESLint and Prettier formatting tools, and the repository comes with defined rules for each tool. We recommend setting up both tools and using these to help automatically style your code to our guidelines.
Cal.com uses the ESLint and Prettier formatting tools, and the repository comes with defined rules for each tool. We recommend setting up both tools and using these to help automatically style your code to our guidelines.
## JavaScript/TypeScript
We use the [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) for all JavaScript and Typescript code.
We use the [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) for all JavaScript and TypeScript code.
## HTML & CSS
We use the [Google HTML/CSS Style Guide](https://google.github.io/styleguide/htmlcssguide.html) for any HTML and CSS markup. However, exceptions to the HTML guide apply where JSX differentiates from standard HTML.
+1 -1
View File
@@ -25,7 +25,7 @@ To remove a product from your account that isn't listed in your Google Account,
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 `<CALENDSO URL>/api/integrations/googlecalendar/callback` replacing CALENDSO URL with the URI at which your application runs.
9. Under Authorized redirect URI's, select Add URI and then add the two URIs `<CALENDSO URL>/api/integrations/googlecalendar/callback` and `<CALENDSO URL>/api/auth/callback/google` replacing CALENDSO 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.
+1 -1
View File
@@ -18,7 +18,7 @@ You need a PostgresDB database hosted somewhere. [Heroku](https://www.heroku.com
git clone https://github.com/<your-fork>/cal.com.git
```
2. Copy the `.env.example` file in `apps/web`, rename it to `.env` and fill it with your settings ([See manual setup](https://github.com/calcom/cal.com#manual) and [Obtaining the Google API Credentials](https://github.com/calcom/cal.com#obtaining-the-google-api-credentials))
2. Copy the `.env.example` file in `apps/web`, rename it to `.env` and fill it with your settings ([See manual setup](https://github.com/calcom/cal.com#manual-setup) and [Obtaining the Google API Credentials](https://github.com/calcom/cal.com#obtaining-the-google-api-credentials))
3. Install packages with `yarn`
+5 -3
View File
@@ -1,3 +1,5 @@
import { SEO_IMG_DEFAULT, DOCS_URL } from "@calcom/lib/constants";
const themeConfig = {
github: "https://github.com/calcom/cal.com",
docsRepositoryBase: "https://github.com/calcom/cal.com/blob/main/apps/docs/pages",
@@ -18,11 +20,11 @@ const themeConfig = {
content="Explore advice and explanations for all of our features, and discover new tips and tricks to get the most out of your subscription."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://cal.com/og-image.png" />
<meta name="twitter:image" content={SEO_IMG_DEFAULT} />
<meta name="twitter:site:domain" content="docs.cal.com" />
<meta name="twitter:url" content="https://docs.cal.com" />
<meta name="twitter:url" content={DOCS_URL} />
<meta name="og:title" content="Cal.com Documentation" />
<meta name="og:image" content="https://cal.com/og-image.png" />
<meta name="og:image" content={SEO_IMG_DEFAULT} />
<meta name="apple-mobile-web-app-title" content="Cal.com Docs" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
+37
View File
@@ -0,0 +1,37 @@
# 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/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
storybook-static/
View File
+57
View File
@@ -0,0 +1,57 @@
const path = require("path");
module.exports = {
stories: ["../stories/**/*.stories.mdx", "../stories/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
"storybook-addon-designs",
"@storybook/addon-a11y",
"storybook-addon-next",
],
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"];
/**
* 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}}
*/
config.resolve.fallback = {
fs: false,
assert: false,
buffer: false,
console: false,
constants: false,
crypto: false,
domain: false,
events: false,
http: false,
https: false,
os: false,
path: false,
punycode: false,
process: false,
querystring: false,
stream: false,
string_decoder: false,
sys: false,
timers: false,
tty: false,
url: false,
util: false,
vm: false,
zlib: false,
};
return config;
},
};
@@ -0,0 +1,8 @@
<script>
window.isEmbed = ()=> {
return location.search.includes("embed=")
}
</script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@500&display=swap');
</style>
+24
View File
@@ -0,0 +1,24 @@
import { RouterContext } from "next/dist/shared/lib/router-context";
import * as NextImage from "next/image";
import "../styles/globals.css";
const OriginalNextImage = NextImage.default;
Object.defineProperty(NextImage, "default", {
configurable: true,
value: (props) => <OriginalNextImage {...props} unoptimized />,
});
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
nextRouter: {
Provider: RouterContext.Provider,
},
};
+21
View File
@@ -0,0 +1,21 @@
# 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
```
Vendored Executable
+5
View File
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
+7
View File
@@ -0,0 +1,7 @@
const withTM = require("next-transpile-modules")(["@calcom/dayjs", "@calcom/ui"]);
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
};
module.exports = withTM(nextConfig);
+63
View File
@@ -0,0 +1,63 @@
{
"name": "@calcom/storybook",
"version": "0.1.0",
"private": true,
"scripts": {
"dev:next": "next dev",
"build:next": "next build",
"start": "next start",
"lint": "next lint",
"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"
},
"dependencies": {
"@calcom/dayjs": "*",
"@calcom/ui": "*",
"@radix-ui/react-avatar": "^0.1.4",
"@radix-ui/react-collapsible": "^0.1.0",
"@radix-ui/react-dialog": "^0.1.0",
"@radix-ui/react-dropdown-menu": "^0.1.1",
"@radix-ui/react-id": "^0.1.0",
"@radix-ui/react-radio-group": "^0.1.1",
"@radix-ui/react-slider": "^0.1.1",
"@radix-ui/react-switch": "^0.1.1",
"@radix-ui/react-tooltip": "^0.1.0",
"next": "^12.2.0",
"next-transpile-modules": "^9.0.0",
"react": "18.1.0",
"react-dom": "18.1.0",
"react-feather": "^2.0.9",
"react-hot-toast": "^2.2.0"
},
"devDependencies": {
"@babel/core": "^7.18.6",
"@calcom/config": "*",
"@storybook/addon-a11y": "^6.5.9",
"@storybook/addon-actions": "^6.5.9",
"@storybook/addon-essentials": "^6.5.9",
"@storybook/addon-interactions": "^6.5.9",
"@storybook/addon-links": "^6.5.9",
"@storybook/addon-postcss": "^2.0.0",
"@storybook/builder-webpack5": "^6.5.9",
"@storybook/manager-webpack5": "^6.5.9",
"@storybook/react": "^6.5.9",
"@types/node": "16.9.1",
"@types/react": "18.0.9",
"@types/react-dom": "18.0.4",
"autoprefixer": "^10.4.7",
"babel-loader": "^8.2.5",
"chromatic": "^6.6.4",
"eslint": "^8.15.0",
"postcss": "^8.4.13",
"postcss-loader": "^7.0.0",
"storybook-addon-designs": "^6.3.1",
"storybook-addon-next": "^1.6.7",
"tailwindcss": "^3.1.3",
"tsconfig-paths-webpack-plugin": "^3.5.2",
"typescript": "^4.6.4"
},
"readme": "ERROR: No README data found!",
"_id": "@calcom/storybook@0.1.0"
}
+9
View File
@@ -0,0 +1,9 @@
import type { AppProps } from "next/app";
import "../styles/globals.css";
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
export default MyApp;
+63
View File
@@ -0,0 +1,63 @@
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;
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+4
View File
@@ -0,0 +1,4 @@
<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>

After

Width:  |  Height:  |  Size: 1.1 KiB

+26
View File
@@ -0,0 +1,26 @@
import { ComponentMeta } from "@storybook/react";
import Avatar from "@calcom/ui/v2/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 />
</>
);
};
@@ -0,0 +1,60 @@
import { ComponentMeta } from "@storybook/react";
import Avatar from "@calcom/ui/v2/Avatar";
import AvatarGroup from "@calcom/ui/v2/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 (
<>
<AvatarGroup size="lg" items={IMAGES} />
</>
);
};
+58
View File
@@ -0,0 +1,58 @@
import { ComponentMeta } from "@storybook/react";
import { Bell } from "react-feather";
import { Badge } from "@calcom/ui/v2";
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
@@ -0,0 +1,46 @@
import { ComponentStory, ComponentMeta } from "@storybook/react";
import { Info } from "react-feather";
import { Banner } from "@calcom/ui/v2";
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")}
/>
);
};
@@ -0,0 +1,22 @@
import { ComponentMeta } from "@storybook/react";
import { Breadcrumb, BreadcrumbItem } from "@calcom/ui/v2";
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",
},
};
+95
View File
@@ -0,0 +1,95 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Trash2 } from "react-feather";
import { Button as ButtonComponent } from "@calcom/ui/v2";
export default {
title: "Button",
component: ButtonComponent,
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 ButtonComponent>;
const Template: ComponentStory<typeof ButtonComponent> = (args) => <ButtonComponent {...args} />;
export const All = () => (
<div>
<h1>Primary</h1>
<div className="flex space-x-2">
<ButtonComponent aria-label="Button Text">Button Text</ButtonComponent>
<ButtonComponent disabled aria-label="Button Text">
Button Text
</ButtonComponent>
</div>
<h1>Secondary</h1>
<div className="flex space-x-2">
<ButtonComponent color="secondary" aria-label="Button Text">
Button Text
</ButtonComponent>
<ButtonComponent disabled color="secondary" aria-label="Button Text">
Button Text
</ButtonComponent>
<ButtonComponent size="icon" color="secondary" StartIcon={Trash2} aria-label="Button Text" />
</div>
<h1>Minimal</h1>
<div className="flex">
<ButtonComponent color="minimal" aria-label="Button Text">
Button Text
</ButtonComponent>
<ButtonComponent disabled color="minimal" aria-label="Button Text">
Button Text
</ButtonComponent>
<ButtonComponent size="icon" color="minimal" StartIcon={Trash2} aria-label="Button Text" />
</div>
<h1>Destructive</h1>
<ButtonComponent size="icon" color="destructive" StartIcon={Trash2} aria-label="Button Text" />
<h1>Tooltip</h1>
<ButtonComponent
tooltip="Deletes EventTypes"
size="icon"
color="destructive"
StartIcon={Trash2}
aria-label="Button Text"
/>
</div>
);
export const Button = Template.bind({});
Button.args = {
color: "primary",
children: "Button Text",
};
export const Disabled = Template.bind({});
Disabled.args = {
...Button.args,
disabled: true,
};
export const Loading = Template.bind({});
Loading.args = {
...Button.args,
loading: true,
};
export const Icon = Template.bind({});
Icon.args = {
color: "secondary",
StartIcon: Trash2,
size: "icon",
};
@@ -0,0 +1,31 @@
import { ComponentMeta } from "@storybook/react";
import { ArrowLeft, ArrowRight, Clipboard, Navigation2, Trash2 } from "react-feather";
import { Button, ButtonGroup } from "@calcom/ui/v2";
export default {
title: "Button Group",
component: ButtonGroup,
} 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" combined />
<Button StartIcon={Navigation2} size="icon" color="secondary" combined />
<Button StartIcon={Clipboard} size="icon" color="secondary" combined />
</ButtonGroup>
<ButtonGroup combined>
<Button StartIcon={ArrowLeft} size="icon" color="secondary" combined />
<Button StartIcon={ArrowRight} size="icon" color="secondary" combined />
</ButtonGroup>
</div>
);
+53
View File
@@ -0,0 +1,53 @@
import { ComponentMeta } from "@storybook/react";
import { BaseCardProps, Card } from "@calcom/ui/v2/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>
);
};
@@ -0,0 +1,31 @@
import { ComponentMeta } from "@storybook/react";
import { Checkbox } from "@calcom/ui/v2";
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
@@ -0,0 +1,208 @@
import { useState } from "react";
import ColorPicker from "@calcom/ui/v2/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)} />;
};
@@ -0,0 +1,17 @@
import { useState } from "react";
import DatePicker from "@calcom/ui/v2/booker/DatePicker";
export default {
title: "Datepicker",
component: DatePicker,
};
export const Default = () => {
const [selected, setSelected] = useState<Date | undefined>(undefined);
return (
<div style={{ width: "455px" }}>
<DatePicker selected={selected} onChange={setSelected} locale="en" />
</div>
);
};
@@ -0,0 +1,26 @@
import { ComponentMeta } from "@storybook/react";
import { Bell } from "react-feather";
import { EmptyScreen as EmptyScreenPattern } from "@calcom/ui/v2";
export default {
title: "pattern/Empty Screen",
component: EmptyScreenPattern,
decorators: [
(Story) => (
<div className="flex items-center justify-center">
<Story />
</div>
),
],
} as ComponentMeta<typeof EmptyScreenPattern>;
export const EmptyScreenS = () => (
<EmptyScreenPattern
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 cupidatat consequat commodo in aliqua ea et. Et quis sit enim proident dolor mollit consectetur tempor dolore reprehenderit consequat adipisicing reprehenderit officia. Sint eu sunt fugiat laborum Lorem irure aute nulla et. Do non in enim ipsum ea."
buttonText="Veniam ut ipsum"
buttonOnClick={() => console.log("Button Clicked")}
/>
);
@@ -0,0 +1,27 @@
import { ComponentMeta } from "@storybook/react";
import { useState } from "react";
import { FormStep } from "@calcom/ui/v2";
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>
);
};
@@ -0,0 +1,30 @@
// Disabling until we figure out what is happening with the RouterMock.
import { ComponentMeta } from "@storybook/react";
import { HorizontalTabs } from "@calcom/ui/v2/navigation/tabs";
import { HorizontalTabItemProps } from "@calcom/ui/v2/navigation/tabs/HorizontalTabItem";
export default {
title: "Horizontal Tabs",
component: HorizontalTabs,
} as ComponentMeta<typeof HorizontalTabs>;
const HorizontalTabsPropsDefault: HorizontalTabItemProps[] = [
{
name: "Tab One",
href: "/tab-one",
},
];
export const Default = () => (
<div className="w-full p-4" style={{ backgroundColor: "#F9FAFB" }}>
<HorizontalTabs tabs={HorizontalTabsPropsDefault} />
</div>
);
Default.parameters = {
nextRouter: {
path: "/[page]",
asPath: "/profile",
},
};
+68
View File
@@ -0,0 +1,68 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Copy } from "react-feather";
import DatePicker from "@calcom/ui/v2/form/DatePicker";
import { TextAreaField, TextField } from "@calcom/ui/v2/form/fields";
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()} />;
+52
View File
@@ -0,0 +1,52 @@
import { useState } from "react";
import { Info } from "react-feather";
import { Button } from "@calcom/ui/v2";
import { Dialog, DialogContent, DialogTrigger } from "@calcom/ui/v2/Dialog";
import { TextField } from "@calcom/ui/v2/form/fields";
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>
);
};
@@ -0,0 +1,25 @@
import { ComponentMeta } from "@storybook/react";
import { Toaster } from "react-hot-toast";
import { Button } from "@calcom/ui/v2";
import showToast from "@calcom/ui/v2/notfications";
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>
);
@@ -0,0 +1,52 @@
import { ComponentMeta } from "@storybook/react";
import { Search } from "react-feather";
import { Button, PageHeader } from "@calcom/ui/v2";
import { TextField } from "@calcom/ui/v2/form";
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
@@ -0,0 +1,19 @@
import * as Radio from "@calcom/ui/v2/form/radio-area/Radio";
import { RadioField } from "@calcom/ui/v2/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>
);
};
+10
View File
@@ -0,0 +1,10 @@
import { ComponentMeta } from "@storybook/react";
import { Select } from "@calcom/ui/v2";
export default {
title: "Form/Select",
component: Select,
} as ComponentMeta<typeof Select>;
export const Single = () => <Select items={[{ value: "Test Test" }]} />;
+15
View File
@@ -0,0 +1,15 @@
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>
);
@@ -0,0 +1,67 @@
// 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}></VerticalTabItem>
// </div>
// );
// export const Active = () => (
// <div className="h-20 w-full bg-gray-100 p-4">
// <VerticalTabItem {...TabItemProps}></VerticalTabItem>
// </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></VerticalTabItem>
// </div>
// );
// export const NoIconNoInfo = () => (
// <div className="h-20 w-full bg-gray-100 p-4">
// <VerticalTabItem name="Event Setup" href="/settings/event"></VerticalTabItem>
// </div>
// );
// export const IconNoInfo = () => (
// <div className="h-20 w-full bg-gray-100 p-4">
// <VerticalTabItem name="Event Setup" href="/settings/event" icon={Link}></VerticalTabItem>
// </div>
// );
// export const IconNoInfoActive = () => (
// <div className="h-20 w-full bg-gray-100 p-4">
// <VerticalTabItem name="Event Setup" href="/settings/events" icon={Link}></VerticalTabItem>
// </div>
// );
// IconNoInfoActive.paramaters = {
// nextRouter: {
// path: "/settings/[tab]",
// asPath: "/settings/events",
// },
// };
@@ -0,0 +1,85 @@
// 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/navigation/tabs";
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",
},
};
+13
View File
@@ -0,0 +1,13 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--brand-color: #292929;
--brand-text-color: #ffffff;
--brand-color-dark-mode: #fafafa;
--brand-text-color-dark-mode: #292929;
font-family: 'Inter', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
+167
View File
@@ -0,0 +1,167 @@
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",
}),
},
},
};
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "@calcom/tsconfig/nextjs.json",
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"],
"compilerOptions": {
"noImplicitAny": false,
"baseUrl": "."
}
}
+6 -2
View File
@@ -3,14 +3,18 @@
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "PORT=4200 next dev",
"build": "next build",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
"dev": "PORT=4200 next dev",
"lint": "next lint",
"lint:fix": "eslint . --ext .ts,.js,.tsx,.jsx --fix",
"lint:report": "eslint . --format json --output-file ../../lint-results/swagger.json",
"start": "PORT=4200 next start"
},
"dependencies": {
"highlight.js": "^11.5.1",
"isarray": "2.0.5",
"next": "12.1.6",
"next": "12.2.0",
"openapi-snippet": "^0.13.0",
"react": "18.1.0",
"react-dom": "18.1.0",
+8 -15
View File
@@ -1,24 +1,17 @@
{
"extends": "@calcom/tsconfig/nextjs.json",
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": ".",
"paths": {
"@lib/*": ["lib/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"include": [
"next-env.d.ts",
"../../packages/types/*.d.ts",
"../../packages/types/next-auth.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": ["node_modules"]
}
+3 -3
View File
@@ -1,7 +1,7 @@
import { XIcon } from "@heroicons/react/outline";
import { useState } from "react";
import { useLocale } from "@lib/hooks/useLocale";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Icon } from "@calcom/ui/Icon";
export default function AddToHomescreen() {
const { t } = useLocale();
@@ -40,7 +40,7 @@ export default function AddToHomescreen() {
type="button"
className="-mr-1 flex rounded-md p-2 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-white">
<span className="sr-only">{t("dismiss")}</span>
<XIcon className="h-6 w-6 text-white" aria-hidden="true" />
<Icon.X className="h-6 w-6 text-white" aria-hidden="true" />
</button>
</div>
</div>
@@ -4,11 +4,11 @@ import { OptionProps } from "react-select";
import { InstallAppButton } from "@calcom/app-store/components";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import type { App } from "@calcom/types/App";
import { Button } from "@calcom/ui";
import { QueryCell } from "@lib/QueryCell";
import { trpc } from "@lib/trpc";
interface AdditionalCalendarSelectorProps {
isLoading?: boolean;
@@ -50,7 +50,7 @@ const AdditionalCalendarSelector = ({ isLoading }: AdditionalCalendarSelectorPro
}));
return (
<Select
name={"additionalCalendar"}
name="additionalCalendar"
placeholder={t("connect_additional_calendar")}
options={options}
styles={{
@@ -73,7 +73,7 @@ const AdditionalCalendarSelector = ({ isLoading }: AdditionalCalendarSelectorPro
},
}}
isSearchable={false}
className="mt-1 mb-2 block w-full min-w-0 flex-1 rounded-none rounded-r-sm border-gray-300 font-medium text-gray-700 sm:text-sm"
className="mt-1 mb-2 block w-full min-w-0 flex-1 rounded-none rounded-r-sm border-gray-300 text-sm font-medium text-gray-700"
isLoading={isLoading}
components={{ Option: ImageOption }}
/>
+246 -197
View File
@@ -1,26 +1,19 @@
import {
BookOpenIcon,
CheckIcon,
DocumentTextIcon,
ExternalLinkIcon,
FlagIcon,
MailIcon,
PlusIcon,
ShieldCheckIcon,
} from "@heroicons/react/outline";
import { ChevronLeftIcon } from "@heroicons/react/solid";
import Link from "next/link";
import React, { useEffect, 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 Badge from "@calcom/ui/Badge";
import { Icon } from "@calcom/ui/Icon";
import Shell from "@calcom/ui/Shell";
import Shell from "@components/Shell";
import Badge from "@components/ui/Badge";
export default function App({
const Component = ({
name,
type,
logo,
@@ -36,25 +29,19 @@ export default function App({
email,
tos,
privacy,
}: {
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;
}) {
isProOnly,
}: Parameters<typeof App>[0]) => {
const { t } = useLocale();
const { data: user } = trpc.useQuery(["viewer.me"]);
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",
@@ -90,176 +77,238 @@ export default function App({
getInstalledApp(type);
}, [type]);
const allowedMultipleInstalls = categories.indexOf("calendar") > -1;
return (
<>
<Shell large isPublic>
<div className="-mx-4 md:-mx-8">
<div className="bg-gray-50 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">
<ChevronLeftIcon 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">
<h1 className="font-cal text-xl text-gray-900">{name}</h1>
<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">
{!isLoading ? (
isGlobal || (installedAppCount > 0 && allowedMultipleInstalls) ? (
<div className="space-x-3">
<Button StartIcon={CheckIcon} color="secondary" disabled>
{installedAppCount > 0
? t("active_install", { count: installedAppCount })
: t("globally_install")}
</Button>
<InstallAppButton
type={type}
render={(buttonProps) => (
<Button StartIcon={PlusIcon} data-testid="install-app-button" {...buttonProps}>
{t("add_another")}
</Button>
)}
/>
</div>
) : (
return (
<div className="-mx-4 md:-mx-8">
<div className="bg-gray-50 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.ChevronLeft 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>
{isProOnly && user?.plan === "FREE" ? (
<Badge className="ml-2" variant="default">
PRO
</Badge>
) : null}
</div>
<h2 className="text-sm text-gray-500">
<span className="capitalize">{categories[0]}</span> • {t("published_by", { author })}
</h2>
</header>
</div>
<div className="mt-4 sm:mt-0 sm:text-right">
{!isLoading ? (
isGlobal || (installedAppCount > 0 && allowedMultipleInstalls) ? (
<div className="flex space-x-3">
<Button StartIcon={Icon.Check} color="secondary" disabled>
{installedAppCount > 0
? t("active_install", { count: installedAppCount })
: t("globally_install")}
</Button>
{!isGlobal && (
<InstallAppButton
type={type}
render={(buttonProps) => (
<Button data-testid="install-app-button" {...buttonProps}>
{t("install_app")}
</Button>
)}
isProOnly={isProOnly}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
onClick: () => {
mutation.mutate({ type });
},
loading: mutation.isLoading,
};
}
return (
<Button StartIcon={Icon.Plus} {...props} data-testid="install-app-button">
{t("add_another")}
</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>
{/* reintroduce once we show permissions and features
<NavTabs tabs={tabs} linkProps={{ shallow: true }} /> */}
</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")}
</>
)}
)}
</div>
) : installedAppCount > 0 ? (
<Button color="secondary" disabled title="App already installed">
{t("installed")}
</Button>
) : (
<InstallAppButton
type={type}
isProOnly={isProOnly}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
onClick: () => {
mutation.mutate({ type });
},
loading: mutation.isLoading,
};
}
return (
<Button data-testid="install-app-button" {...props}>
{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>
<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}>
<BookOpenIcon 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}>
<ExternalLinkIcon 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}>
<MailIcon 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}>
<DocumentTextIcon 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}>
<ShieldCheckIcon 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">
<FlagIcon className="inline h-3 w-3" /> Report App
</a>
</div>
)}
</div>
</div>
</Shell>
</>
</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.BookOpen 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.ExternalLink 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.Mail 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.File 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.Shield 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.Flag 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>
);
}
+1 -1
View File
@@ -24,7 +24,7 @@ const tabs = [
export default function BookingsShell({ children }: { children: React.ReactNode }) {
return (
<>
<NavTabs tabs={tabs} linkProps={{ shallow: true }} />
<NavTabs tabs={tabs} />
<main>{children}</main>
</>
);
@@ -3,8 +3,7 @@ import React, { useEffect, useState } from "react";
import Select from "react-select";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@lib/trpc";
import { trpc } from "@calcom/trpc/react";
interface Props {
onChange: (value: { externalId: string; integration: string }) => void;
@@ -61,15 +60,17 @@ const DestinationCalendarSelector = ({
query.data.connectedCalendars.map((selectedCalendar) => ({
key: selectedCalendar.credentialId,
label: `${selectedCalendar.integration.title} (${selectedCalendar.primary?.name})`,
options: (selectedCalendar.calendars ?? []).map((cal) => ({
label: cal.name || "",
value: `${cal.integration}:${cal.externalId}`,
})),
options: (selectedCalendar.calendars ?? [])
.filter((cal) => cal.readOnly === false)
.map((cal) => ({
label: cal.name || "",
value: `${cal.integration}:${cal.externalId}`,
})),
})) ?? [];
return (
<div className="relative" title={`${t("select_destination_calendar")}: ${selectedOption?.label || ""}`}>
<Select
name={"primarySelectedCalendar"}
name="primarySelectedCalendar"
placeholder={!hidePlaceholder ? `${t("select_destination_calendar")}:` : undefined}
options={options}
styles={{
@@ -98,7 +99,7 @@ const DestinationCalendarSelector = ({
}}
isSearchable={false}
className={classNames(
"mt-1 mb-2 block w-full min-w-0 flex-1 rounded-none rounded-r-sm border-gray-300 sm:text-sm",
"mt-1 mb-2 block w-full min-w-0 flex-1 rounded-none rounded-r-sm border-gray-300 text-sm",
!hidePlaceholder && "font-medium"
)}
onChange={(option) => {
+49 -35
View File
@@ -1,4 +1,3 @@
import { ArrowLeftIcon, ChevronRightIcon, CodeIcon, EyeIcon, SunIcon } from "@heroicons/react/solid";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
import classNames from "classnames";
import { useRouter } from "next/router";
@@ -8,12 +7,14 @@ import { components, ControlProps } from "react-select";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { EventType } from "@calcom/prisma/client";
import { trpc } from "@calcom/trpc/react";
import { SVGComponent } from "@calcom/types/SVGComponent";
import { Button, Switch } from "@calcom/ui";
import { Dialog, DialogClose, DialogContent } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields";
import { EMBED_LIB_URL, WEBAPP_URL } from "@lib/config/constants";
import { trpc } from "@lib/trpc";
import NavTabs from "@components/NavTabs";
import ColorPicker from "@components/ui/colorpicker";
@@ -143,7 +144,7 @@ function MyComponent() {
const cal = await getCalApi();
${uiInstructionCode}
})();
}, [])
}, [])
return <Cal calLink="${calLink}" style={{width:"${width}",height:"${height}",overflow:"scroll"}} />;
};`;
},
@@ -164,7 +165,7 @@ function MyComponent() {
Cal("floatingButton", ${floatingButtonArg});
${uiInstructionCode}
})();
}, [])
}, [])
};`;
},
"element-click": ({ calLink, uiInstructionCode }: { calLink: string; uiInstructionCode: string }) => {
@@ -177,7 +178,7 @@ function MyComponent() {
const cal = await getCalApi();
${uiInstructionCode}
})();
}, [])
}, [])
return <button data-cal-link="${calLink}" />;
};`;
},
@@ -452,7 +453,7 @@ const tabs = [
{
name: "HTML",
tabName: "embed-code",
icon: CodeIcon,
icon: Icon.Code,
type: "code",
Component: forwardRef<
HTMLTextAreaElement | HTMLIFrameElement | null,
@@ -467,12 +468,15 @@ const tabs = [
}
return (
<>
<small className="flex py-4 text-neutral-500">{t("place_where_cal_widget_appear")}</small>
<div>
<small className="flex py-4 text-neutral-500">{t("place_where_cal_widget_appear")}</small>
</div>
<TextArea
data-testid="embed-code"
ref={ref as typeof ref & MutableRefObject<HTMLTextAreaElement>}
name="embed-code"
className="h-[36rem]"
className="h-[calc(100%-50px)] font-mono"
style={{ resize: "none", overflow: "auto" }}
readOnly
value={
`<!-- Cal ${embedType} embed code begins -->\n` +
@@ -486,7 +490,8 @@ ${getEmbedSnippetString()}
${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previewState })}
</script>
<!-- Cal ${embedType} embed code ends -->`
}></TextArea>
}
/>
<p className="hidden text-sm text-gray-500">
{t(
"Need help? See our guides for embedding Cal on Wix, Squarespace, or WordPress, check our common questions, or explore advanced embed options."
@@ -499,7 +504,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previ
{
name: "React",
tabName: "embed-react",
icon: CodeIcon,
icon: Icon.Code,
type: "code",
Component: forwardRef<
HTMLTextAreaElement | HTMLIFrameElement | null,
@@ -519,8 +524,9 @@ ${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previ
data-testid="embed-react"
ref={ref as typeof ref & MutableRefObject<HTMLTextAreaElement>}
name="embed-react"
className="h-[36rem]"
className="h-[calc(100%-50px)] font-mono"
readOnly
style={{ resize: "none", overflow: "auto" }}
value={`/* First make sure that you have installed the package */
/* If you are using yarn */
@@ -529,7 +535,8 @@ ${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previ
/* If you are using npm */
// npm install @calcom/embed-react
${getEmbedTypeSpecificString({ embedFramework: "react", embedType, calLink, previewState })}
`}></TextArea>
`}
/>
</>
);
}),
@@ -537,7 +544,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "react", embedType, calLink, prev
{
name: "Preview",
tabName: "embed-preview",
icon: EyeIcon,
icon: Icon.Eye,
type: "iframe",
Component: forwardRef<
HTMLIFrameElement | HTMLTextAreaElement | null,
@@ -573,7 +580,7 @@ Cal("init", {origin:"${WEBAPP_URL}"});
const ThemeSelectControl = ({ children, ...props }: ControlProps<{ value: Theme; label: string }, false>) => {
return (
<components.Control {...props}>
<SunIcon className="h-[32px] w-[32px] text-gray-500" />
<Icon.Sun className="ml-2 h-4 w-4 text-gray-500" />
{children}
</components.Control>
);
@@ -592,10 +599,10 @@ const ChooseEmbedTypesDialogContent = () => {
<p className="text-sm text-gray-500">{t("choose_ways_put_cal_site")}</p>
</div>
</div>
<div className="flex">
<div className="flex items-start">
{embeds.map((embed, index) => (
<button
className="mr-2 w-1/3 p-3 text-left hover:rounded-md hover:border hover:bg-neutral-100"
className="mr-2 w-1/3 border border-transparent p-3 text-left hover:rounded-md hover:border-gray-200 hover:bg-neutral-100"
key={index}
data-testid={embed.type}
onClick={() => {
@@ -800,11 +807,11 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
},
});
}}>
<ArrowLeftIcon className="mr-4 w-4"></ArrowLeftIcon>
<Icon.ArrowLeft className="mr-4 w-4" />
</button>
{embed.title}
</h3>
<hr className={classNames("mt-4", embedType === "element-click" ? "hidden" : "")}></hr>
<hr className={classNames("mt-4", embedType === "element-click" ? "hidden" : "")} />
<div className={classNames("mt-4 font-medium", embedType === "element-click" ? "hidden" : "")}>
<Collapsible
open={isEmbedCustomizationOpen}
@@ -819,7 +826,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
? "Floating Popup Customization"
: "Element Click Customization"}
</div>
<ChevronRightIcon
<Icon.ChevronRight
className={`${
isEmbedCustomizationOpen ? "rotate-90 transform" : ""
} ml-auto h-5 w-5 text-neutral-500`}
@@ -850,7 +857,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
}}
addOnLeading={<InputLeading>W</InputLeading>}
/>
<span className="p-2">x</span>
<span className="p-2">×</span>
<TextField
labelProps={{ className: "hidden" }}
name="height"
@@ -916,7 +923,8 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
},
};
});
}}></Switch>
}}
/>
</div>
<div
className={classNames(
@@ -937,7 +945,8 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
});
}}
defaultValue={FloatingPopupPositionOptions[0]}
options={FloatingPopupPositionOptions}></Select>
options={FloatingPopupPositionOptions}
/>
</div>
<div
className={classNames(
@@ -958,7 +967,8 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
},
};
});
}}></ColorPicker>
}}
/>
</div>
</div>
<div
@@ -980,20 +990,21 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
},
};
});
}}></ColorPicker>
}}
/>
</div>
</div>
</CollapsibleContent>
</Collapsible>
</div>
<hr className="mt-4"></hr>
<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>
<ChevronRightIcon
<Icon.ChevronRight
className={`${
isBookingCustomizationOpen ? "rotate-90 transform" : ""
} ml-auto h-5 w-5 text-neutral-500`}
@@ -1020,7 +1031,8 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
};
});
}}
options={ThemeOptions}></Select>
options={ThemeOptions}
/>
</Label>
{[
{ name: "brandColor", title: "Brand Color" },
@@ -1063,7 +1075,8 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
embedType={embedType}
calLink={calLink}
previewState={previewState}
ref={refOfEmbedCodesRefs.current[tab.name]}></tab.Component>
ref={refOfEmbedCodesRefs.current[tab.name]}
/>
) : (
<tab.Component
embedType={embedType}
@@ -1073,7 +1086,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
/>
)}
</div>
<div className={router.query.tabName == "embed-preview" ? "block" : "hidden"}></div>
<div className={router.query.tabName == "embed-preview" ? "block" : "hidden"} />
</div>
<div className="mt-8 flex flex-row-reverse gap-x-2">
{tab.type === "code" ? (
@@ -1122,13 +1135,15 @@ export const EmbedDialog = () => {
export const EmbedButton = ({
eventTypeId,
StartIcon,
children,
className = "",
dark,
...props
}: {
eventTypeId: EventType["id"];
StartIcon?: SVGComponent;
children?: React.ReactNode;
className: string;
dark?: boolean;
}) => {
const { t } = useLocale();
const router = useRouter();
@@ -1153,15 +1168,14 @@ export const EmbedButton = ({
<Button
type="button"
color="minimal"
StartIcon={StartIcon}
size="sm"
className={className}
{...props}
data-test-eventtype-id={eventTypeId}
data-testid={"event-type-embed"}
data-testid="event-type-embed"
onClick={() => openEmbedModal()}>
<CodeIcon
className={classNames("h-4 w-4 ltr:mr-2 rtl:ml-2", dark ? "" : "text-neutral-500")}></CodeIcon>
{t("Embed")}
{children}
</Button>
);
};
+6 -1
View File
@@ -1,11 +1,16 @@
import { useTranslation } from "next-i18next";
import { useEffect } from "react";
import { trpc } from "@lib/trpc";
import { trpc } from "@calcom/trpc/react";
export function useViewerI18n() {
return trpc.useQuery(["viewer.public.i18n"], {
staleTime: Infinity,
/**
* i18n should never be clubbed with other queries, so that it's caching can be managed independently.
* We intend to not cache i18n query
**/
context: { skipBatch: true },
});
}
+1 -1
View File
@@ -142,7 +142,7 @@ export default function ImageUploader({
</div>
)}
{result && <CropContainer imageSrc={result as string} onCropComplete={setCroppedAreaPixels} />}
<label className="mt-8 rounded-sm border border-gray-300 bg-white px-3 py-1 text-xs font-medium leading-4 text-gray-700 hover:bg-gray-50 hover:text-gray-900 hover:shadow-sm focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-1 dark:border-gray-800 dark:bg-transparent dark:text-white dark:hover:bg-gray-900">
<label className="mt-8 rounded-sm border border-gray-300 bg-white px-3 py-1 text-xs font-medium leading-4 text-gray-700 hover:bg-gray-50 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-1 dark:border-gray-800 dark:bg-transparent dark:text-white dark:hover:bg-gray-900">
<input
onInput={onInputFile}
type="file"
+5 -3
View File
@@ -18,6 +18,7 @@ export interface NavTabProps {
tabName?: string;
icon?: SVGComponent;
adminRequired?: boolean;
className?: string;
}[];
linkProps?: Omit<LinkProps, "href">;
}
@@ -58,7 +59,7 @@ const NavTabs: FC<NavTabProps> = ({ tabs, linkProps, ...props }) => {
: noop;
const Component = tab.adminRequired ? AdminRequired : Fragment;
const className = tab.className || "";
return (
<Component key={tab.name}>
<Link key={tab.name} href={href} {...linkProps}>
@@ -68,14 +69,15 @@ const NavTabs: FC<NavTabProps> = ({ tabs, linkProps, ...props }) => {
isCurrent
? "border-neutral-900 text-neutral-900"
: "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700",
"group inline-flex items-center border-b-2 py-4 px-1 text-sm font-medium"
"group inline-flex items-center border-b-2 py-4 px-1 text-sm font-medium",
className
)}
aria-current={isCurrent ? "page" : undefined}>
{tab.icon && (
<tab.icon
className={classNames(
isCurrent ? "text-neutral-900" : "text-gray-400 group-hover:text-gray-500",
"-ml-0.5 hidden h-5 w-5 ltr:mr-2 rtl:ml-2 sm:inline-block"
"-ml-0.5 hidden h-4 w-4 ltr:mr-2 rtl:ml-2 sm:inline-block"
)}
aria-hidden="true"
/>
+9 -16
View File
@@ -1,48 +1,41 @@
import {
CreditCardIcon,
KeyIcon,
LockClosedIcon,
UserGroupIcon,
UserIcon,
CodeIcon,
} from "@heroicons/react/solid";
import React, { ComponentProps } from "react";
import ErrorBoundary from "@lib/ErrorBoundary";
import ErrorBoundary from "@calcom/ui/ErrorBoundary";
import { Icon } from "@calcom/ui/Icon";
import Shell from "@calcom/ui/Shell";
import NavTabs from "./NavTabs";
import Shell from "./Shell";
const tabs = [
{
name: "profile",
href: "/settings/profile",
icon: UserIcon,
icon: Icon.User,
},
{
name: "teams",
href: "/settings/teams",
icon: UserGroupIcon,
icon: Icon.Users,
},
{
name: "security",
href: "/settings/security",
icon: KeyIcon,
icon: Icon.Key,
},
{
name: "developer",
href: "/settings/developer",
icon: CodeIcon,
icon: Icon.Terminal,
},
{
name: "billing",
href: "/settings/billing",
icon: CreditCardIcon,
icon: Icon.CreditCard,
},
{
name: "admin",
href: "/settings/admin",
icon: LockClosedIcon,
icon: Icon.Lock,
adminRequired: true,
},
];
+4 -5
View File
@@ -1,10 +1,9 @@
import { InformationCircleIcon } from "@heroicons/react/outline";
import { Trans } from "next-i18next";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import Button from "@calcom/ui/Button";
import { Dialog, DialogClose, DialogContent } from "@calcom/ui/Dialog";
import { useLocale } from "@lib/hooks/useLocale";
import { Icon } from "@calcom/ui/Icon";
export function UpgradeToProDialog({
modalOpen,
@@ -20,7 +19,7 @@ export function UpgradeToProDialog({
<Dialog open={modalOpen}>
<DialogContent>
<div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-yellow-100">
<InformationCircleIcon className="h-6 w-6 text-yellow-400" aria-hidden="true" />
<Icon.AlertTriangle className="h-6 w-6 text-yellow-400" aria-hidden="true" />
</div>
<div className="mb-4 sm:flex sm:items-start">
<div className="mt-3 sm:mt-0 sm:text-left">
@@ -43,7 +42,7 @@ export function UpgradeToProDialog({
</div>
<div className="mt-5 gap-x-2 sm:mt-4 sm:flex sm:flex-row-reverse">
<DialogClose asChild>
<Button className="btn-wide table-cell text-center" onClick={() => setModalOpen(false)}>
<Button className="table-cell w-full text-center" onClick={() => setModalOpen(false)}>
{t("dismiss")}
</Button>
</DialogClose>
+1
View File
@@ -19,6 +19,7 @@ export default function AllApps({ apps }: { apps: App[] }) {
logo={app.logo}
rating={app.rating}
reviews={app.reviews}
isProOnly={app.isProOnly}
/>
))}
</div>
+12 -1
View File
@@ -1,5 +1,7 @@
import Link from "next/link";
import { trpc } from "@calcom/trpc/react";
import Badge from "@calcom/ui/Badge";
import Button from "@calcom/ui/Button";
interface AppCardProps {
@@ -10,9 +12,11 @@ interface AppCardProps {
description: string;
rating: number;
reviews?: number;
isProOnly?: boolean;
}
export default function AppCard(props: AppCardProps) {
const { data: user } = trpc.useQuery(["viewer.me"]);
return (
<Link href={"/apps/" + props.slug}>
<a
@@ -32,7 +36,14 @@ export default function AppCard(props: AppCardProps) {
Add
</Button>
</div>
<h3 className="font-medium">{props.name}</h3>
<div className="flex items-center">
<h3 className="font-medium">{props.name}</h3>
{props.isProOnly && user?.plan === "FREE" ? (
<Badge className="ml-2" variant="default">
PRO
</Badge>
) : null}
</div>
{/* TODO: add reviews <div className="flex text-sm text-gray-800">
<span>{props.rating} stars</span> <StarIcon className="ml-1 mt-0.5 h-4 w-4 text-yellow-600" />
<span className="pl-1 text-gray-500">{props.reviews} reviews</span>
+4 -3
View File
@@ -1,9 +1,10 @@
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 { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/solid";
import { useEffect, useRef } from "react";
import { Icon } from "@calcom/ui/Icon";
const Slider = <T extends string | unknown>({
title = "",
className = "",
@@ -51,10 +52,10 @@ const Slider = <T extends string | unknown>({
)}
<div className="glide__arrows ml-auto" data-glide-el="controls">
<button data-glide-dir="<" className="mr-4">
<ArrowLeftIcon className="h-5 w-5 text-gray-600 hover:text-black" />
<Icon.ArrowLeft className="h-5 w-5 text-gray-600 hover:text-black" />
</button>
<button data-glide-dir=">">
<ArrowRightIcon className="h-5 w-5 text-gray-600 hover:text-black" />
<Icon.ArrowRight className="h-5 w-5 text-gray-600 hover:text-black" />
</button>
</div>
</div>
@@ -30,6 +30,7 @@ const TrendingAppsSlider = <T extends App>({ items }: { items: T[] }) => {
logo={app.logo}
rating={app.rating}
reviews={app.reviews}
isProOnly={app.isProOnly}
/>
)}
/>
+3 -3
View File
@@ -2,11 +2,11 @@ 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";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
import { trpc } from "@lib/trpc";
interface Props {
email: string;
@@ -34,7 +34,7 @@ export default function SAMLLogin(props: Props) {
<div className="mt-5">
<Button
color="secondary"
data-testid={"saml"}
data-testid="saml"
className="flex w-full justify-center"
onClick={async (event) => {
event.preventDefault();
@@ -1,15 +1,15 @@
import { PlusIcon } from "@heroicons/react/solid";
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";
import { trpc } from "@lib/trpc";
export function NewScheduleButton({ name = "new-schedule" }: { name?: string }) {
const router = useRouter();
@@ -41,7 +41,7 @@ export function NewScheduleButton({ name = "new-schedule" }: { name?: string })
return (
<Dialog name={name} clearQueryParamsOnClose={["copy-schedule-id"]}>
<DialogTrigger asChild>
<Button data-testid={name} StartIcon={PlusIcon}>
<Button data-testid={name} StartIcon={Icon.Plus}>
{t("new_schedule_btn")}
</Button>
</DialogTrigger>
@@ -65,7 +65,7 @@ export function NewScheduleButton({ name = "new-schedule" }: { name?: string })
type="text"
id="name"
required
className="block w-full rounded-sm border-gray-300 text-sm shadow-sm"
className="block w-full rounded-sm border-gray-300 text-sm"
placeholder={t("default_schedule_name")}
{...register("name")}
/>
+21 -24
View File
@@ -1,16 +1,14 @@
import { PlusIcon, TrashIcon } from "@heroicons/react/outline";
import { DuplicateIcon } from "@heroicons/react/solid";
import classNames from "classnames";
import dayjs, { Dayjs, ConfigType } from "dayjs";
import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";
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, { DropdownMenuTrigger, DropdownMenuContent } from "@calcom/ui/Dropdown";
import Dropdown, { DropdownMenuContent } 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";
@@ -19,9 +17,6 @@ import { TimeRange } from "@lib/types/schedule";
import Select from "@components/ui/form/Select";
dayjs.extend(utc);
dayjs.extend(timezone);
/** Begin Time Increments For Select */
const increment = 15;
@@ -207,7 +202,7 @@ export const DayRanges = ({
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,
});
@@ -235,36 +230,38 @@ export const DayRanges = ({
<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 sm:flex-grow-0">
<div className="flex flex-grow space-x-1 sm:flex-grow-0">
<TimeRangeField name={`${name}.${index}`} />
<Button
type="button"
size="icon"
color="minimal"
StartIcon={TrashIcon}
type="button"
StartIcon={Icon.Trash}
onClick={() => remove(index)}
/>
</div>
{index === 0 && (
<div className="absolute top-2 right-0 text-right sm:relative sm:top-0 sm:flex-grow">
<Button
className="text-neutral-400"
type="button"
color="minimal"
size="icon"
StartIcon={PlusIcon}
onClick={handleAppend}
/>
<Tooltip content={t("add_time_availability") as string}>
<Button
className="text-neutral-400"
type="button"
color="minimal"
size="icon"
StartIcon={Icon.Plus}
onClick={handleAppend}
/>
</Tooltip>
<Dropdown>
<DropdownMenuTrigger asChild>
<Tooltip content={t("duplicate") as string}>
<Button
type="button"
color="minimal"
size="icon"
StartIcon={DuplicateIcon}
StartIcon={Icon.Copy}
onClick={handleAppend}
/>
</DropdownMenuTrigger>
</Tooltip>
<DropdownMenuContent>
<CopyTimes
disabled={[parseInt(name.substring(name.lastIndexOf(".") + 1), 10)]}
@@ -1,14 +1,13 @@
import { DotsHorizontalIcon, TrashIcon } from "@heroicons/react/solid";
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 { inferQueryOutput } from "@lib/trpc";
import { Icon } from "@calcom/ui/Icon";
export function ScheduleListItem({
schedule,
@@ -48,7 +47,7 @@ export function ScheduleListItem({
</div>
<Dropdown>
<DropdownMenuTrigger className="group mr-5 h-10 w-10 border border-transparent p-0 text-neutral-500 hover:border-gray-200">
<DotsHorizontalIcon className="h-5 w-5 group-hover:text-gray-800" />
<Icon.MoreHorizontal className="h-5 w-5 group-hover:text-gray-800" />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
@@ -62,7 +61,7 @@ export function ScheduleListItem({
type="button"
color="warn"
className="w-full font-normal"
StartIcon={isDeleting ? undefined : TrashIcon}
StartIcon={isDeleting ? undefined : Icon.Trash}
loading={isDeleting}>
{isDeleting ? t("deleting") : t("delete_schedule")}
</Button>
@@ -19,13 +19,13 @@ 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>
<SkeletonText width="32" height="2"></SkeletonText>
<SkeletonText width="32" height="4" />
<SkeletonText width="32" height="2" />
</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>
<SkeletonText width="12" height="6" />
</div>
</div>
</li>
@@ -37,8 +37,8 @@ export const AvailabilitySelectSkeletonLoader = () => {
<li className="group flex w-full items-center justify-between rounded-sm border border-gray-200 px-[10px] py-3">
<div className="flex-grow truncate text-sm">
<div className="flex justify-between">
<SkeletonText width="32" height="4"></SkeletonText>
<SkeletonText width="4" height="4"></SkeletonText>
<SkeletonText width="32" height="4" />
<SkeletonText width="4" height="4" />
</div>
</div>
</li>
@@ -1,18 +1,17 @@
import { SchedulingType } from "@prisma/client";
import dayjs, { Dayjs } from "dayjs";
import Link from "next/link";
import { useRouter } from "next/router";
import { FC, useEffect, useState } from "react";
import dayjs, { Dayjs } from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { nameOfDay } from "@calcom/lib/weekday";
import type { Slot } from "@calcom/trpc/server/routers/viewer/slots";
import { SkeletonContainer, SkeletonText } from "@calcom/ui";
import classNames from "@lib/classNames";
import { timeZone } from "@lib/clock";
import type { Slot } from "@server/routers/viewer/slots";
type AvailableTimesProps = {
timeFormat: string;
eventTypeId: number;
@@ -102,10 +101,10 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
brand === "#fff" || brand === "#ffffff" ? "border-brandcontrast" : "border-brand"
)}>
{dayjs(slot.time).tz(timeZone()).format(timeFormat)}
{!!seatsPerTimeSlot && <p className={`text-sm`}>{t("booking_full")}</p>}
{!!seatsPerTimeSlot && <p className="text-sm">{t("booking_full")}</p>}
</div>
) : (
<Link href={bookingUrl}>
<Link href={bookingUrl} prefetch={false}>
<a
className={classNames(
"text-primary-500 hover:bg-brand hover:text-brandcontrast dark:hover:bg-darkmodebrand dark:hover:text-darkmodebrandcontrast mb-2 block rounded-sm border bg-white py-4 font-medium hover:text-white dark:border-transparent dark:bg-gray-600 dark:text-neutral-200 dark:hover:border-black",
+27 -34
View File
@@ -1,26 +1,17 @@
import {
BanIcon,
CheckIcon,
ClockIcon,
LocationMarkerIcon,
PaperAirplaneIcon,
PencilAltIcon,
XIcon,
} from "@heroicons/react/outline";
import { RefreshIcon } from "@heroicons/react/solid";
import { BookingStatus } from "@prisma/client";
import dayjs from "dayjs";
import { useRouter } from "next/router";
import { useState } from "react";
import { useMutation } from "react-query";
import { parseRecurringEvent } from "@calcom/lib";
import dayjs from "@calcom/dayjs";
import classNames from "@calcom/lib/classNames";
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 Button from "@calcom/ui/Button";
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
import { TextArea } from "@calcom/ui/form/fields";
@@ -28,8 +19,7 @@ import { HttpError } from "@lib/core/http/error";
import useMeQuery from "@lib/hooks/useMeQuery";
import { linkValueToString } from "@lib/linkValueToString";
import { LocationType } from "@lib/location";
import { parseRecurringDates } from "@lib/parseDate";
import { inferQueryInput, inferQueryOutput, trpc } from "@lib/trpc";
import { extractRecurringDates } from "@lib/parseDate";
import { EditLocationDialog } from "@components/dialog/EditLocationDialog";
import { RescheduleDialog } from "@components/dialog/RescheduleDialog";
@@ -41,7 +31,7 @@ type BookingItem = inferQueryOutput<"viewer.bookings">["bookings"][number];
type BookingItemProps = BookingItem & {
listingStatus: BookingListingStatus;
recurringCount?: number;
recurringBookings?: BookingItem[];
};
function BookingListItem(booking: BookingItemProps) {
@@ -102,7 +92,7 @@ function BookingListItem(booking: BookingItemProps) {
onClick: () => {
setRejectionDialogIsOpen(true);
},
icon: BanIcon,
icon: Icon.Slash,
disabled: mutation.isLoading,
},
{
@@ -114,7 +104,7 @@ function BookingListItem(booking: BookingItemProps) {
onClick: () => {
mutation.mutate(true);
},
icon: CheckIcon,
icon: Icon.Check,
disabled: mutation.isLoading,
color: "primary",
},
@@ -127,23 +117,29 @@ function BookingListItem(booking: BookingItemProps) {
booking.listingStatus === "recurring" && booking.recurringEventId !== null
? t("cancel_all_remaining")
: t("cancel"),
href: `/cancel/${booking.uid}`,
icon: XIcon,
/* When cancelling we need to let the UI and the API know if the intention is to
cancel all remaining bookings or just that booking instance. */
href: `/cancel/${booking.uid}${
booking.listingStatus === "recurring" && booking.recurringEventId !== null
? "?allRemainingBookings=true"
: ""
}`,
icon: Icon.X,
},
{
id: "edit_booking",
label: t("edit_booking"),
icon: PencilAltIcon,
icon: Icon.Edit,
actions: [
{
id: "reschedule",
icon: ClockIcon,
icon: Icon.Clock,
label: t("reschedule_booking"),
href: `/reschedule/${booking.uid}`,
},
{
id: "reschedule_request",
icon: PaperAirplaneIcon,
icon: Icon.Send,
iconClassName: "rotate-45 w-[18px] -ml-[2px]",
label: t("send_reschedule_request"),
onClick: () => {
@@ -156,7 +152,7 @@ function BookingListItem(booking: BookingItemProps) {
onClick: () => {
setIsOpenLocationDialog(true);
},
icon: LocationMarkerIcon,
icon: Icon.MapPin,
},
],
},
@@ -169,7 +165,7 @@ function BookingListItem(booking: BookingItemProps) {
const RequestSentMessage = () => {
return (
<div className="ml-1 mr-8 flex text-gray-500" data-testid="request_reschedule_sent">
<PaperAirplaneIcon className="-mt-[1px] w-4 rotate-45" />
<Icon.Send className="-mt-[1px] w-4 rotate-45" />
<p className="ml-2 ">{t("reschedule_request_sent")}</p>
</div>
);
@@ -202,13 +198,10 @@ function BookingListItem(booking: BookingItemProps) {
let recurringStrings: string[] = [];
let recurringDates: Date[] = [];
const today = new Date();
if (booking.recurringCount && booking.eventType.recurringEvent?.freq !== undefined) {
[recurringStrings, recurringDates] = parseRecurringDates(
{
startDate: booking.startTime,
recurringEvent: parseRecurringEvent(booking.eventType.recurringEvent),
recurringCount: booking.recurringCount,
},
if (booking.recurringBookings && booking.eventType.recurringEvent?.freq !== undefined) {
[recurringStrings, recurringDates] = extractRecurringDates(
booking.recurringBookings,
user?.timeZone,
i18n
);
if (booking.status === BookingStatus.PENDING) {
@@ -307,7 +300,7 @@ function BookingListItem(booking: BookingItemProps) {
{dayjs(booking.endTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")}
</div>
<div className="text-sm text-gray-400">
{booking.recurringCount &&
{booking.recurringBookings &&
booking.eventType?.recurringEvent?.freq &&
(booking.listingStatus === "recurring" || booking.listingStatus === "cancelled") && (
<div className="underline decoration-gray-400 decoration-dashed underline-offset-2">
@@ -317,7 +310,7 @@ function BookingListItem(booking: BookingItemProps) {
<p key={key}>{aDate}</p>
))}>
<div className="text-gray-600 dark:text-white">
<RefreshIcon className="float-left mr-1 mt-[2px] inline-block h-4 w-4 text-gray-400" />
<Icon.RefreshCcw className="float-left mr-1 mt-[2px] inline-block h-4 w-4 text-gray-400" />
<p className="pl-[21px]">
{booking.status === BookingStatus.ACCEPTED
? `${t("event_remaining", {
@@ -326,7 +319,7 @@ function BookingListItem(booking: BookingItemProps) {
: getEveryFreqFor({
t,
recurringEvent: booking.eventType.recurringEvent,
recurringCount: booking.recurringCount,
recurringCount: booking.recurringBookings.length,
})}
</p>
</div>
+80 -85
View File
@@ -1,13 +1,12 @@
import { XIcon } from "@heroicons/react/solid";
import { useRouter } from "next/router";
import { useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { RecurringEvent } from "@calcom/types/Calendar";
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 useTheme from "@lib/hooks/useTheme";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
import { Icon } from "@calcom/ui/Icon";
type Props = {
booking: {
@@ -32,90 +31,86 @@ export default function CancelBooking(props: Props) {
const [loading, setLoading] = useState(false);
const telemetry = useTelemetry();
const [error, setError] = useState<string | null>(booking ? null : t("booking_already_cancelled"));
const { isReady, Theme } = useTheme(props.theme);
useTheme(props.theme);
if (isReady) {
return (
<>
<Theme />
{error && (
<div>
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
<XIcon className="h-6 w-6 text-red-600" />
</div>
<div className="mt-3 text-center sm:mt-5">
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
{error}
</h3>
</div>
return (
<>
{error && (
<div>
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
<Icon.X className="h-6 w-6 text-red-600" />
</div>
)}
{!error && (
<div className="mt-5 sm:mt-6">
<label className="text-bookingdark font-medium dark:text-white">{t("cancellation_reason")}</label>
<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 "
rows={3}
/>
<div className="flex rtl:space-x-reverse">
{!props.recurringEvent && (
<div className="w-full">
<Button color="secondary" onClick={() => router.push("/reschedule/" + booking?.uid)}>
{t("reschedule_this")}
</Button>
</div>
)}
<div className="w-full space-x-2 text-right">
<Button color="secondary" onClick={() => props.setIsCancellationMode(false)}>
{t("nevermind")}
</Button>
<Button
data-testid="cancel"
onClick={async () => {
setLoading(true);
const payload = {
uid: booking?.uid,
reason: cancellationReason,
};
telemetry.event(telemetryEventTypes.bookingCancelled, collectPageParameters());
const res = await fetch("/api/cancel", {
body: JSON.stringify(payload),
headers: {
"Content-Type": "application/json",
},
method: "DELETE",
});
if (res.status >= 200 && res.status < 300) {
await router.push(
`/cancel/success?name=${props.profile.name}&title=${booking?.title}&eventPage=${
profile.slug
}&team=${team ? 1 : 0}&recurring=${!!props.recurringEvent}`
);
} else {
setLoading(false);
setError(
`${t("error_with_status_code_occured", { status: res.status })} ${t(
"please_try_again"
)}`
);
}
}}
loading={loading}>
{t("cancel_event")}
<div className="mt-3 text-center sm:mt-5">
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
{error}
</h3>
</div>
</div>
)}
{!error && (
<div className="mt-5 sm:mt-6">
<label className="text-bookingdark font-medium dark:text-white">{t("cancellation_reason")}</label>
<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 "
rows={3}
/>
<div className="flex rtl:space-x-reverse">
{!props.recurringEvent && (
<div className="w-full">
<Button color="secondary" onClick={() => router.push("/reschedule/" + booking?.uid)}>
{t("reschedule_this")}
</Button>
</div>
)}
<div className="w-full space-x-2 text-right">
<Button color="secondary" onClick={() => props.setIsCancellationMode(false)}>
{t("nevermind")}
</Button>
<Button
data-testid="cancel"
onClick={async () => {
setLoading(true);
const payload = {
uid: booking?.uid,
reason: cancellationReason,
};
telemetry.event(telemetryEventTypes.bookingCancelled, collectPageParameters());
const res = await fetch("/api/cancel", {
body: JSON.stringify(payload),
headers: {
"Content-Type": "application/json",
},
method: "DELETE",
});
if (res.status >= 200 && res.status < 300) {
await router.push(
`/cancel/success?name=${props.profile.name}&title=${booking?.title}&eventPage=${
profile.slug
}&team=${team ? 1 : 0}&recurring=${!!props.recurringEvent}`
);
} else {
setLoading(false);
setError(
`${t("error_with_status_code_occured", { status: res.status })} ${t(
"please_try_again"
)}`
);
}
}}
loading={loading}>
{t("cancel_event")}
</Button>
</div>
</div>
)}
</>
);
}
return <></>;
</div>
)}
</>
);
}
-269
View File
@@ -1,269 +0,0 @@
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/solid";
import { PeriodType } from "@prisma/client";
import dayjs, { Dayjs } from "dayjs";
import dayjsBusinessTime from "dayjs-business-days2";
import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";
import { memoize } from "lodash";
import { useEffect, useRef, useState } from "react";
import { useEmbedStyles } from "@calcom/embed-core/embed-iframe";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import classNames from "@lib/classNames";
import { timeZone } from "@lib/clock";
import { weekdayNames } from "@lib/core/i18n/weekday";
import { doWorkAsync } from "@lib/doWorkAsync";
import isOutOfBounds from "@lib/isOutOfBounds";
import getSlots from "@lib/slots";
import { WorkingHours } from "@lib/types/schedule";
import Loader from "@components/Loader";
dayjs.extend(dayjsBusinessTime);
dayjs.extend(utc);
dayjs.extend(timezone);
type DatePickerProps = {
weekStart: string;
onDatePicked: (pickedDate: Dayjs) => void;
workingHours: WorkingHours[];
eventLength: number;
date: Dayjs | null;
periodType: PeriodType;
periodStartDate: Date | null;
periodEndDate: Date | null;
periodDays: number | null;
periodCountCalendarDays: boolean | null;
minimumBookingNotice: number;
};
function DatePicker({
weekStart,
onDatePicked,
workingHours,
eventLength,
date,
periodType = PeriodType.UNLIMITED,
periodStartDate,
periodEndDate,
periodDays,
periodCountCalendarDays,
minimumBookingNotice,
}: DatePickerProps): JSX.Element {
const { i18n } = useLocale();
const [browsingDate, setBrowsingDate] = useState<Dayjs | null>(date);
const enabledDateButtonEmbedStyles = useEmbedStyles("enabledDateButton");
const disabledDateButtonEmbedStyles = useEmbedStyles("disabledDateButton");
const [month, setMonth] = useState<string>("");
const [year, setYear] = useState<string>("");
const [isFirstMonth, setIsFirstMonth] = useState<boolean>(false);
const [daysFromState, setDays] = useState<
| {
disabled: boolean;
date: number;
}[]
| null
>(null);
useEffect(() => {
if (!browsingDate || (date && browsingDate.utcOffset() !== date?.utcOffset())) {
setBrowsingDate(date || dayjs().tz(timeZone()));
}
}, [date, browsingDate]);
useEffect(() => {
if (browsingDate) {
setMonth(browsingDate.toDate().toLocaleString(i18n.language, { month: "long" }));
setYear(browsingDate.format("YYYY"));
setIsFirstMonth(browsingDate.startOf("month").isBefore(dayjs()));
setDays(null);
}
}, [browsingDate, i18n.language]);
const isDisabled = (
day: number,
{
browsingDate,
periodType,
periodStartDate,
periodEndDate,
periodCountCalendarDays,
periodDays,
eventLength,
minimumBookingNotice,
workingHours,
}: Omit<DatePickerProps, "weekStart" | "onDatePicked" | "date"> & {
browsingDate: Dayjs;
}
) => {
const date = browsingDate.startOf("day").date(day);
return (
isOutOfBounds(date, {
periodType,
periodStartDate,
periodEndDate,
periodCountCalendarDays,
periodDays,
}) ||
!getSlots({
inviteeDate: date,
frequency: eventLength,
minimumBookingNotice,
workingHours,
eventLength,
}).length
);
};
const isDisabledRef = useRef(
memoize(isDisabled, (day, { browsingDate }) => {
// Make a composite cache key
return day + "_" + browsingDate.toString();
})
);
const days = (() => {
if (!browsingDate) {
return [];
}
if (daysFromState) {
return daysFromState;
}
// Create placeholder elements for empty days in first week
let weekdayOfFirst = browsingDate.date(1).day();
if (weekStart === "Monday") {
weekdayOfFirst -= 1;
if (weekdayOfFirst < 0) weekdayOfFirst = 6;
}
const days = Array(weekdayOfFirst).fill(null);
const isDisabledMemoized = isDisabledRef.current;
const daysInMonth = browsingDate.daysInMonth();
const daysInitialOffset = days.length;
// Build UI with All dates disabled
for (let i = 1; i <= daysInMonth; i++) {
days.push({
disabled: true,
date: i,
});
}
// Update dates with their availability
doWorkAsync({
batch: 1,
name: "DatePicker",
length: daysInMonth,
callback: (i: number) => {
const day = i + 1;
days[daysInitialOffset + i] = {
disabled: isDisabledMemoized(day, {
browsingDate,
periodType,
periodStartDate,
periodEndDate,
periodCountCalendarDays,
periodDays,
eventLength,
minimumBookingNotice,
workingHours,
}),
date: day,
};
},
batchDone: () => {
setDays([...days]);
},
});
return days;
// eslint-disable-next-line react-hooks/exhaustive-deps
})();
if (!browsingDate) {
return <Loader />;
}
// Handle month changes
const incrementMonth = () => {
setBrowsingDate(browsingDate?.add(1, "month"));
};
const decrementMonth = () => {
setBrowsingDate(browsingDate?.subtract(1, "month"));
};
return (
<div
className={
"mt-8 sm:mt-0 sm:min-w-[455px] " +
(date
? "w-full sm:w-1/2 sm:border-r sm:pl-4 sm:pr-6 sm:dark:border-gray-700 md:w-1/3 "
: "w-full sm:pl-4")
}>
<div className="mb-4 flex text-xl font-light">
<span className="w-1/2 dark:text-white">
<strong className="text-bookingdarker dark:text-white">{month}</strong>{" "}
<span className="text-bookinglight">{year}</span>
</span>
<div className="w-1/2 text-right dark:text-gray-400">
<button
onClick={decrementMonth}
className={classNames(
"group p-1 ltr:mr-2 rtl:ml-2",
isFirstMonth && "text-bookinglighter dark:text-gray-600"
)}
disabled={isFirstMonth}
data-testid="decrementMonth">
<ChevronLeftIcon className="h-5 w-5 group-hover:text-black dark:group-hover:text-white" />
</button>
<button className="group p-1" onClick={incrementMonth} data-testid="incrementMonth">
<ChevronRightIcon className="h-5 w-5 group-hover:text-black dark:group-hover:text-white" />
</button>
</div>
</div>
<div className="border-bookinglightest grid grid-cols-7 gap-4 border-t border-b text-center dark:border-gray-800 sm:border-0">
{weekdayNames(i18n.language, weekStart === "Sunday" ? 0 : 1, "short").map((weekDay) => (
<div key={weekDay} className="text-bookinglight my-4 text-xs uppercase tracking-widest">
{weekDay}
</div>
))}
</div>
<div className="grid grid-cols-7 gap-2 text-center">
{days.map((day, idx) => (
<div
key={day === null ? `e-${idx}` : `day-${day.date}`}
style={{
paddingTop: "100%",
}}
className="relative w-full">
{day === null ? (
<div key={`e-${idx}`} />
) : (
<button
onClick={() => onDatePicked(browsingDate.date(day.date))}
disabled={day.disabled}
style={
day.disabled ? { ...disabledDateButtonEmbedStyles } : { ...enabledDateButtonEmbedStyles }
}
className={classNames(
"hover:border-brand disabled:text-bookinglighter absolute top-0 left-0 right-0 bottom-0 mx-auto w-full rounded-sm text-center font-medium hover:border disabled:cursor-default disabled:font-light disabled:hover:border-0 dark:hover:border-white",
date && date.isSame(browsingDate.date(day.date), "day")
? "bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast disabled:bg-gray-100 disabled:dark:bg-gray-600 disabled:dark:text-white"
: ""
)}
data-testid="day"
data-disabled={day.disabled}>
{day.date}
</button>
)}
</div>
))}
</div>
</div>
);
}
export default DatePicker;
+6 -7
View File
@@ -1,21 +1,20 @@
import { FC, useEffect, useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import Switch from "@calcom/ui/Switch";
import { useLocale } from "@lib/hooks/useLocale";
import TimezoneSelect, { ITimezoneOption } from "@components/ui/form/TimezoneSelect";
import TimezoneSelect, { ITimezoneOption } from "@calcom/ui/form/TimezoneSelect";
import { is24h, timeZone } from "../../lib/clock";
type Props = {
onSelectTimeZone: (selectedTimeZone: string) => void;
onToggle24hClock: (is24hClock: boolean) => void;
timeFormat: string;
};
const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone }) => {
const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone, timeFormat }) => {
const [selectedTimeZone, setSelectedTimeZone] = useState("");
const [is24hClock, setIs24hClock] = useState(false);
const [is24hClock, setIs24hClock] = useState(timeFormat === "HH:mm" && true);
const { t } = useLocale();
useEffect(() => {
@@ -53,7 +52,7 @@ const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone }) => {
id="timeZone"
value={selectedTimeZone}
onChange={(tz: ITimezoneOption) => setSelectedTimeZone(tz.value)}
className="focus:border-brand mt-1 mb-2 block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm"
className="focus:border-brand mt-1 mb-2 block w-full rounded-md border-gray-300 text-sm focus:ring-black"
/>
</div>
) : null;
@@ -1,24 +1,6 @@
// Get router variables
import {
ArrowLeftIcon,
ChevronDownIcon,
ChevronUpIcon,
ClipboardCheckIcon,
ClockIcon,
CreditCardIcon,
GlobeIcon,
InformationCircleIcon,
LocationMarkerIcon,
RefreshIcon,
VideoCameraIcon,
} from "@heroicons/react/solid";
import { EventType } from "@prisma/client";
import * as Collapsible from "@radix-ui/react-collapsible";
import { useContracts } from "contexts/contractsContext";
import dayjs, { Dayjs } from "dayjs";
import customParseFormat from "dayjs/plugin/customParseFormat";
import timeZone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";
import { TFunction } from "next-i18next";
import { useRouter } from "next/router";
import { useEffect, useMemo, useState } from "react";
@@ -26,31 +8,32 @@ import { FormattedNumber, IntlProvider } from "react-intl";
import { z } from "zod";
import { AppStoreLocationType, LocationObject, LocationType } from "@calcom/app-store/locations";
import dayjs, { Dayjs } from "@calcom/dayjs";
import {
useEmbedNonStylesConfig,
useEmbedStyles,
useIsBackgroundTransparent,
useIsEmbed,
useEmbedStyles,
useEmbedNonStylesConfig,
useIsBackgroundTransparent,
} from "@calcom/embed-core/embed-iframe";
import { useContracts } from "@calcom/features/ee/web3/contexts/contractsContext";
import CustomBranding from "@calcom/lib/CustomBranding";
import classNames from "@calcom/lib/classNames";
import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { yyyymmdd } from "@calcom/lib/date-fns";
import { CAL_URL, WEBSITE_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import useTheme from "@calcom/lib/hooks/useTheme";
import { getRecurringFreq } from "@calcom/lib/recurringStrings";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
import { localStorage } from "@calcom/lib/webstorage";
import { trpc } from "@calcom/trpc/react";
import { Icon } from "@calcom/ui/Icon";
import DatePicker from "@calcom/ui/booker/DatePicker";
import { asStringOrUndefined } from "@lib/asStringOrNull";
import { timeZone as localStorageTimeZone } from "@lib/clock";
// import { timeZone } from "@lib/clock";
import { useExposePlanGlobally } from "@lib/hooks/useExposePlanGlobally";
import useTheme from "@lib/hooks/useTheme";
import { isBrandingHidden } from "@lib/isBrandingHidden";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
import { detectBrowserTimeFormat } from "@lib/timeFormat";
import { trpc } from "@lib/trpc";
import CustomBranding from "@components/CustomBranding";
import AvailableTimes from "@components/booking/AvailableTimes";
import TimeOptions from "@components/booking/TimeOptions";
import { HeadSeo } from "@components/seo/head-seo";
@@ -61,10 +44,6 @@ import type { AvailabilityPageProps } from "../../../pages/[user]/[type]";
import type { DynamicAvailabilityPageProps } from "../../../pages/d/[link]/[slug]";
import type { AvailabilityTeamPageProps } from "../../../pages/team/[slug]/[type]";
dayjs.extend(utc);
dayjs.extend(timeZone);
dayjs.extend(customParseFormat);
type Props = AvailabilityTeamPageProps | AvailabilityPageProps | DynamicAvailabilityPageProps;
export const locationKeyToString = (location: LocationObject, t: TFunction) => {
@@ -96,23 +75,19 @@ export const locationKeyToString = (location: LocationObject, t: TFunction) => {
}
};
const GoBackToPreviousPage = ({ slug }: { slug: string }) => {
const GoBackToPreviousPage = ({ t }: { t: TFunction }) => {
const router = useRouter();
const [previousPage, setPreviousPage] = useState<string>();
useEffect(() => {
setPreviousPage(document.referrer);
}, []);
return previousPage === `${WEBAPP_URL}/${slug}` ? (
const path = router.asPath.split("/");
path.pop(); // Remove the last item (where we currently are)
path.shift(); // Removes first item e.g. if we were visitng "/teams/test/30mins" the array will new look like ["teams","test"]
const slug = path.join("/");
return (
<div className="flex h-full flex-col justify-end">
<ArrowLeftIcon
className="h-4 w-4 text-black transition-opacity hover:cursor-pointer dark:text-white"
onClick={() => router.back()}
/>
<p className="sr-only">Go Back</p>
<button title={t("profile")} onClick={() => router.replace(`${WEBSITE_URL}/${slug}`)}>
<Icon.ArrowLeft className="h-4 w-4 text-black transition-opacity hover:cursor-pointer dark:text-white" />
<p className="sr-only">Go Back</p>
</button>
</div>
) : (
<></>
);
};
@@ -125,16 +100,16 @@ const useSlots = ({
eventTypeId: number;
startTime?: Dayjs;
endTime?: Dayjs;
timeZone: string;
timeZone?: string;
}) => {
const { data, isLoading } = trpc.useQuery(
const { data, isLoading, isIdle } = trpc.useQuery(
[
"viewer.public.slots.getSchedule",
{
eventTypeId,
startTime: startTime?.toISOString() || "",
timeZone,
endTime: endTime?.toISOString() || "",
timeZone,
},
],
{ enabled: !!startTime && !!endTime }
@@ -148,12 +123,12 @@ const useSlots = ({
}
}, [data]);
return { slots: cachedSlots, isLoading };
// The very first time isIdle is set if auto-fetch is disabled, so isIdle should also be considered a loading state.
return { slots: cachedSlots, isLoading: isLoading || isIdle };
};
const SlotPicker = ({
eventType,
timezoneDropdown,
timeFormat,
timeZone,
recurringEventCount,
@@ -161,45 +136,44 @@ const SlotPicker = ({
weekStart = 0,
}: {
eventType: Pick<EventType, "id" | "schedulingType" | "slug">;
timezoneDropdown: JSX.Element;
timeFormat: string;
timeZone: string;
timeZone?: string;
seatsPerTimeSlot?: number;
recurringEventCount?: number;
weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
}) => {
const [selectedDate, setSelectedDate] = useState<Dayjs>();
const [browsingDate, setBrowsingDate] = useState<Dayjs>();
const { date, setQuery: setDate } = useRouterQuery("date");
const { month, setQuery: setMonth } = useRouterQuery("month");
const router = useRouter();
useEffect(() => {
if (!router.isReady) {
return;
}
const month = asStringOrUndefined(router.query.month);
const date = asStringOrUndefined(router.query.date);
if (!router.isReady) return;
// Etc/GMT is not actually a timeZone, so handle this select option explicitly to prevent a hard crash.
if (timeZone === "Etc/GMT") {
setBrowsingDate(dayjs.utc(month).startOf("month"));
setBrowsingDate(dayjs.utc(month).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0));
if (date) {
setSelectedDate(dayjs.utc(date));
}
} else {
setBrowsingDate(dayjs(month).tz(timeZone, true).startOf("month"));
// Set the start of the month without shifting time like startOf() may do.
setBrowsingDate(
dayjs.tz(month, timeZone).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0)
);
if (date) {
setSelectedDate(dayjs(date).tz(timeZone, true));
// It's important to set the date immediately to the timeZone, dayjs(date) will convert to browsertime.
setSelectedDate(dayjs.tz(date, timeZone));
}
}
}, [router.isReady, router.query.month, router.query.date, timeZone]);
}, [router.isReady, month, date, timeZone]);
const { i18n, isLocaleReady } = useLocale();
const { slots: _1 } = useSlots({
eventTypeId: eventType.id,
startTime: selectedDate?.startOf("month"),
endTime: selectedDate?.endOf("month"),
startTime: selectedDate?.startOf("day"),
endTime: selectedDate?.endOf("day"),
timeZone,
});
const { slots: _2, isLoading } = useSlots({
@@ -215,38 +189,18 @@ const SlotPicker = ({
<>
<DatePicker
isLoading={isLoading}
className={
"mt-8 w-full sm:mt-0 sm:min-w-[455px] " +
(selectedDate
? "sm:w-1/2 sm:border-r sm:pl-4 sm:pr-6 sm:dark:border-gray-700 md:w-1/3 "
: "sm:pl-4")
}
className={classNames(
"mt-8 w-full sm:mt-0 sm:min-w-[455px]",
selectedDate ? "sm:w-1/2 sm:border-r sm:pl-4 sm:pr-6 sm:dark:border-gray-700 md:w-1/3 " : "sm:pl-4"
)}
includedDates={Object.keys(slots).filter((k) => slots[k].length > 0)}
locale={isLocaleReady ? i18n.language : "en"}
selected={selectedDate}
onChange={(selectedDate) => {
router.replace(
{
query: {
...router.query,
date: selectedDate.format("YYYY-MM-DD"),
},
},
undefined,
{ shallow: true }
);
onChange={(newDate) => {
setDate(newDate.format("YYYY-MM-DD"));
}}
onMonthChange={(browsingDate) => {
router.replace(
{
query: {
...router.query,
month: browsingDate.format("YYYY-MM"),
},
},
undefined,
{ shallow: true }
);
onMonthChange={(newMonth) => {
setMonth(newMonth.format("YYYY-MM"));
}}
browsingDate={browsingDate}
weekStart={weekStart}
@@ -255,7 +209,7 @@ const SlotPicker = ({
{selectedDate && (
<AvailableTimes
isLoading={isLoading}
slots={slots[yyyymmdd(selectedDate.toDate())]}
slots={slots[selectedDate.format("YYYY-MM-DD")]}
date={selectedDate}
timeFormat={timeFormat}
eventTypeId={eventType.id}
@@ -274,15 +228,18 @@ function TimezoneDropdown({
onChangeTimeFormat,
onChangeTimeZone,
timeZone,
timeFormat,
}: {
onChangeTimeFormat: (newTimeFormat: string) => void;
onChangeTimeZone: (newTimeZone: string) => void;
timeZone?: string;
timeFormat: string;
}) {
const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false);
useEffect(() => {
handleToggle24hClock(localStorage.getItem("timeOption.is24hClock") === "true");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -299,16 +256,22 @@ function TimezoneDropdown({
return (
<Collapsible.Root open={isTimeOptionsOpen} onOpenChange={setIsTimeOptionsOpen}>
<Collapsible.Trigger className="min-w-32 text-bookinglight mb-1 -ml-2 px-2 py-1 text-left dark:text-white">
<GlobeIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
{timeZone || dayjs.tz.guess()}
{isTimeOptionsOpen ? (
<ChevronUpIcon className="ml-1 -mt-1 inline-block h-4 w-4" />
) : (
<ChevronDownIcon className="ml-1 -mt-1 inline-block h-4 w-4" />
)}
<p className="text-gray-600 dark:text-white">
<Icon.Globe className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
{timeZone}
{isTimeOptionsOpen ? (
<Icon.ChevronUp className="ml-1 -mt-1 inline-block h-4 w-4 text-gray-400" />
) : (
<Icon.ChevronDown className="ml-1 -mt-1 inline-block h-4 w-4 text-gray-400" />
)}
</p>
</Collapsible.Trigger>
<Collapsible.Content>
<TimeOptions onSelectTimeZone={handleSelectTimeZone} onToggle24hClock={handleToggle24hClock} />
<TimeOptions
onSelectTimeZone={handleSelectTimeZone}
onToggle24hClock={handleToggle24hClock}
timeFormat={timeFormat}
/>
</Collapsible.Content>
</Collapsible.Root>
);
@@ -320,12 +283,25 @@ const dateQuerySchema = z.object({
timeZone: z.string().optional().default(""),
});
const useRouterQuery = <T extends string>(name: T) => {
const router = useRouter();
const query = z.object({ [name]: z.string().optional() }).parse(router.query);
const setQuery = (newValue: string | number | null | undefined) => {
router.replace({ query: { ...router.query, [name]: newValue } }, undefined, { shallow: true });
};
return { [name]: query[name], setQuery } as {
[K in T]: string | undefined;
} & { setQuery: typeof setQuery };
};
const AvailabilityPage = ({ profile, eventType }: Props) => {
const router = useRouter();
const isEmbed = useIsEmbed();
const query = dateQuerySchema.parse(router.query);
const { rescheduleUid } = query;
const { Theme } = useTheme(profile.theme);
useTheme(profile.theme);
const { t } = useLocale();
const { contracts } = useContracts();
const availabilityDatePickerEmbedStyles = useEmbedStyles("availabilityDatePicker");
@@ -378,10 +354,10 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
: recurringEventCount
? "max-w-4xl"
: "max-w-3xl";
const timezoneDropdown = useMemo(
() => (
<TimezoneDropdown
timeFormat={timeFormat}
onChangeTimeFormat={setTimeFormat}
timeZone={timeZone}
onChangeTimeZone={setTimeZone}
@@ -389,15 +365,21 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
),
[timeZone]
);
const rawSlug = profile.slug ? profile.slug.split("/") : [];
if (rawSlug.length > 1) rawSlug.pop(); //team events have team name as slug, but user events have [user]/[type] as slug.
const slug = rawSlug.join("/");
return (
<>
<Theme />
<HeadSeo
title={`${rescheduleUid ? t("reschedule") : ""} ${eventType.title} | ${profile.name}`}
description={`${rescheduleUid ? t("reschedule") : ""} ${eventType.title}`}
name={profile.name || undefined}
username={profile.slug || undefined}
username={slug || undefined}
nextSeoProps={{
nofollow: eventType.hidden,
noindex: eventType.hidden,
}}
/>
<CustomBranding lightVal={profile.brandColor} darkVal={profile.darkBrandColor} />
<div>
@@ -444,13 +426,13 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
<div className="flex flex-col space-y-4">
{eventType?.description && (
<p className="text-gray-600 dark:text-white">
<InformationCircleIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
<Icon.Info className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
{eventType.description}
</p>
)}
{eventType?.requiresConfirmation && (
<p className="text-gray-600 dark:text-white">
<ClipboardCheckIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
<Icon.CheckSquare className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
{t("requires_confirmation")}
</p>
)}
@@ -459,9 +441,9 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
{Object.values(AppStoreLocationType).includes(
eventType.locations[0].type as unknown as AppStoreLocationType
) ? (
<VideoCameraIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<Icon.Video className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
) : (
<LocationMarkerIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<Icon.MapPin className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
)}
{locationKeyToString(eventType.locations[0], t)}
@@ -470,7 +452,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
{eventType.locations.length > 1 && (
<div className="flex-warp flex text-gray-600 dark:text-white">
<div className="mr-[10px] ml-[2px] -mt-1 ">
<LocationMarkerIcon className="inline-block h-4 w-4 text-gray-400" />
<Icon.MapPin className="inline-block h-4 w-4 text-gray-400" />
</div>
<p>
{eventType.locations.map((el, i, arr) => {
@@ -487,12 +469,12 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
</div>
)}
<p className="text-gray-600 dark:text-white">
<ClockIcon className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
<Icon.Clock className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
{eventType.length} {t("minutes")}
</p>
{eventType.price > 0 && (
<div className="text-gray-600 dark:text-white">
<CreditCardIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 dark:text-gray-400" />
<Icon.CreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 dark:text-gray-400" />
<IntlProvider locale="en">
<FormattedNumber
value={eventType.price / 100.0}
@@ -504,7 +486,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
)}
{!rescheduleUid && eventType.recurringEvent && (
<div className="text-gray-600 dark:text-white">
<RefreshIcon className="float-left mr-[10px] mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
<Icon.RefreshCcw className="float-left mr-[10px] mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
<div className="ml-[27px]">
<p className="mb-1 -ml-2 inline px-2 py-1">
{getRecurringFreq({ t, recurringEvent: eventType.recurringEvent })}
@@ -513,7 +495,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
type="number"
min="1"
max={eventType.recurringEvent.count}
className="w-15 h-7 rounded-sm border-gray-300 bg-white text-gray-600 shadow-sm [appearance:textfield] ltr:mr-2 rtl:ml-2 dark:border-gray-500 dark:bg-gray-600 dark:text-white sm:text-sm"
className="w-15 h-7 rounded-sm border-gray-300 bg-white text-sm text-gray-600 [appearance:textfield] ltr:mr-2 rtl:ml-2 dark:border-gray-500 dark:bg-gray-600 dark:text-white"
defaultValue={eventType.recurringEvent.count}
onChange={(event) => {
setRecurringEventCount(parseInt(event?.target.value));
@@ -584,7 +566,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
{eventType?.description && (
<div className="flex text-gray-600 dark:text-white">
<div>
<InformationCircleIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<Icon.Info className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
</div>
<p>{eventType.description}</p>
</div>
@@ -592,7 +574,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
{eventType?.requiresConfirmation && (
<div className="flex text-gray-600 dark:text-white">
<div>
<ClipboardCheckIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<Icon.CheckSquare className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
</div>
{t("requires_confirmation")}
</div>
@@ -602,9 +584,9 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
{Object.values(AppStoreLocationType).includes(
eventType.locations[0].type as unknown as AppStoreLocationType
) ? (
<VideoCameraIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<Icon.Video className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
) : (
<LocationMarkerIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<Icon.MapPin className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
)}
{locationKeyToString(eventType.locations[0], t)}
@@ -613,7 +595,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
{eventType.locations.length > 1 && (
<div className="flex-warp flex text-gray-600 dark:text-white">
<div className="mr-[10px] ml-[2px] -mt-1 ">
<LocationMarkerIcon className="inline-block h-4 w-4 text-gray-400" />
<Icon.MapPin className="inline-block h-4 w-4 text-gray-400" />
</div>
<p>
{eventType.locations.map((el, i, arr) => {
@@ -630,12 +612,12 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
</div>
)}
<p className="text-gray-600 dark:text-white">
<ClockIcon className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
<Icon.Clock className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
{eventType.length} {t("minutes")}
</p>
{!rescheduleUid && eventType.recurringEvent && (
<div className="text-gray-600 dark:text-white">
<RefreshIcon className="float-left mr-[10px] mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
<Icon.RefreshCcw className="float-left mr-[10px] mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
<div className="ml-[27px]">
<p className="mb-1 -ml-2 inline px-2 py-1">
{getRecurringFreq({ t, recurringEvent: eventType.recurringEvent })}
@@ -644,7 +626,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
type="number"
min="1"
max={eventType.recurringEvent.count}
className="w-15 h-7 rounded-sm border-gray-300 bg-white text-gray-600 shadow-sm [appearance:textfield] ltr:mr-2 rtl:ml-2 dark:border-gray-500 dark:bg-gray-600 dark:text-white sm:text-sm"
className="w-15 h-7 rounded-sm border-gray-300 bg-white text-sm text-gray-600 [appearance:textfield] ltr:mr-2 rtl:ml-2 dark:border-gray-500 dark:bg-gray-600 dark:text-white"
defaultValue={eventType.recurringEvent.count}
onChange={(event) => {
setRecurringEventCount(parseInt(event?.target.value));
@@ -660,7 +642,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
)}
{eventType.price > 0 && (
<p className="-ml-2 px-2 py-1 text-gray-600 dark:text-white">
<CreditCardIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<Icon.CreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<IntlProvider locale="en">
<FormattedNumber
value={eventType.price / 100.0}
@@ -673,7 +655,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
{timezoneDropdown}
</div>
<GoBackToPreviousPage slug={profile.slug || ""} />
{!isEmbed && <GoBackToPreviousPage t={t} />}
{/* Temporarily disabled - booking?.startTime && rescheduleUid && (
<div>
@@ -689,29 +671,20 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
</div>
)*/}
</div>
{timeZone && (
<SlotPicker
weekStart={
typeof profile.weekStart === "string"
? ([
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
].indexOf(profile.weekStart) as 0 | 1 | 2 | 3 | 4 | 5 | 6)
: profile.weekStart /* Allows providing weekStart as number */
}
eventType={eventType}
timezoneDropdown={timezoneDropdown}
timeFormat={timeFormat}
timeZone={timeZone}
seatsPerTimeSlot={eventType.seatsPerTimeSlot || undefined}
recurringEventCount={recurringEventCount}
/>
)}
<SlotPicker
weekStart={
typeof profile.weekStart === "string"
? (["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"].indexOf(
profile.weekStart
) as 0 | 1 | 2 | 3 | 4 | 5 | 6)
: profile.weekStart /* Allows providing weekStart as number */
}
eventType={eventType}
timeFormat={timeFormat}
timeZone={timeZone}
seatsPerTimeSlot={eventType.seatsPerTimeSlot || undefined}
recurringEventCount={recurringEventCount}
/>
</div>
</div>
{(!eventType.users[0] || !isBrandingHidden(eventType.users[0])) && !isEmbed && <PoweredByCal />}

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