Compare commits

..
112 Commits
Author SHA1 Message Date
zomars e8d6d50bc2 v1.8.5 2022-08-09 16:56:53 -06:00
9221042db4 feat: signin test email magic signup in app (#3749)
* feat: signin test email magic signup in app

* fix: no signIn page in nextauth

* fix: remove commented signIN

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

remove import useRouter

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

Co-authored-by: Agusti Fernandez Pardo <git@agusti.me>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
2022-08-09 15:38:21 -06:00
alannncandOmar López b936b7c256 Fix url redirect to a only path redirect when joining a team (#3757)
* fix full url redirect to a only path redirect

* Remove string templates

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

Co-authored-by: Omar López <zomars@me.com>
2022-08-09 15:19:44 -06:00
Udit TakkarandCarina Wollendorfer 57869f0b88 fix: cancel booking buttons design (#3748)
* fix: cancel booking buttons design

* chore: remove rounded

Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2022-08-09 14:23:55 -04:00
alannnc 7a4795bdbf Fix request-reschedule for team events (#3721) 2022-08-09 11:23:12 -06:00
Peer Richelsen 773bb5424b Update README.md 2022-08-09 15:55:48 +02:00
Peer Richelsen 2c600cff43 Update README.md 2022-08-09 15:55:04 +02:00
Omar Lópezandsean-brydon 0eeee42811 Adds middleware to get V2 early access (#3617)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-08-09 10:21:15 +01:00
Udit Takkarandkodiakhq[bot] 8eb45ee1eb fix: remove mt (#3740)
* fix: remove mt

* fix: un comment icon

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-08-09 04:49:46 +00:00
Alex van AndelandLeo Giovanetti 87d4afea13 Same-day schedules created invalid workingHours (#3742)
* Same-day schedules created invalid workingHours

* Uncomment logger

* Previous version did not properly substract the days

Co-authored-by: Leo Giovanetti <hello@leog.me>
2022-08-08 14:17:33 -06:00
Peer Richelsenandzomars 7776bbbf52 added a few desktop only improvements (#3725)
* added a few desktop only improvements

* added horizontal line

* fixed icon

* hide download button when already in desktop app

* codacy fixes

Co-authored-by: zomars <zomars@me.com>
2022-08-08 13:39:51 -06:00
88384192f8 Fixes bug when changing the location of a booking (#3585)
* use correct booking

* delete calendar event and update booking reference

* use middleware to get booking

* updating event instad of deleting and creating a new one

* fix issue with google hangout link

* remove unsed imports

* Apply suggestions from code review

* Update bookings.tsx

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-08-08 19:38:02 +00:00
zomars f036f0fb64 RHF conditionals fixes
Improved legibility
2022-08-08 13:24:49 -06:00
Ilya KatzandJoe Au-Yeung d0f34aa699 Update instructions for integration with google calendar (#3558)
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2022-08-08 11:06:53 -04:00
+2 507f5d028e Setup Wizard Not Pulling Timezone (#3677)
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Matheus Benini <matheus_benini@hotmail.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Justine Nakitto <justinejjingo@gmail.com>

Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Matheus Benini <matheus_benini@hotmail.com>
Co-authored-by: Grace Nshokano <grace.devolop@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Murilo Amaral <87545137+MuriloAmarals@users.noreply.github.com>
Co-authored-by: Júlio Piubello da Silva Cabral <julio.piubello@gitstart.dev>
Co-authored-by: Matheus Muniz <matheusmuniz100@hotmail.com>
Co-authored-by: Justine Nakitto <justinejjingo@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-08-08 12:19:11 +00:00
Leo GiovanettiandPeer Richelsen 477c411611 Updating website to latest (#3731)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-08-08 11:05:44 +02:00
alannncandkodiakhq[bot] 3652ffa40a Added notes for running tests for embed-core (#3720)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-08-08 08:06:44 +00:00
Peer Richelsenandsean-brydon 1332b76441 added ping.gg app (#3728)
* added ping.gg app, not sure why its not showing

* nit

* Fixes

* fixed email

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-08-08 05:57:09 +00:00
Peer Richelsen 1a3aaa134e Update README.md 2022-08-07 19:45:32 +02:00
Peer Richelsen af06ded584 Update README.md 2022-08-07 19:44:31 +02:00
Peer Richelsen 2f9862bb47 fixed icons (#3730) 2022-08-07 14:29:07 +02:00
zomars 22d3fb7e92 Adds verify page 2022-08-05 21:09:52 -06:00
zomars 3ed206df23 Squashed commit of the following:
commit c378c647fe4822071cbdfc577fcfc950e53973df
Author: zomars <zomars@me.com>
Date:   Fri Aug 5 17:36:21 2022 -0600

    Cleanup

commit a89cc7981df1c2aae96188466833490eb3b227a1
Author: zomars <zomars@me.com>
Date:   Fri Aug 5 17:17:57 2022 -0600

    redirect fixes

commit a27979c2b3f06c9956ee9abfef1e82a5558b6411
Author: zomars <zomars@me.com>
Date:   Fri Aug 5 17:02:33 2022 -0600

    Fingers crossed

commit c08a09e8d2ba3eb2561f418bb3b0370245f79bf7
Author: zomars <zomars@me.com>
Date:   Fri Aug 5 16:28:55 2022 -0600

    Update [...nextauth].tsx

commit 4063bddb2ea3cbf3dda6be6cc10070ecc1eb578c
Author: zomars <zomars@me.com>
Date:   Fri Aug 5 16:05:21 2022 -0600

    Submodule sync

commit e44dc73a3b751ba4cacc85abfe6680a668393c0d
Author: zomars <zomars@me.com>
Date:   Fri Aug 5 16:05:06 2022 -0600

    Upgrade next-auth

commit 2b36e0a1b376083739ea06b793f1523e2f158a33
Author: zomars <zomars@me.com>
Date:   Fri Aug 5 15:23:03 2022 -0600

    Magic signup fixes

commit 2cc81ca7e2f79a4e3fcab370e23ac29cbe3ee18d
Author: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Date:   Thu Aug 4 11:49:51 2022 -0400

    Properly parse list calendar call to MS Graph (#3698)

commit 65887d8f9db8b399964fce38ec104ed24fcc48e6
Author: Leo Giovanetti <hello@leog.me>
Date:   Thu Aug 4 12:52:50 2022 -0300

    Fix app-store seeding (#3693) (#3697)

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

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

commit 71594b962352519c4ed4ee6cf68d16aac9b71e26
Author: Leo Giovanetti <hello@leog.me>
Date:   Wed Aug 3 16:48:07 2022 -0300

    fixed dark mode profile page (#3650) (#3681)

    * fixed dark mode profile page

    * nit

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

commit 78a5ae988274ff05f26309033821acc48e22823d
Author: Leo Giovanetti <hello@leog.me>
Date:   Wed Aug 3 14:05:02 2022 -0300

    Potential fix (#3671) (#3676)

commit b006240b0c62e9e62944cb7184793f099358e2ec
Merge: 6179b3fbe e7418f68a
Author: zomars <zomars@me.com>
Date:   Mon Aug 1 18:20:47 2022 -0600

    Merge branch 'main' into production

commit 6179b3fbe06709f1cf3a4e54214a7dbb1543765e
Author: zomars <zomars@me.com>
Date:   Mon Aug 1 18:20:40 2022 -0600

    Fixes successRedirectUrl validation

commit 14443099e0377e8d9af52f53f15c2f8b91b784d2
Author: zomars <zomars@me.com>
Date:   Mon Aug 1 17:59:47 2022 -0600

    successRedirectUrl fixes
2022-08-05 18:08:05 -06:00
ff3bd5fc61 Event description now supports markdown formatting (#3688)
* yarn

* yarn update

* added Mardown style formatting with sanitized inputs

* Revert "yarn"

This reverts commit 46eb3595cf687504c6783315749b3124936080b9.

* Revert "yarn"

This reverts commit 46eb3595cf687504c6783315749b3124936080b9.

* Revert "yarn update"

This reverts commit f4600d83a67b930c6dd72e292827578856ca5428.

* Revert "yarn"

This reverts commit 46eb3595cf687504c6783315749b3124936080b9.

* Revert "yarn update"

This reverts commit f4600d83a67b930c6dd72e292827578856ca5428.

* Revert "Revert "yarn update""

This reverts commit 8ccab5ee237fb228115229f3c89cf4b848bc9449.

* Revert "Revert "yarn""

This reverts commit 78a755eb39cb518eb0526272a3c11882c48004de.

* yarn.lock removed

* Parses using prisma middleware

* Reverting changes

* Fixes hydration warning

* Fixes Dom warnings

* Update yarn.lock

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2022-08-05 18:44:30 +00:00
be16b72400 feat: support lark calendar (#3019)
* feat: support lark calendar

* New Crowdin translations by Github Action (#3016)

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

* fix: lark calendar get app ticket bugs

* feat: add send msg service for lark bot

* fix: comment on PR of lark-calendar

* chore: update lark bot message

* Refactors add endpoint

* Adds missing GET endpoint handler

* Update yarn.lock

* fix: comments

* Update yarn.lock

* fix: comment about inferred type

* Added fetcher helper

* Update yarn.lock

Co-authored-by: chengcheng.frontend <chengcheng.frontend@bytedance.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: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2022-08-05 17:56:20 +00:00
775fe92960 Fix bug for reschedule on opt-in bookings (#3678)
* Fix bug for reschedule on opt-in bookings

* Fix a missing query parameter being removed when changing month, useRouterQuery

* Added test for rescheduling user type condition

* Fix conditional that triggers input disabling when rescheduling

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
2022-08-05 17:08:47 +00:00
Omar López 70d6c8d8e7 Cleans up cli generator and enables prettier formatting (#3718) 2022-08-05 17:41:26 +01:00
Udit Takkar f589d06ed4 fix: icon positioning (#3715) 2022-08-05 20:33:27 +05:30
Udit TakkarandPeer Richelsen 2b7b03fffd fix: group hover bug (#3699)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-08-05 09:07:06 +00:00
a53ca3389c updates to all public booking pages (#3648)
* updates to all public booking pages

* sidebar tweak

* resolving error

* Fix linting errors

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-08-05 08:46:44 +00:00
Hariom Balhara 85764b6aa6 Embed Test: Verifies that app remains hidden till link is ready (#3669)
* Add test to verify that webapp remains hidden till link is considered ready

* Make tests more stable
2022-08-05 08:26:15 +00:00
Hariom Balhara ee767a3e41 Fix textarea dark theme (#3712) 2022-08-05 07:32:21 +00:00
Leo Giovanetti 414233961c Fixing App tabs routing (#3711)
* Removing linkProps

* Fixing format
2022-08-05 05:21:43 +00:00
Leo Giovanetti 7747b3d9a0 Adding debugging info (#3706) 2022-08-04 16:55:20 -03:00
Udit Takkar da638c4b1b fix: made add-to-calendar responsive (#3703) 2022-08-04 13:27:04 -06:00
218c3d7b50 fix heroku build (#3630)
* pick https://github.com/nosovk/cal.com/pull/1

* make all devdeps to devDependencies

* add more ENV to Heroku config
https://github.com/calcom/cal.com/pull/3630

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2022-08-04 11:44:29 -06:00
Joe Au-Yeung 37309e8435 Properly parse list calendar call to MS Graph (#3698) 2022-08-04 16:49:51 +01:00
Hariom Balharaandkodiakhq[bot] 2aee450c0b Fix app-store seeding (#3693)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-08-04 14:15:23 +00:00
Carina Wollendorfer ca7a78dcda Improve design of workflow reminder email (#3685) 2022-08-04 08:52:05 +00:00
fc4b88e222 New Crowdin translations by Github Action (#3689)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-08-04 10:39:19 +02:00
github-actions[bot] affedd1014 New Crowdin translations by Github Action (#3683) 2022-08-04 09:25:24 +01:00
Carina Wollendorfer 4240235cb8 make edit button full width (#3686) 2022-08-04 09:23:36 +01:00
2a73fd8188 Variables for custom templates in workflows (#3602)
* 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

* add dropdown for adding dynamic variables to string

* use lighter text colour for disabled button

* change textarea for email subject to one row only

* add function to create custom template with dynamic variables

* add location to dynamic variables

* add missing locations to custom template

* add variables at cursor position

* remove second curly brakets from variables

* code cleanup

* fix names for attendee and organizer

* enable internationaliztion for custom reminders

* disabled add workflow button when mutation is loading

* fix translation keys

* internationalize dates

* code clean up

* add translation for email subject

* skip translation for english

* remove language from BookingInfo type

* add additional notes as variable

* add custom inputs as variables

* add information for custom inputs

* add translation

* Add formated variables in additional input information

* fix type errors

* code clean up

* Update AddVariablesDropdown.tsx

* Update variableTranslations.ts

* Update variableTranslations.ts

* Formatting

* Update [workflow].tsx

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-08-03 22:22:38 +00:00
e878e1a665 Fix/api build (#3675)
* fix: shared next-auth calendso session type fix

* fix: move imports from @lib -> @calcom/lib to fix types

* Consolidates next-auth definitions

Co-authored-by: Agusti Fernandez Pardo <git@agusti.me>
Co-authored-by: zomars <zomars@me.com>
2022-08-03 13:18:26 -06:00
298a6d5e15 Tweaks for Website signup form (#3635)
* WIP, only deep testing needed

* Feedback suggestions

* Uses early returns instead

* Allows overriding t function via prop

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2022-08-03 13:59:40 -03:00
sean-brydon 532723fb41 Update V2 components + Move to react-icons (#3670) 2022-08-03 16:01:29 +00:00
b3b82fa1cf When no location, defaults to cal video (#3517)
* yarn

* fixed bug

* fixed warning

* fixed problem on server side

* yarn update

* Delete yarn.lock

* removed unused type

* Revert "Delete yarn.lock"

This reverts commit 4fe99af4757343d9efb2326be546cd81161c8da3.

revert delete yarn.lock

* Delete yarn.lock

* Revert "Delete yarn.lock"

This reverts commit 9f76a548e0950202674e5c5fcb755db3ff7867bd.

revert delete yarn.lock

* Revert "yarn update"

This reverts commit 2bef496c1693acd509a7b577ca7013e94ab7200a.

* yarn

* remove yarn.lock

* remove yarn.lock

* removed double declaration

* Revert "yarn update"

This reverts commit 2bef496c1693acd509a7b577ca7013e94ab7200a.

* stop tracking yarn lockfile

* undo all yarn lockfile changes

* Revert "yarn"

This reverts commit 46eb3595cf687504c6783315749b3124936080b9.

* remove all yarn changes

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

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

* zod transform insteead of ternary operator

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2022-08-03 09:35:08 -06:00
Leo Giovanetti 48a1f22ca5 Potential fix (#3671) 2022-08-03 15:23:01 +00:00
Peer Richelsen 77bf1d0712 Update README.md 2022-08-03 14:38:11 +02:00
Peer Richelsen 23c79fff75 Update README.md 2022-08-03 14:37:45 +02:00
Peer Richelsen abea19ad39 Update LICENSE 2022-08-03 14:36:43 +02:00
Peer Richelsen 5ca2d7cae2 Update LICENSE 2022-08-03 14:36:32 +02:00
Adam Garbowski 3ac140fad5 [Proposal] Event type error popover proposal (#3627) 2022-08-03 09:03:01 +00:00
Heiki 5358e5848d feat(troubleshoot): add event titles to the troubleshoot page (#3434) (#3437) 2022-08-03 09:19:16 +02:00
6301ea16f8 Avoid duplicating any video credential (#3593)
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2022-08-02 12:52:21 -06:00
alannnc e767d233ad Fix/google calendar false alert (#3636) 2022-08-02 18:07:45 +00:00
Peer Richelsen 7480a73905 fixed dark mode profile page (#3650)
* fixed dark mode profile page

* nit
2022-08-02 18:59:33 +02:00
Agusti Fernandez Pardo fe50b8de18 fix: cleanup onboarding username (#3638) 2022-08-02 10:33:21 -06:00
Agusti Fernandez Pardo fd8b323e47 fix: base_url for login link should be WEBAPP_URL not WEBSITE_URL (#3637) 2022-08-02 10:05:09 -06:00
Leo Giovanettiandkodiakhq[bot] 760026b2e6 Correcting syntax of overloading (#3598)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-08-02 05:52:25 +00:00
zomars 53de4ddaaf Fixes successRedirectUrl
commit b006240b0c62e9e62944cb7184793f099358e2ec
Merge: 6179b3fbe e7418f68a
Author: zomars <zomars@me.com>
Date:   Mon Aug 1 18:20:47 2022 -0600

    Merge branch 'main' into production

commit 6179b3fbe06709f1cf3a4e54214a7dbb1543765e
Author: zomars <zomars@me.com>
Date:   Mon Aug 1 18:20:40 2022 -0600

    Fixes successRedirectUrl validation

commit 14443099e0377e8d9af52f53f15c2f8b91b784d2
Author: zomars <zomars@me.com>
Date:   Mon Aug 1 17:59:47 2022 -0600

    successRedirectUrl fixes
2022-08-01 18:45:47 -06:00
zomars e7418f68a4 Submodule sync 2022-08-01 17:44:40 -06:00
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
527 changed files with 8281 additions and 3236 deletions
+8
View File
@@ -88,3 +88,11 @@ VITAL_REGION="us"
# @see https://github.com/calcom/cal.com/blob/main/packages/app-store/zapier/README.md
ZAPIER_INVITE_LINK=""
# *********************************************************************************************************
# - LARK
# Needed to enable Lark Calendar integration and Login with Lark
# @see <https://open.larksuite.com/document/ukTMukTMukTM/ukDNz4SO0MjL5QzM/g>
LARK_OPEN_APP_ID=""
LARK_OPEN_APP_SECRET=""
LARK_OPEN_VERIFICATION_TOKEN=""
# *********************************************************************************************************
+2
View File
@@ -30,6 +30,8 @@ NEXT_PUBLIC_WEBAPP_URL='http://localhost:3000'
NEXT_PUBLIC_WEBSITE_URL='http://localhost:3000'
NEXT_PUBLIC_CONSOLE_URL='http://localhost:3004'
NEXT_PUBLIC_EMBED_LIB_URL='http://localhost:3000/embed/embed.js'
# Make sure you don't have .example.com as a value
NEXTAUTH_DOMAIN='localhost:3000'
# To enable SAML login, set both these variables
# @see https://github.com/calcom/cal.com/tree/main/packages/ee#setting-up-saml-login
+3 -25
View File
@@ -15,38 +15,14 @@ on:
# List of jobs
jobs:
check-if-ui-has-changed:
runs-on: ubuntu-latest
# Declare outputs for next jobs
outputs:
docs_changed: ${{ steps.check_file_changed.outputs.docs_changed }}
steps:
- uses: actions/checkout@v2
with:
# Checkout as many commits as needed for the diff
fetch-depth: 2
- shell: pwsh
id: check_file_changed
run: |
# Diff HEAD with the previous commit
$diff = git diff --name-only HEAD^ HEAD
# Check if a file under /packages/ui or apps/storybook has been modified since the previous commit
$SourceDiff = $diff | Where-Object { $_ -match '^packages/ui/' -or $_ -match '^apps/storybook/' }
$HasDiff = $SourceDiff.Length -gt 0
# Set the output named "hasUiChanges"
Write-Host "::set-output name=hasUiChanges::$HasDiff"
chromatic-deployment:
runs-on: ubuntu-latest
needs: [ check-if-ui-has-changed ]
if: needs.checkIfUiHasChanged.outputs.hasUiChanges== 'True'
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }} # So we can test on forks
fetch-depth: 2
fetch-depth: 0
- name: Install dependencies
run: yarn
# 👇 Adds Chromatic as a step in the workflow
@@ -56,3 +32,5 @@ jobs:
with:
# 👇 Chromatic projectToken, refer to the manage page to obtain it.
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
workingDir: "apps/storybook"
buildScriptName: "build"
+1 -1
View File
@@ -2,7 +2,7 @@ Copyright (c) 2020-present Cal.com, Inc.
Portions of this software are licensed as follows:
* All content that resides under "apps/web/ee/" (https://github.com/calcom/cal.com/tree/main/apps/web/ee) directory of this repository (Enterprise Edition) is licensed under the license defined in "ee/LICENSE".
* All content that resides under https://github.com/calcom/cal.com/tree/main/packages/features/ee directory of this repository (Enterprise Edition) is licensed under the license defined in "ee/LICENSE".
* All third party components incorporated into the Cal.com Software are licensed under the original license provided by the owner of the applicable component.
* Content outside of the above mentioned directories or restrictions above is available under the "AGPLv3" license as defined below.
+32 -4
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>
@@ -61,6 +64,9 @@ That's where Cal.com comes in. Self-hosted or hosted by us. White-label by desig
<a href="https://www.producthunt.com/posts/calendso?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-calendso" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/top-post-badge.svg?post_id=291910&theme=light&period=monthly" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a> <a href="https://www.producthunt.com/posts/calendso?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-calendso" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=291910&theme=light" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a> <a href="https://www.producthunt.com/stories/how-this-open-source-calendly-alternative-rocketed-to-product-of-the-day" target="_blank"><img src="https://cal.com/maker-grant.svg" alt="Cal.com - The open source Calendly alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
### Built With
- [Next.js](https://nextjs.org/)
@@ -84,7 +90,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)_
@@ -260,12 +266,16 @@ yarn workspace @calcom/web playwright-report
### Docker
The Docker configuration for Cal is an effort powered by people within the community. Cal.com, Inc. does not provide official support for Docker, but we will accept fixes and documentation. Use at your own risk.
The Docker configuration for Cal is an effort powered by people within the community.
If you want to contribute to the Docker repository, [reply here](https://github.com/calcom/docker/discussions/32).
The Docker configuration can be found [in our docker repository](https://github.com/calcom/docker).
Issues with Docker? Find your answer or open a new discussion [here](https://github.com/calcom/docker/discussions) to ask the community.
Cal.com, Inc. does not provide official support for Docker, but we will accept fixes and documentation. Use at your own risk.
### Heroku
<a href="https://heroku.com/deploy?template=https://github.com/calcom/cal.com">
@@ -280,7 +290,9 @@ You can deploy Cal on [Railway](https://railway.app/) using the button above. Th
### Vercel
[![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_SECRET,NEXTAUTH_URL,CALENDSO_ENCRYPTION_KEY,NEXT_PUBLIC_LICENSE_CONSENT,CALCOM_LICENSE_KEY&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)
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 -->
@@ -298,7 +310,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
@@ -316,6 +328,22 @@ We have a list of [good first issues](https://github.com/calcom/cal.com/labels/
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.
#### *Adding google calendar to Cal.com App Store*
After adding Google credentials, you can now Google Calendar App to the app store.
You can repopulate the App store by running
```
cd packages/prisma
yarn seed-app-store
```
You will need to complete a few more steps to activate Google Calendar App.
Make sure to complete section "Obtaining the Google API Credentials". After the do the
following
1. Add extra redirect URL `<Cal.com URL>/api/auth/callback/google`
1. Under 'OAuth concent screen', click "PUBLISH APP"
### Obtaining Microsoft Graph Client ID and Secret
1. Open [Azure App Registration](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps) and select New registration
+59 -4
View File
@@ -10,18 +10,73 @@
}
],
"env": {
"BASE_URL": {
"PGSSLMODE": {
"description": "If you use Heroku to deploy Postgres (or use self-signed certs for Postgres) then put 'no-verify' here.",
"value": "no-verify"
},
"NEXT_PUBLIC_WEBAPP_URL": {
"description": "Replace HEROKU_APP_NAME with the name given to your app",
"value": "https://HEROKU_APP_NAME.herokuapp.com"
"value": "https://HEROKU_APP_NAME.herokuapp.com",
"required": "true"
},
"CALENDSO_ENCRYPTION_KEY": {
"description": "Application Key for symmetric encryption and decryption. Must be 32 bytes for AES256 encryption algorithm.",
"value": "secret"
"value": "secret",
"required": "true"
},
"NEXTAUTH_SECRET": "secret",
"NEXTAUTH_URL": {
"description": "Replace HEROKU_APP_NAME with the name given to your app",
"value": "https://HEROKU_APP_NAME.herokuapp.com"
},
"NEXTAUTH_SECRET": {
"description": "Cookie encryption key",
"generator": "secret",
"required": "true"
},
"NEXT_PUBLIC_LICENSE_CONSENT": {
"description": "License consent",
"value": "true",
"required": "true"
},
"CRON_API_KEY": {
"description": "ApiKey for cronjobs",
"value": ""
},
"SEND_FEEDBACK_EMAIL": {
"description": "Send feedback email",
"value": ""
},
"SENDGRID_API_KEY": {
"description": "Sendgrid api key. Used for email reminders in workflows",
"value": ""
},
"SENDGRID_EMAIL": {
"description": "Sendgrid email. Used for email reminders in workflows",
"value": ""
},
"EMAIL_FROM": {
"description": "Configures the global From: header whilst sending emails",
"value": ""
},
"EMAIL_SERVER_HOST": {
"description": "Configures the global SMTP server host",
"value": "smtp.gmail.com"
},
"EMAIL_SERVER_PORT": {
"description": "Configures the global SMTP server port",
"value": "465"
},
"EMAIL_SERVER_USER": {
"description": "Configures the global SMTP server user",
"value": "<gmail_emailAddress>"
},
"EMAIL_SERVER_PASSWORD": {
"description": "Configures the global SMTP server password",
"value": "<gmail_app_password>"
},
"NEXT_PUBLIC_TEAM_IMPERSONATION": {
"description": "Set the following value to true if you wish to enable Team Impersonation",
"value": "false"
}
},
"scripts": {
+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.
+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,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",
},
};
-2
View File
@@ -8,6 +8,4 @@
--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;
}
+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.FiX className="h-6 w-6 text-white" aria-hidden="true" />
</button>
</div>
</div>
@@ -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 }}
/>
+14 -26
View File
@@ -1,28 +1,17 @@
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 LicenseRequired from "@ee/components/LicenseRequired";
import Shell from "@components/Shell";
import Badge from "@components/ui/Badge";
import Badge from "@calcom/ui/Badge";
import { Icon } from "@calcom/ui/Icon";
import Shell from "@calcom/ui/Shell";
const Component = ({
name,
@@ -94,7 +83,7 @@ const Component = ({
<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")}
<Icon.FiChevronLeft className="h-5 w-5" /> {t("browse_apps")}
</a>
</Link>
<div className="items-center justify-between py-4 sm:flex sm:py-8">
@@ -119,7 +108,7 @@ const Component = ({
{!isLoading ? (
isGlobal || (installedAppCount > 0 && allowedMultipleInstalls) ? (
<div className="flex space-x-3">
<Button StartIcon={CheckIcon} color="secondary" disabled>
<Button StartIcon={Icon.FiCheck} color="secondary" disabled>
{installedAppCount > 0
? t("active_install", { count: installedAppCount })
: t("globally_install")}
@@ -138,7 +127,7 @@ const Component = ({
};
}
return (
<Button StartIcon={PlusIcon} {...props} data-testid="install-app-button">
<Button StartIcon={Icon.FiPlus} {...props} data-testid="install-app-button">
{t("add_another")}
</Button>
);
@@ -182,8 +171,6 @@ const Component = ({
)}
</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">
@@ -223,7 +210,7 @@ const Component = ({
rel="noreferrer"
className="text-blue-500 no-underline hover:underline"
href={docs}>
<BookOpenIcon className="mr-1 -mt-1 inline h-4 w-4" />
<Icon.FiBookOpen className="mr-1 -mt-1 inline h-4 w-4" />
{t("documentation")}
</a>
</li>
@@ -235,7 +222,7 @@ const Component = ({
rel="noreferrer"
className="text-blue-500 no-underline hover:underline"
href={website}>
<ExternalLinkIcon className="mr-1 -mt-px inline h-4 w-4" />
<Icon.FiExternalLink className="mr-1 -mt-px inline h-4 w-4" />
{website.replace("https://", "")}
</a>
</li>
@@ -247,7 +234,8 @@ const Component = ({
rel="noreferrer"
className="text-blue-500 no-underline hover:underline"
href={"mailto:" + email}>
<MailIcon className="mr-1 -mt-px inline h-4 w-4" />
<Icon.FiMail className="mr-1 -mt-px inline h-4 w-4" />
{email}
</a>
</li>
@@ -259,7 +247,7 @@ const Component = ({
rel="noreferrer"
className="text-blue-500 no-underline hover:underline"
href={tos}>
<DocumentTextIcon className="mr-1 -mt-px inline h-4 w-4" />
<Icon.FiFile className="mr-1 -mt-px inline h-4 w-4" />
{t("terms_of_service")}
</a>
</li>
@@ -271,7 +259,7 @@ const Component = ({
rel="noreferrer"
className="text-blue-500 no-underline hover:underline"
href={privacy}>
<ShieldCheckIcon className="mr-1 -mt-px inline h-4 w-4" />
<Icon.FiShield className="mr-1 -mt-px inline h-4 w-4" />
{t("privacy_policy")}
</a>
</li>
@@ -284,7 +272,7 @@ const Component = ({
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
<Icon.FiFlag className="inline h-3 w-3" /> Report App
</a>
</div>
</div>
+1 -3
View File
@@ -19,9 +19,7 @@ export default function AppsShell({ children }: { children: React.ReactNode }) {
return (
<>
<div className="mb-12 block lg:hidden">
{status === "authenticated" && <NavTabs tabs={tabs} linkProps={{ shallow: true }} />}
</div>
<div className="mb-12 block lg:hidden">{status === "authenticated" && <NavTabs tabs={tabs} />}</div>
<main className="pb-6">{children}</main>
</>
);
@@ -99,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) => {
+22 -17
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";
@@ -9,8 +8,10 @@ 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";
@@ -452,7 +453,7 @@ const tabs = [
{
name: "HTML",
tabName: "embed-code",
icon: CodeIcon,
icon: Icon.FiCode,
type: "code",
Component: forwardRef<
HTMLTextAreaElement | HTMLIFrameElement | null,
@@ -474,7 +475,8 @@ const tabs = [
data-testid="embed-code"
ref={ref as typeof ref & MutableRefObject<HTMLTextAreaElement>}
name="embed-code"
className="h-[calc(100%-50px)]"
className="h-[calc(100%-50px)] font-mono"
style={{ resize: "none", overflow: "auto" }}
readOnly
value={
`<!-- Cal ${embedType} embed code begins -->\n` +
@@ -502,7 +504,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previ
{
name: "React",
tabName: "embed-react",
icon: CodeIcon,
icon: Icon.FiCode,
type: "code",
Component: forwardRef<
HTMLTextAreaElement | HTMLIFrameElement | null,
@@ -522,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-[calc(100%-50px)]"
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 */
@@ -541,7 +544,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "react", embedType, calLink, prev
{
name: "Preview",
tabName: "embed-preview",
icon: EyeIcon,
icon: Icon.FiEye,
type: "iframe",
Component: forwardRef<
HTMLIFrameElement | HTMLTextAreaElement | null,
@@ -577,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.FiSun className="ml-2 h-4 w-4 text-gray-500" />
{children}
</components.Control>
);
@@ -596,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={() => {
@@ -804,7 +807,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
},
});
}}>
<ArrowLeftIcon className="mr-4 w-4" />
<Icon.FiArrowLeft className="mr-4 w-4" />
</button>
{embed.title}
</h3>
@@ -823,7 +826,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
? "Floating Popup Customization"
: "Element Click Customization"}
</div>
<ChevronRightIcon
<Icon.FiChevronRight
className={`${
isEmbedCustomizationOpen ? "rotate-90 transform" : ""
} ml-auto h-5 w-5 text-neutral-500`}
@@ -854,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"
@@ -1001,7 +1004,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
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.FiChevronRight
className={`${
isBookingCustomizationOpen ? "rotate-90 transform" : ""
} ml-auto h-5 w-5 text-neutral-500`}
@@ -1132,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();
@@ -1163,14 +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"
onClick={() => openEmbedModal()}>
<CodeIcon className={classNames("h-4 w-4 ltr:mr-2 rtl:ml-2", dark ? "" : "text-neutral-500")} />
{t("Embed")}
{children}
</Button>
);
};
+1 -1
View File
@@ -77,7 +77,7 @@ const NavTabs: FC<NavTabProps> = ({ tabs, linkProps, ...props }) => {
<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.FiUser,
},
{
name: "teams",
href: "/settings/teams",
icon: UserGroupIcon,
icon: Icon.FiUsers,
},
{
name: "security",
href: "/settings/security",
icon: KeyIcon,
icon: Icon.FiKey,
},
{
name: "developer",
href: "/settings/developer",
icon: CodeIcon,
icon: Icon.FiTerminal,
},
{
name: "billing",
href: "/settings/billing",
icon: CreditCardIcon,
icon: Icon.FiCreditCard,
},
{
name: "admin",
href: "/settings/admin",
icon: LockClosedIcon,
icon: Icon.FiLock,
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.FiAlertTriangle 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 -2
View File
@@ -1,10 +1,9 @@
import Link from "next/link";
import { trpc } from "@calcom/trpc/react";
import Badge from "@calcom/ui/Badge";
import Button from "@calcom/ui/Button";
import Badge from "@components/ui/Badge";
interface AppCardProps {
logo: string;
name: string;
+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.FiArrowLeft 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.FiArrowRight className="h-5 w-5 text-gray-600 hover:text-black" />
</button>
</div>
</div>
+1 -1
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";
interface Props {
email: string;
@@ -1,4 +1,3 @@
import { PlusIcon } from "@heroicons/react/solid";
import { useRouter } from "next/router";
import { useForm } from "react-hook-form";
@@ -7,6 +6,7 @@ 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";
@@ -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.FiPlus}>
{t("new_schedule_btn")}
</Button>
</DialogTrigger>
@@ -1,5 +1,3 @@
import { PlusIcon, TrashIcon } from "@heroicons/react/outline";
import { DuplicateIcon } from "@heroicons/react/solid";
import classNames from "classnames";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
@@ -9,6 +7,7 @@ import dayjs, { Dayjs, ConfigType } from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import Button from "@calcom/ui/Button";
import Dropdown, { DropdownMenuContent } from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
import { defaultDayRange } from "@lib/availability";
@@ -231,13 +230,13 @@ 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.FiTrash}
onClick={() => remove(index)}
/>
</div>
@@ -249,7 +248,7 @@ export const DayRanges = ({
type="button"
color="minimal"
size="icon"
StartIcon={PlusIcon}
StartIcon={Icon.FiPlus}
onClick={handleAppend}
/>
</Tooltip>
@@ -259,7 +258,7 @@ export const DayRanges = ({
type="button"
color="minimal"
size="icon"
StartIcon={DuplicateIcon}
StartIcon={Icon.FiCopy}
onClick={handleAppend}
/>
</Tooltip>
@@ -1,4 +1,3 @@
import { DotsHorizontalIcon, TrashIcon } from "@heroicons/react/solid";
import Link from "next/link";
import { Fragment } from "react";
@@ -8,6 +7,7 @@ import { Availability } from "@calcom/prisma/client";
import { inferQueryOutput } from "@calcom/trpc/react";
import { Button } from "@calcom/ui";
import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
export function ScheduleListItem({
schedule,
@@ -47,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.FiMoreHorizontal className="h-5 w-5 group-hover:text-gray-800" />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
@@ -61,7 +61,7 @@ export function ScheduleListItem({
type="button"
color="warn"
className="w-full font-normal"
StartIcon={isDeleting ? undefined : TrashIcon}
StartIcon={isDeleting ? undefined : Icon.FiTrash}
loading={isDeleting}>
{isDeleting ? t("deleting") : t("delete_schedule")}
</Button>
@@ -49,14 +49,14 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
}, []);
return (
<div className="mt-8 flex flex-col text-center sm:mt-0 sm:w-1/3 sm:pl-4 md:-mb-5">
<div className="mb-4 text-left text-lg font-light text-gray-600">
<span className="text-bookingdarker w-1/2 dark:text-white">
<strong>{nameOfDay(i18n.language, Number(date.format("d")))}</strong>
<span className="text-bookinglight">
{date.format(", D ")}
{date.toDate().toLocaleString(i18n.language, { month: "long" })}
</span>
<div className="mt-8 flex flex-col px-4 text-center sm:mt-0 sm:w-1/3 sm:p-5 md:-mb-4 ">
<div className="mb-4 text-left text-base">
<span className="text-bookingdarker mb-8 w-1/2 break-words font-semibold text-gray-900 dark:text-white">
{nameOfDay(i18n.language, Number(date.format("d")))}
</span>
<span className="text-bookinglight font-medium">
{date.format(", D ")}
{date.toDate().toLocaleString(i18n.language, { month: "long" })}
</span>
</div>
<div className="grid flex-grow grid-cols-2 gap-x-2 overflow-y-auto sm:block md:h-[364px]">
@@ -107,7 +107,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
<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",
"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 text-sm font-medium hover:text-white dark:border-transparent dark:bg-gray-600 dark:text-neutral-200 dark:hover:border-black",
brand === "#fff" || brand === "#ffffff" ? "border-brandcontrast" : "border-brand"
)}
data-testid="time">
+15 -21
View File
@@ -1,13 +1,3 @@
import {
BanIcon,
CheckIcon,
ClockIcon,
LocationMarkerIcon,
PaperAirplaneIcon,
PencilAltIcon,
XIcon,
} from "@heroicons/react/outline";
import { RefreshIcon } from "@heroicons/react/solid";
import { BookingStatus } from "@prisma/client";
import { useRouter } from "next/router";
import { useState } from "react";
@@ -21,6 +11,7 @@ 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";
@@ -101,7 +92,7 @@ function BookingListItem(booking: BookingItemProps) {
onClick: () => {
setRejectionDialogIsOpen(true);
},
icon: BanIcon,
icon: Icon.FiSlash,
disabled: mutation.isLoading,
},
{
@@ -113,7 +104,7 @@ function BookingListItem(booking: BookingItemProps) {
onClick: () => {
mutation.mutate(true);
},
icon: CheckIcon,
icon: Icon.FiCheck,
disabled: mutation.isLoading,
color: "primary",
},
@@ -133,23 +124,23 @@ function BookingListItem(booking: BookingItemProps) {
? "?allRemainingBookings=true"
: ""
}`,
icon: XIcon,
icon: Icon.FiX,
},
{
id: "edit_booking",
label: t("edit_booking"),
icon: PencilAltIcon,
icon: Icon.FiEdit,
actions: [
{
id: "reschedule",
icon: ClockIcon,
icon: Icon.FiClock,
label: t("reschedule_booking"),
href: `/reschedule/${booking.uid}`,
},
{
id: "reschedule_request",
icon: PaperAirplaneIcon,
iconClassName: "rotate-45 w-[18px] -ml-[2px]",
icon: Icon.FiSend,
iconClassName: "rotate-45 w-[16px] -translate-x-0.5 ",
label: t("send_reschedule_request"),
onClick: () => {
setIsOpenRescheduleDialog(true);
@@ -161,7 +152,7 @@ function BookingListItem(booking: BookingItemProps) {
onClick: () => {
setIsOpenLocationDialog(true);
},
icon: LocationMarkerIcon,
icon: Icon.FiMapPin,
},
],
},
@@ -174,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.FiSend className="-mt-[1px] w-4 rotate-45" />
<p className="ml-2 ">{t("reschedule_request_sent")}</p>
</div>
);
@@ -319,8 +310,11 @@ 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" />
<p className="pl-[21px]">
<Icon.FiRefreshCcw
stroke-width="3"
className="float-left mr-1 mt-1.5 inline-block h-3 w-3 text-gray-400"
/>
<p className="mt-1 pl-5 text-xs">
{booking.status === BookingStatus.ACCEPTED
? `${t("event_remaining", {
count: recurringDates.length,
+13 -10
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: {
@@ -39,7 +38,7 @@ export default function CancelBooking(props: Props) {
{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" />
<Icon.FiX 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">
@@ -58,19 +57,23 @@ export default function CancelBooking(props: Props) {
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">
<div className=" flex flex-col-reverse rtl:space-x-reverse sm:flex-row">
{!props.recurringEvent && (
<div className="w-full">
<Button color="secondary" onClick={() => router.push("/reschedule/" + booking?.uid)}>
<div className=" border-bookinglightest mt-5 flex w-full justify-center border-t pt-3 sm:mt-0 sm:justify-start sm:border-0 sm:pt-0">
<Button
color="secondary"
className="border-0 sm:border"
onClick={() => router.push("/reschedule/" + booking?.uid)}>
{t("reschedule_this")}
</Button>
</div>
)}
<div className="w-full space-x-2 text-right">
<div className="mb-2 flex w-full space-x-2 text-right sm:mb-0 ">
<Button color="secondary" onClick={() => props.setIsCancellationMode(false)}>
{t("nevermind")}
</Button>
<Button
className="flex grow justify-center"
data-testid="cancel"
onClick={async () => {
setLoading(true);
+4 -5
View File
@@ -2,8 +2,7 @@ import { FC, useEffect, useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import Switch from "@calcom/ui/Switch";
import TimezoneSelect, { ITimezoneOption } from "@components/ui/form/TimezoneSelect";
import TimezoneSelect, { ITimezoneOption } from "@calcom/ui/form/TimezoneSelect";
import { is24h, timeZone } from "../../lib/clock";
@@ -34,9 +33,9 @@ const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone, timeFormat
};
return selectedTimeZone !== "" ? (
<div className="max-w-80 absolute z-10 w-full rounded-sm border border-gray-200 bg-white px-4 py-2 dark:border-0 dark:bg-gray-700">
<div className="max-w-80 absolute z-10 w-full rounded-sm rounded border border-gray-200 bg-white px-4 pt-4 pb-3 shadow-sm dark:border-0 dark:bg-gray-700">
<div className="mb-4 flex">
<div className="font-medium text-gray-600 dark:text-white">{t("time_options")}</div>
<div className="text-sm font-medium text-gray-600 dark:text-white">{t("time_options")}</div>
<div className="ml-auto flex items-center">
<label className="ltl:mr-3 mr-2 align-text-top text-sm font-medium text-neutral-700 ltr:ml-3 rtl:mr-3 dark:text-white">
{t("am_pm")}
@@ -53,7 +52,7 @@ const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone, timeFormat
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 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,22 +1,7 @@
// 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 { TFunction } from "next-i18next";
import Link from "next/link";
import { useRouter } from "next/router";
import { useEffect, useMemo, useState } from "react";
import { FormattedNumber, IntlProvider } from "react-intl";
@@ -25,30 +10,33 @@ import { z } from "zod";
import { AppStoreLocationType, LocationObject, LocationType } from "@calcom/app-store/locations";
import dayjs, { Dayjs } from "@calcom/dayjs";
import {
useIsEmbed,
useEmbedStyles,
useEmbedNonStylesConfig,
useEmbedStyles,
useIsBackgroundTransparent,
useIsEmbed,
} 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, 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 { 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 AvailableTimes from "@components/booking/AvailableTimes";
import TimeOptions from "@components/booking/TimeOptions";
import EventTypeDescriptionSafeHTML from "@components/eventtype/EventTypeDescriptionSafeHTML";
import { HeadSeo } from "@components/seo/head-seo";
import AvatarGroup from "@components/ui/AvatarGroup";
import PoweredByCal from "@components/ui/PoweredByCal";
@@ -64,6 +52,9 @@ export const locationKeyToString = (location: LocationObject, t: TFunction) => {
case LocationType.InPerson:
return location.address || "In Person"; // If disabled address won't exist on the object
case LocationType.Link:
case LocationType.Ping:
case LocationType.Riverside:
case LocationType.Whereby:
return location.link || "Link"; // If disabled link won't exist on the object
case LocationType.Phone:
return t("your_number");
@@ -97,7 +88,7 @@ const GoBackToPreviousPage = ({ t }: { t: TFunction }) => {
return (
<div className="flex h-full flex-col justify-end">
<button title={t("profile")} onClick={() => router.replace(`${WEBSITE_URL}/${slug}`)}>
<ArrowLeftIcon className="h-4 w-4 text-black transition-opacity hover:cursor-pointer dark:text-white" />
<Icon.FiArrowLeft className="h-4 w-4 text-black transition-opacity hover:cursor-pointer dark:text-white" />
<p className="sr-only">Go Back</p>
</button>
</div>
@@ -204,7 +195,9 @@ const SlotPicker = ({
isLoading={isLoading}
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"
selectedDate
? "border-gray-200 sm:w-1/2 sm:border-r sm:p-4 sm:pr-6 sm:dark:border-gray-700 md:w-1/3 "
: "sm:p-4"
)}
includedDates={Object.keys(slots).filter((k) => slots[k].length > 0)}
locale={isLocaleReady ? i18n.language : "en"}
@@ -269,13 +262,13 @@ 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">
<p className="text-gray-600 dark:text-white">
<GlobeIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<p className="py-1 text-sm font-medium text-gray-600 dark:text-white">
<Icon.FiGlobe className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-500" />
{timeZone}
{isTimeOptionsOpen ? (
<ChevronUpIcon className="ml-1 -mt-1 inline-block h-4 w-4 text-gray-400" />
<Icon.FiChevronUp className="ml-1 -mt-1 inline-block h-4 w-4 text-gray-500" />
) : (
<ChevronDownIcon className="ml-1 -mt-1 inline-block h-4 w-4 text-gray-400" />
<Icon.FiChevronDown className="ml-1 -mt-1 inline-block h-4 w-4 text-gray-500" />
)}
</p>
</Collapsible.Trigger>
@@ -298,10 +291,14 @@ const dateQuerySchema = z.object({
const useRouterQuery = <T extends string>(name: T) => {
const router = useRouter();
const query = z.object({ [name]: z.string().optional() }).parse(router.query);
const existingQueryParams = router.asPath.split("?")[1];
const urlParams = new URLSearchParams(existingQueryParams);
const query = Object.fromEntries(urlParams);
const setQuery = (newValue: string | number | null | undefined) => {
router.replace({ query: { ...router.query, [name]: newValue } }, undefined, { shallow: true });
router.replace({ query: { ...router.query, ...query, [name]: newValue } }, undefined, { shallow: true });
};
return { [name]: query[name], setQuery } as {
@@ -436,16 +433,18 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
<h1 className="text-bookingdark mb-4 break-words text-xl font-semibold dark:text-white">
{eventType.title}
</h1>
<div className="flex flex-col space-y-4">
<div className="flex flex-col space-y-2">
{eventType?.description && (
<p className="text-gray-600 dark:text-white">
<InformationCircleIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
{eventType.description}
</p>
<div className="flex py-1 text-sm font-medium text-gray-600 dark:text-white">
<div>
<Icon.FiInfo className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
</div>
<EventTypeDescriptionSafeHTML eventType={eventType} />
</div>
)}
{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.FiCheckSquare className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-500" />
{t("requires_confirmation")}
</p>
)}
@@ -454,9 +453,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.FiVideo className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-500" />
) : (
<LocationMarkerIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<Icon.FiMapPin className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-500" />
)}
{locationKeyToString(eventType.locations[0], t)}
@@ -465,7 +464,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.FiMapPin className="inline-block h-4 w-4 text-gray-500" />
</div>
<p>
{eventType.locations.map((el, i, arr) => {
@@ -482,12 +481,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.FiClock className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4 text-gray-500" />
{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.FiCreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 dark:text-gray-500" />
<IntlProvider locale="en">
<FormattedNumber
value={eventType.price / 100.0}
@@ -499,7 +498,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.FiRefreshCcw className="float-left mr-[10px] mt-1 ml-[2px] inline-block h-4 w-4 text-gray-500" />
<div className="ml-[27px]">
<p className="mb-1 -ml-2 inline px-2 py-1">
{getRecurringFreq({ t, recurringEvent: eventType.recurringEvent })}
@@ -508,7 +507,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 [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));
@@ -533,7 +532,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
{t("former_time")}
</p>
<p className="text-gray-500 line-through dark:text-white">
<CalendarIcon className="mr-[10px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<CalendarIcon className="mr-[10px] -mt-1 inline-block h-4 w-4 text-gray-500" />
{typeof booking.startTime === "string" &&
parseDate(dayjs(booking.startTime), i18n)}
</p>
@@ -546,10 +545,10 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
</div>
</div>
<div className="p-4 sm:flex sm:py-5">
<div className="overflow-hidden sm:flex">
<div
className={
"hidden overflow-hidden pr-8 sm:border-r sm:dark:border-gray-700 md:flex md:flex-col " +
"hidden overflow-hidden border-gray-200 p-5 sm:border-r sm:dark:border-gray-700 md:flex md:flex-col " +
(isAvailableTimesVisible ? "sm:w-1/3" : recurringEventCount ? "sm:w-2/3" : "sm:w-1/2")
}>
<AvatarGroup
@@ -569,46 +568,46 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
size={10}
truncateAfter={3}
/>
<h2 className="mt-3 break-words font-medium text-gray-500 dark:text-gray-300">
<h2 className="mt-2 break-words text-sm font-medium text-gray-500 dark:text-gray-300">
{profile.name}
</h2>
<h1 className="font-cal mb-4 break-words text-xl font-semibold text-gray-900 dark:text-white">
<h1 className="font-cal mb-6 break-words text-2xl text-gray-900 dark:text-white">
{eventType.title}
</h1>
<div className="flex flex-col space-y-4">
<div className="flex flex-col space-y-2">
{eventType?.description && (
<div className="flex text-gray-600 dark:text-white">
<div className="flex py-1 text-sm font-medium 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.FiInfo className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-500" />
</div>
<p>{eventType.description}</p>
<EventTypeDescriptionSafeHTML eventType={eventType} />
</div>
)}
{eventType?.requiresConfirmation && (
<div className="flex text-gray-600 dark:text-white">
<div className="flex items-center text-sm font-medium 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.FiCheckSquare className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-500" />
</div>
{t("requires_confirmation")}
</div>
)}
{eventType.locations.length === 1 && (
<p className="text-gray-600 dark:text-white">
<p className="text-sm text-gray-600 dark:text-white">
{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.FiVideo className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-500" />
) : (
<LocationMarkerIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<Icon.FiMapPin className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-500" />
)}
{locationKeyToString(eventType.locations[0], t)}
</p>
)}
{eventType.locations.length > 1 && (
<div className="flex-warp flex text-gray-600 dark:text-white">
<div className="flex-warp flex font-medium 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.FiMapPin className="inline-block h-4 w-4 text-gray-500" />
</div>
<p>
{eventType.locations.map((el, i, arr) => {
@@ -616,7 +615,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
<span key={el.type}>
{locationKeyToString(el, t)}{" "}
{arr.length - 1 !== i && (
<span className="font-light"> {t("or_lowercase")} </span>
<span className="text-sm font-light"> {t("or_lowercase")} </span>
)}
</span>
);
@@ -624,13 +623,13 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
</p>
</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" />
<p className="py-1 text-sm font-medium text-gray-600 dark:text-white">
<Icon.FiClock className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4 text-gray-500" />
{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.FiRefreshCcw className="float-left mr-[10px] mt-1 ml-[2px] inline-block h-4 w-4 text-gray-500" />
<div className="ml-[27px]">
<p className="mb-1 -ml-2 inline px-2 py-1">
{getRecurringFreq({ t, recurringEvent: eventType.recurringEvent })}
@@ -639,7 +638,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 [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));
@@ -655,7 +654,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.FiCreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-500" />
<IntlProvider locale="en">
<FormattedNumber
value={eventType.price / 100.0}
@@ -678,7 +677,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
{t("former_time")}
</p>
<p className="text-gray-500 line-through dark:text-white">
<CalendarIcon className="mr-[10px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<CalendarIcon className="mr-[10px] -mt-1 inline-block h-4 w-4 text-gray-500" />
{typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)}
</p>
</div>
@@ -1,19 +1,7 @@
import {
CalendarIcon,
ClockIcon,
CreditCardIcon,
ExclamationCircleIcon,
ExclamationIcon,
InformationCircleIcon,
ClipboardCheckIcon,
RefreshIcon,
} from "@heroicons/react/solid";
import { zodResolver } from "@hookform/resolvers/zod";
import { EventTypeCustomInputType, WorkflowActions } from "@prisma/client";
import { useContracts } from "contexts/contractsContext";
import { isValidPhoneNumber } from "libphonenumber-js";
import { useSession } from "next-auth/react";
import dynamic from "next/dynamic";
import Head from "next/head";
import { useRouter } from "next/router";
import { useEffect, useMemo, useState } from "react";
@@ -24,35 +12,37 @@ import { useMutation } from "react-query";
import { v4 as uuidv4 } from "uuid";
import { z } from "zod";
import { createPaymentLink } from "@calcom/app-store/stripepayment/lib/client";
import dayjs from "@calcom/dayjs";
import {
useEmbedNonStylesConfig,
useIsBackgroundTransparent,
useIsEmbed,
} 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 { useLocale } from "@calcom/lib/hooks/useLocale";
import useTheme from "@calcom/lib/hooks/useTheme";
import { HttpError } from "@calcom/lib/http-error";
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
import { createPaymentLink } from "@calcom/stripe/client";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { Button } from "@calcom/ui/Button";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
import PhoneInput from "@calcom/ui/form/PhoneInputLazy";
import { EmailInput, Form } from "@calcom/ui/form/fields";
import { asStringOrNull } from "@lib/asStringOrNull";
import { timeZone } from "@lib/clock";
import { ensureArray } from "@lib/ensureArray";
import useTheme from "@lib/hooks/useTheme";
import { LocationObject, LocationType } from "@lib/location";
import createBooking from "@lib/mutations/bookings/create-booking";
import createRecurringBooking from "@lib/mutations/bookings/create-recurring-booking";
import { parseDate, parseRecurringDates } from "@lib/parseDate";
import slugify from "@lib/slugify";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
import AvatarGroup from "@components/ui/AvatarGroup";
import type PhoneInputType from "@components/ui/form/PhoneInput";
import { BookPageProps } from "../../../pages/[user]/book";
import { HashLinkPageProps } from "../../../pages/d/[link]/book";
@@ -67,11 +57,6 @@ declare global {
};
}
/** These are like 40kb that not every user needs */
const PhoneInput = dynamic(
() => import("@components/ui/form/PhoneInput")
) as unknown as typeof PhoneInputType;
type BookingPageProps = (BookPageProps | TeamBookingPageProps | HashLinkPageProps) & {
locationLabels: Record<LocationType, string>;
};
@@ -233,11 +218,25 @@ const BookingPage = ({
? booking?.attendees.slice(1).map((attendee) => attendee.email)
: [];
// There should only exists one default userData variable for primaryAttendee.
const defaultUserValues = {
email: booking?.attendees[0].email
? booking.attendees[0].email
: router.query.email
? (router.query.email as string)
: "",
name: booking?.attendees[0].name
? booking.attendees[0].name
: router.query.name
? (router.query.name as string)
: "",
};
const defaultValues = () => {
if (!rescheduleUid) {
return {
name: (router.query.name as string) || (!loggedInIsOwner && session?.user?.name) || "",
email: (router.query.email as string) || (!loggedInIsOwner && session?.user?.email) || "",
name: defaultUserValues.name || (!loggedInIsOwner && session?.user?.name) || "",
email: defaultUserValues.email || (!loggedInIsOwner && session?.user?.email) || "",
notes: (router.query.notes as string) || "",
guests: ensureArray(router.query.guest) as string[],
customInputs: eventType.customInputs.reduce(
@@ -259,8 +258,8 @@ const BookingPage = ({
const customInputType = booking.customInputs;
return {
name: primaryAttendee.name || "",
email: primaryAttendee.email || "",
name: defaultUserValues.name,
email: defaultUserValues.email || "",
guests: guestListEmails,
notes: booking.description || "",
rescheduleReason: "",
@@ -335,6 +334,9 @@ const BookingPage = ({
case LocationType.Whereby: {
return locationInfo(locationType)?.link || "";
}
case LocationType.Ping: {
return locationInfo(locationType)?.link || "";
}
// Catches all other location types, such as Google Meet, Zoom etc.
default:
return selectedLocation || "";
@@ -443,10 +445,11 @@ const BookingPage = ({
}
};
const disableInput = !!rescheduleUid;
// Should be disabled when rescheduleUid is present and data was found in defaultUserValues name/email fields.
const disableInput = !!rescheduleUid && !!defaultUserValues.email && !!defaultUserValues.name;
const disabledExceptForOwner = disableInput && !loggedInIsOwner;
const inputClassName =
"focus:border-brand block w-full rounded-sm border-gray-300 focus:ring-black disabled:bg-gray-200 disabled:hover:cursor-not-allowed dark:border-gray-900 dark:bg-gray-700 dark:text-white dark:selection:bg-green-500 disabled:dark:text-gray-500 sm:text-sm";
"focus:border-brand block w-full rounded-sm border-gray-300 focus:ring-black disabled:bg-gray-200 disabled:hover:cursor-not-allowed dark:border-gray-900 dark:bg-gray-700 dark:text-white dark:selection:bg-green-500 disabled:dark:text-gray-500 text-sm";
let isSmsReminderNumberNeeded = false;
@@ -494,8 +497,8 @@ const BookingPage = ({
isBackgroundTransparent ? "" : "dark:border-1 bg-white dark:bg-gray-800",
"rounded-md dark:border-gray-600 sm:border"
)}>
<div className="px-4 py-5 sm:flex sm:p-4">
<div className="sm:w-1/2 sm:border-r sm:dark:border-gray-700">
<div className="sm:flex">
<div className="px-6 pt-6 pb-0 sm:w-1/2 sm:border-r sm:pb-6 sm:dark:border-gray-700">
<AvatarGroup
border="border-2 border-white dark:border-gray-800"
size={14}
@@ -533,24 +536,24 @@ const BookingPage = ({
</p>
)}
{eventType?.description && (
<p className="text-bookinglight mb-2 dark:text-white">
<InformationCircleIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<p className="text-bookinglight mb-2 text-sm dark:text-white">
<Icon.FiInfo className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
{eventType.description}
</p>
)}
{eventType?.requiresConfirmation && (
<p className="text-bookinglight mb-2 dark:text-white">
<ClipboardCheckIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<p className="text-bookinglight mb-2 text-sm dark:text-white">
<Icon.FiClipboard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
{t("requires_confirmation")}
</p>
)}
<p className="text-bookinglight mb-2 dark:text-white">
<ClockIcon className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
<p className="text-bookinglight mb-2 text-sm dark:text-white">
<Icon.FiClock className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
{eventType.length} {t("minutes")}
</p>
{eventType.price > 0 && (
<p className="text-bookinglight mb-1 -ml-2 px-2 py-1 dark:text-white">
<CreditCardIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
<p className="text-bookinglight mb-1 -ml-2 px-2 py-1 text-sm dark:text-white">
<Icon.FiCreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
<IntlProvider locale="en">
<FormattedNumber
value={eventType.price / 100.0}
@@ -561,8 +564,8 @@ const BookingPage = ({
</p>
)}
{!rescheduleUid && eventType.recurringEvent?.freq && recurringEventCount && (
<div className="mb-3 text-gray-600 dark:text-white">
<RefreshIcon className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
<div className="mb-3 text-sm text-gray-600 dark:text-white">
<Icon.FiRefreshCw className="mr-[10px] -mt-1 ml-[2px] inline-block h-4 w-4 text-gray-400" />
<p className="mb-1 -ml-2 inline px-2 py-1">
{getEveryFreqFor({
t,
@@ -572,9 +575,9 @@ const BookingPage = ({
</p>
</div>
)}
<div className="text-bookinghighlight mb-4 flex">
<CalendarIcon className="mr-[10px] ml-[2px] inline-block h-4 w-4" />
<div className="-mt-1">
<div className="text-bookinghighlight mb-4 flex items-center text-sm">
<Icon.FiCalendar className="mr-[10px] ml-[2px] inline-block h-4 w-4" />
<div>
{(rescheduleUid || !eventType.recurringEvent?.freq) &&
parseDate(dayjs(date).tz(timeZone()), i18n)}
{!rescheduleUid &&
@@ -586,7 +589,7 @@ const BookingPage = ({
content={recurringStrings.slice(5).map((aDate, key) => (
<p key={key}>{aDate}</p>
))}>
<p className="text-gray-600 dark:text-white">
<p className="text-sm text-gray-600 dark:text-white">
{t("plus_more", { count: recurringStrings.length - 5 })}
</p>
</Tooltip>
@@ -601,17 +604,17 @@ const BookingPage = ({
)}
{booking?.startTime && rescheduleUid && (
<div>
<p className="mt-8 mb-2 text-gray-600 dark:text-white" data-testid="former_time_p">
<p className="mt-8 mb-2 text-sm text-gray-600 dark:text-white" data-testid="former_time_p">
{t("former_time")}
</p>
<p className="text-gray-500 line-through dark:text-white">
<CalendarIcon className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
<Icon.FiCalendar className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4 text-gray-400" />
{typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)}
</p>
</div>
)}
</div>
<div className="sm:w-1/2 sm:pl-8 sm:pr-4">
<div className="p-6 sm:w-1/2">
<Form form={bookingForm} handleSubmit={bookEvent}>
<div className="mb-4">
<label htmlFor="name" className="block text-sm font-medium text-gray-700 dark:text-white">
@@ -650,7 +653,7 @@ const BookingPage = ({
/>
{bookingForm.formState.errors.email && (
<div className="mt-2 flex items-center text-sm text-red-700 ">
<ExclamationCircleIcon className="mr-2 h-3 w-3" />
<Icon.FiInfo className="mr-2 h-3 w-3" />
<p>{t("email_validation_error")}</p>
</div>
)}
@@ -696,7 +699,7 @@ const BookingPage = ({
</div>
{bookingForm.formState.errors.phone && (
<div className="mt-2 flex items-center text-sm text-red-700 ">
<ExclamationCircleIcon className="mr-2 h-3 w-3" />
<Icon.FiInfo className="mr-2 h-3 w-3" />
<p>{t("invalid_number")}</p>
</div>
)}
@@ -854,7 +857,7 @@ const BookingPage = ({
</div>
{bookingForm.formState.errors.smsReminderNumber && (
<div className="mt-2 flex items-center text-sm text-red-700 ">
<ExclamationCircleIcon className="mr-2 h-3 w-3" />
<Icon.FiInfo className="mr-2 h-3 w-3" />
<p>{t("invalid_number")}</p>
</div>
)}
@@ -874,7 +877,6 @@ const BookingPage = ({
rows={3}
className={inputClassName}
placeholder={t("reschedule_placeholder")}
disabled={disabledExceptForOwner}
/>
) : (
<textarea
@@ -922,7 +924,7 @@ function ErrorMessage({ error }: { error: unknown }) {
<div data-testid="booking-fail" className="mt-2 border-l-4 border-yellow-400 bg-yellow-50 p-4">
<div className="flex">
<div className="flex-shrink-0">
<ExclamationIcon className="h-5 w-5 text-yellow-400" aria-hidden="true" />
<Icon.FiAlertTriangle className="h-5 w-5 text-yellow-400" aria-hidden="true" />
</div>
<div className="ltr:ml-3 rtl:mr-3">
<p className="text-sm text-yellow-700">
@@ -1,11 +1,10 @@
import { ExclamationIcon } from "@heroicons/react/outline";
import { CheckIcon } from "@heroicons/react/solid";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import React, { PropsWithChildren } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button } from "@calcom/ui/Button";
import { DialogClose, DialogContent } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
export type DeleteStripeDialogContentProps = {
cancelAllBookingsBtnText?: string;
@@ -37,17 +36,17 @@ export default function DeleteStripeDialogContent(props: PropsWithChildren<Delet
<div className="mt-0.5 ltr:mr-3">
{variety === "danger" && (
<div className="mx-auto rounded-full bg-red-100 p-2 text-center">
<ExclamationIcon className="h-5 w-5 text-red-600" />
<Icon.FiAlertTriangle className="h-5 w-5 text-red-600" />
</div>
)}
{variety === "warning" && (
<div className="mx-auto rounded-full bg-orange-100 p-2 text-center">
<ExclamationIcon className="h-5 w-5 text-orange-600" />
<Icon.FiAlertTriangle className="h-5 w-5 text-orange-600" />
</div>
)}
{variety === "success" && (
<div className="mx-auto rounded-full bg-green-100 p-2 text-center">
<CheckIcon className="h-5 w-5 text-green-600" />
<Icon.FiCheck className="h-5 w-5 text-green-600" />
</div>
)}
</div>
@@ -1,31 +1,25 @@
import { LocationMarkerIcon } from "@heroicons/react/solid";
import { zodResolver } from "@hookform/resolvers/zod";
import { isValidPhoneNumber } from "libphonenumber-js";
import dynamic from "next/dynamic";
import { useEffect } from "react";
import { Controller, useForm, useWatch } from "react-hook-form";
import { z } from "zod";
import classNames from "@calcom/lib/classNames";
import { LocationOptionsToString } from "@calcom/app-store/locations";
import { LocationType } from "@calcom/core/location";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
import { Button } from "@calcom/ui";
import { Dialog, DialogContent } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import PhoneInput from "@calcom/ui/form/PhoneInputLazy";
import { Form } from "@calcom/ui/form/fields";
import { QueryCell } from "@lib/QueryCell";
import { linkValueToString } from "@lib/linkValueToString";
import { LocationType } from "@lib/location";
import { LocationOptionsToString } from "@lib/locationOptions";
import CheckboxField from "@components/ui/form/CheckboxField";
import type PhoneInputType from "@components/ui/form/PhoneInput";
import Select from "@components/ui/form/Select";
const PhoneInput = dynamic(
() => import("@components/ui/form/PhoneInput")
) as unknown as typeof PhoneInputType;
type BookingItem = inferQueryOutput<"viewer.bookings">["bookings"][number];
type OptionTypeBase = {
@@ -91,6 +85,11 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
.string()
.regex(/^http(s)?:\/\/(www\.)?around.co\/[a-zA-Z0-9]*/)
.optional()
: selection?.value === LocationType.Ping
? z
.string()
.regex(/^http(s)?:\/\/(www\.)?ping.gg\/call\/[a-zA-Z0-9]*/)
.optional()
: selection?.value === LocationType.Riverside
? z
.string()
@@ -173,7 +172,7 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
{...locationFormMethods.register("locationLink")}
required
id="link"
className="block w-full rounded-sm border-gray-300 sm:text-sm"
className="block w-full rounded-sm border-gray-300 text-sm"
defaultValue={
defaultValues
? defaultValues.find(
@@ -330,6 +329,54 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
)}
</div>
</>
) : selectedLocation === LocationType.Ping ? (
<>
<div>
<label htmlFor="address" className="block text-sm font-medium text-gray-700">
{t("set_ping_link")}
</label>
<div className="mt-1">
<input
type="text"
{...locationFormMethods.register("locationLink")}
id="pinglink"
placeholder="https://www.ping.gg/call/theo"
required
className="block w-full rounded-sm border-gray-300 text-sm"
defaultValue={
defaultValues
? defaultValues.find(
(location: { type: LocationType }) => location.type === LocationType.Ping
)?.address
: undefined
}
/>
</div>
{!booking && (
<div className="mt-3">
<Controller
name="displayLocationPublicly"
control={locationFormMethods.control}
render={() => (
<CheckboxField
defaultChecked={
defaultValues
? defaultValues.find((location) => location.type === LocationType.Ping)
?.displayLocationPublicly
: undefined
}
description={t("display_location_label")}
onChange={(e) =>
locationFormMethods.setValue("displayLocationPublicly", e.target.checked)
}
informationIconText={t("display_location_info_badge")}
/>
)}
/>
</div>
)}
</div>
</>
) : selectedLocation === LocationType.Riverside ? (
<>
<div>
@@ -388,7 +435,7 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
<div className="inline-block transform rounded-sm bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6 sm:align-middle">
<div className="mb-4 sm:flex sm:items-start">
<div className="bg-secondary-100 mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full sm:mx-0 sm:h-10 sm:w-10">
<LocationMarkerIcon className="text-primary-600 h-6 w-6" />
<Icon.FiMapPin className="text-primary-600 h-6 w-6" />
</div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
@@ -422,7 +469,8 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
newLocation === LocationType.Link ||
newLocation === LocationType.Whereby ||
newLocation === LocationType.Around ||
newLocation === LocationType.Riverside
newLocation === LocationType.Riverside ||
newLocation === LocationType.Ping
) {
details = { link: values.locationLink, displayLocationPublicly };
}
@@ -460,7 +508,7 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
: locationOptions
}
isSearchable={false}
className="my-4 block w-full min-w-0 flex-1 rounded-sm border border-gray-300 sm:text-sm"
className="my-4 block w-full min-w-0 flex-1 rounded-sm border border-gray-300 text-sm"
onChange={(val) => {
if (val) {
locationFormMethods.setValue("locationType", val.value);
@@ -1,4 +1,3 @@
import { ClockIcon } from "@heroicons/react/outline";
import { RescheduleResponse } from "pages/api/book/request-reschedule";
import React, { useState, Dispatch, SetStateAction } from "react";
import { useMutation } from "react-query";
@@ -8,6 +7,7 @@ import showToast from "@calcom/lib/notification";
import { 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 { TextArea } from "@calcom/ui/form/fields";
import * as fetchWrapper from "@lib/core/http/fetch-wrapper";
@@ -58,7 +58,7 @@ export const RescheduleDialog = (props: IRescheduleDialog) => {
<DialogContent>
<div className="flex flex-row space-x-3">
<div className="flex h-10 w-10 flex-shrink-0 justify-center rounded-full bg-[#FAFAFA]">
<ClockIcon className="m-auto h-6 w-6" />
<Icon.FiClock className="m-auto h-6 w-6" />
</div>
<div className="pt-1">
<DialogHeader title={t("send_reschedule_request")} />
@@ -1,4 +1,3 @@
import { ChevronDownIcon, PlusIcon } from "@heroicons/react/solid";
import { zodResolver } from "@hookform/resolvers/zod";
import { SchedulingType } from "@prisma/client";
import { useSession } from "next-auth/react";
@@ -23,6 +22,7 @@ import Dropdown, {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
import { Form, InputLeading, TextAreaField, TextField } from "@calcom/ui/form/fields";
import { HttpError } from "@lib/core/http/error";
@@ -286,14 +286,14 @@ export function CreateEventTypeTrigger(props: CreateEventTypeTrigger) {
<Button
onClick={() => props.openModal(props.options[0])}
data-testid="new-event-type"
StartIcon={PlusIcon}
StartIcon={Icon.FiPlus}
disabled={!props.canAddEvents}>
{t("new_event_type_btn")}
</Button>
) : (
<Dropdown>
<DropdownMenuTrigger asChild>
<Button EndIcon={ChevronDownIcon}>{t("new_event_type_btn")}</Button>
<Button EndIcon={Icon.FiChevronDown}>{t("new_event_type_btn")}</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>{t("new_event_subtitle")}</DropdownMenuLabel>
@@ -1,11 +1,3 @@
import {
ClipboardCheckIcon,
ClockIcon,
CreditCardIcon,
RefreshIcon,
UserIcon,
UsersIcon,
} from "@heroicons/react/solid";
import { Prisma, SchedulingType } from "@prisma/client";
import { useMemo } from "react";
import { FormattedNumber, IntlProvider } from "react-intl";
@@ -13,6 +5,7 @@ import { FormattedNumber, IntlProvider } from "react-intl";
import { parseRecurringEvent } from "@calcom/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { baseEventTypeSelect } from "@calcom/prisma/selects";
import { Icon } from "@calcom/ui/Icon";
import classNames from "@lib/classNames";
@@ -44,34 +37,34 @@ export const EventTypeDescription = ({ eventType, className }: EventTypeDescript
{eventType.description.length > 100 && "..."}
</h2>
)}
<ul className="mt-2 flex flex-wrap sm:flex-nowrap">
<li className="mr-4 mb-1 flex items-center whitespace-nowrap">
<ClockIcon className="mr-1.5 inline h-4 w-4 text-neutral-400" aria-hidden="true" />
{eventType.length}m
<ul className="mt-2 flex flex-wrap space-x-1 sm:flex-nowrap ">
<li className="mb-1 flex items-center whitespace-nowrap rounded-sm bg-gray-100 px-1 py-px text-xs text-gray-800 dark:bg-gray-900 dark:text-white">
<Icon.FiClock className="mr-1.5 inline h-3 w-3" aria-hidden="true" />
{eventType.length} {t("minutes")}
</li>
{eventType.schedulingType ? (
<li className="mr-4 mb-1 flex items-center whitespace-nowrap">
<UsersIcon className="mr-1.5 inline h-4 w-4 text-neutral-400" aria-hidden="true" />
<li className="mb-1 flex items-center whitespace-nowrap rounded-sm bg-gray-100 px-1 py-px text-xs text-gray-800 dark:bg-gray-900 dark:text-white">
<Icon.FiUsers className="mr-1.5 inline h-3 w-3" aria-hidden="true" />
{eventType.schedulingType === SchedulingType.ROUND_ROBIN && t("round_robin")}
{eventType.schedulingType === SchedulingType.COLLECTIVE && t("collective")}
</li>
) : (
<li className="mr-4 mb-1 flex items-center whitespace-nowrap">
<UserIcon className="mr-1.5 inline h-4 w-4 text-neutral-400" aria-hidden="true" />
<li className="mb-1 flex items-center whitespace-nowrap rounded-sm bg-gray-100 px-1 py-px text-xs text-gray-800 dark:bg-gray-900 dark:text-white">
<Icon.FiUser className="mr-1.5 inline h-3 w-3" aria-hidden="true" />
{t("1_on_1")}
</li>
)}
{recurringEvent?.count && recurringEvent.count > 0 && (
<li className="mr-4 mb-1 flex items-center whitespace-nowrap">
<RefreshIcon className="mr-1.5 inline h-4 w-4 text-neutral-400" aria-hidden="true" />
<li className="mb-1 flex items-center whitespace-nowrap rounded-sm bg-gray-100 px-1 py-px text-xs text-gray-800 dark:bg-gray-900 dark:text-white">
<Icon.FiRefreshCw className="mr-1.5 inline h-3 w-3" aria-hidden="true" />
{t("repeats_up_to", {
count: recurringEvent.count,
})}
</li>
)}
{eventType.price > 0 && (
<li className="mr-4 mb-1 flex items-center whitespace-nowrap">
<CreditCardIcon className="mr-1.5 inline h-4 w-4 text-neutral-400" aria-hidden="true" />
<li className="mb-1 flex items-center whitespace-nowrap rounded-sm bg-gray-100 px-1 py-px text-xs text-gray-800 dark:bg-gray-900 dark:text-white">
<Icon.FiCreditCard className="mr-1.5 inline h-3 w-3" aria-hidden="true" />
<IntlProvider locale="en">
<FormattedNumber
value={eventType.price / 100.0}
@@ -82,8 +75,8 @@ export const EventTypeDescription = ({ eventType, className }: EventTypeDescript
</li>
)}
{eventType.requiresConfirmation && (
<li className="mr-4 mb-1 flex items-center whitespace-nowrap">
<ClipboardCheckIcon className="mr-1.5 inline h-4 w-4 text-neutral-400" aria-hidden="true" />
<li className="mb-1 flex items-center whitespace-nowrap rounded-sm bg-gray-100 px-1 py-px text-xs text-gray-800 dark:bg-gray-900 dark:text-white">
<Icon.FiCheckSquare className="mr-1.5 inline h-3 w-3" aria-hidden="true" />
{t("requires_confirmation")}
</li>
)}
@@ -0,0 +1,12 @@
export type EventTypeDescriptionSafeProps = {
eventType: { description: string | null };
};
export const EventTypeDescriptionSafeHTML = ({ eventType }: EventTypeDescriptionSafeProps) => {
const props: JSX.IntrinsicElements["div"] = { suppressHydrationWarning: true };
// @ts-expect-error: @see packages/prisma/middleware/eventTypeDescriptionParseAndSanitize.ts
if (eventType.description) props.dangerouslySetInnerHTML = { __html: eventType.descriptionAsSafeHTML };
return <div {...props} />;
};
export default EventTypeDescriptionSafeHTML;
@@ -4,7 +4,7 @@ import { UseFormReturn } from "react-hook-form";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Frequency } from "@calcom/prisma/zod-utils";
import { RecurringEvent } from "@calcom/types/Calendar";
import type { RecurringEvent } from "@calcom/types/Calendar";
import { Alert } from "@calcom/ui/Alert";
import Select from "@components/ui/form/Select";
@@ -82,7 +82,7 @@ export default function RecurringEventController({
type="number"
min="1"
max="20"
className="block w-16 rounded-sm border-gray-300 [appearance:textfield] ltr:mr-2 rtl:ml-2 sm:text-sm"
className="block w-16 rounded-sm border-gray-300 text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2"
defaultValue={recurringEventState.interval}
onChange={(event) => {
const newVal = {
@@ -97,7 +97,7 @@ export default function RecurringEventController({
options={recurringEventFreqOptions}
value={recurringEventFreqOptions[recurringEventState.freq]}
isSearchable={false}
className="w-18 block min-w-0 rounded-sm sm:text-sm"
className="w-18 block min-w-0 rounded-sm text-sm"
onChange={(event) => {
const newVal = {
...recurringEventState,
@@ -114,7 +114,7 @@ export default function RecurringEventController({
type="number"
min="1"
max="20"
className="block w-16 rounded-sm border-gray-300 [appearance:textfield] ltr:mr-2 rtl:ml-2 sm:text-sm"
className="block w-16 rounded-sm border-gray-300 text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2"
defaultValue={recurringEventState.count}
onChange={(event) => {
const newVal = {
@@ -1,8 +1,7 @@
import { LinkIcon } from "@heroicons/react/outline";
import { ClockIcon, DotsHorizontalIcon, ExternalLinkIcon, UserIcon } from "@heroicons/react/solid";
import React from "react";
import { SkeletonAvatar, SkeletonContainer, SkeletonText } from "@calcom/ui";
import { Icon } from "@calcom/ui/Icon";
function SkeletonLoader() {
return (
@@ -35,11 +34,11 @@ function SkeletonItem() {
<div className="">
<ul className="mt-2 flex space-x-4 rtl:space-x-reverse ">
<li className="flex items-center whitespace-nowrap">
<ClockIcon className="mt-0.5 mr-1.5 inline h-4 w-4 text-gray-200" />
<Icon.FiClock className="mt-0.5 mr-1.5 inline h-4 w-4 text-gray-200" />
<SkeletonText width="12" height="4" />
</li>
<li className="flex items-center whitespace-nowrap">
<UserIcon className="mt-0.5 mr-1.5 inline h-4 w-4 text-gray-200" />
<Icon.FiUser className="mt-0.5 mr-1.5 inline h-4 w-4 text-gray-200" />
<SkeletonText width="16" height="4" />
</li>
</ul>
@@ -47,14 +46,14 @@ function SkeletonItem() {
</div>
<div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 sm:flex">
<div className="flex justify-between rtl:space-x-reverse">
<div className="btn-icon appearance-none">
<ExternalLinkIcon className="h-5 w-5" />
<div className="cursor-pointer appearance-none rounded-sm border border-transparent p-2 text-neutral-500 hover:border-gray-300 hover:text-neutral-900">
<Icon.FiExternalLink className="h-5 w-5" />
</div>
<div className="btn-icon appearance-none">
<LinkIcon className="h-5 w-5" />
<div className="cursor-pointer appearance-none rounded-sm border border-transparent p-2 text-neutral-500 hover:border-gray-300 hover:text-neutral-900">
<Icon.FiLink className="h-5 w-5" />
</div>
<div className="btn-icon appearance-none">
<DotsHorizontalIcon className="h-5 w-5" />
<div className="cursor-pointer appearance-none rounded-sm border border-transparent p-2 text-neutral-500 hover:border-gray-300 hover:text-neutral-900">
<Icon.FiMoreHorizontal className="h-5 w-5" />
</div>
</div>
</div>
@@ -7,15 +7,15 @@ import showToast from "@calcom/lib/notification";
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
import { Alert } from "@calcom/ui/Alert";
import Button from "@calcom/ui/Button";
import { List } from "@calcom/ui/List";
import { ShellSubHeading } from "@calcom/ui/Shell";
import Switch from "@calcom/ui/Switch";
import SkeletonLoader from "@calcom/ui/apps/SkeletonLoader";
import { QueryCell } from "@lib/QueryCell";
import AdditionalCalendarSelector from "@components/AdditionalCalendarSelector";
import DestinationCalendarSelector from "@components/DestinationCalendarSelector";
import { List } from "@components/List";
import { ShellSubHeading } from "@components/Shell";
import SkeletonLoader from "@components/apps/SkeletonLoader";
import DisconnectIntegration from "./DisconnectIntegration";
import IntegrationListItem from "./IntegrationListItem";
@@ -1,14 +1,12 @@
import { useState } from "react";
import { useMutation } from "react-query";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { trpc } from "@calcom/trpc/react";
import { ButtonBaseProps } from "@calcom/ui/Button";
import ConfirmationDialogContent from "@calcom/ui/ConfirmationDialogContent";
import { Dialog } from "@calcom/ui/Dialog";
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
export default function DisconnectIntegration(props: {
/** Integration credential id */
id: number;
@@ -1,9 +1,9 @@
import Link from "next/link";
import { ReactNode } from "react";
import classNames from "@lib/classNames";
import { ListItem, ListItemText, ListItemTitle } from "@calcom/ui/List";
import { ListItem, ListItemText, ListItemTitle } from "@components/List";
import classNames from "@lib/classNames";
function IntegrationListItem(props: {
imageSrc?: string;
@@ -1,6 +1,6 @@
import { ReactNode } from "react";
import Badge from "@components/ui/Badge";
import Badge from "@calcom/ui/Badge";
function pluralize(opts: { num: number; plural: string; singular: string }) {
if (opts.num === 0) {
@@ -56,7 +56,7 @@ const CustomInputTypeForm: FC<Props> = (props) => {
defaultValue={selectedInputOption}
options={inputOptions}
isSearchable={false}
className="mt-1 mb-2 block w-full min-w-0 flex-1 sm:text-sm"
className="mt-1 mb-2 block w-full min-w-0 flex-1 text-sm"
onChange={(option) => option && field.onChange(option.value)}
value={selectedInputOption}
onBlur={field.onBlur}
@@ -74,7 +74,7 @@ const ChangePasswordSection = () => {
name="current_password"
id="current_password"
required
className="block w-full rounded-sm border-gray-300 sm:text-sm"
className="block w-full rounded-sm border-gray-300 text-sm"
placeholder={t("your_old_password")}
/>
</div>
@@ -91,7 +91,7 @@ const ChangePasswordSection = () => {
value={newPassword}
required
onInput={(e) => setNewPassword(e.currentTarget.value)}
className="block w-full rounded-sm border-gray-300 sm:text-sm"
className="block w-full rounded-sm border-gray-300 text-sm"
placeholder={t("super_secure_new_password")}
/>
</div>
@@ -70,7 +70,7 @@ const DisableTwoFactorAuthModal = ({ onDisable, onCancel }: DisableTwoFactorAuth
required
value={password}
onInput={(e) => setPassword(e.currentTarget.value)}
className="block w-full rounded-sm border-gray-300 sm:text-sm"
className="block w-full rounded-sm border-gray-300 text-sm"
/>
</div>
@@ -1,10 +1,9 @@
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { trpc } from "@calcom/trpc/react";
import Badge from "@calcom/ui/Badge";
import Button from "@calcom/ui/Button";
import Badge from "@components/ui/Badge";
const DisableUserImpersonation = ({ disableImpersonation }: { disableImpersonation: boolean }) => {
const utils = trpc.useContext();
@@ -139,7 +139,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
required
value={password}
onInput={(e) => setPassword(e.currentTarget.value)}
className="block w-full rounded-sm border-gray-300 sm:text-sm"
className="block w-full rounded-sm border-gray-300 text-sm"
/>
</div>
@@ -175,7 +175,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
minLength={6}
inputMode="numeric"
onInput={(e) => setTotpCode(e.currentTarget.value)}
className="block w-full rounded-sm border-gray-300 sm:text-sm"
className="block w-full rounded-sm border-gray-300 text-sm"
autoComplete="one-time-code"
/>
</div>
@@ -1,11 +1,10 @@
import { useState } from "react";
import Badge from "@calcom/ui/Badge";
import Button from "@calcom/ui/Button";
import { useLocale } from "@lib/hooks/useLocale";
import Badge from "@components/ui/Badge";
import DisableTwoFactorModal from "./DisableTwoFactorModal";
import EnableTwoFactorModal from "./EnableTwoFactorModal";
@@ -1,6 +1,7 @@
import { ShieldCheckIcon } from "@heroicons/react/solid";
import React from "react";
import { ShieldCheckIcon } from "@calcom/ui/Icon";
const TwoFactorModalHeader = ({ title, description }: { title: string; description: string }) => {
return (
<div className="mb-4 sm:flex sm:items-start">
@@ -1,10 +1,9 @@
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { trpc } from "@calcom/trpc/react";
import Badge from "@calcom/ui/Badge";
import Button from "@calcom/ui/Button";
import Badge from "@components/ui/Badge";
const DisableTeamImpersonation = ({ teamId, memberId }: { teamId: number; memberId: number }) => {
const { t } = useLocale();
@@ -90,7 +90,7 @@ export default function MemberChangeRoleModal(props: {
value={role}
onChange={(option) => option && setRole(option)}
id="role"
className="mt-1 block w-full rounded-md border-gray-300 sm:text-sm"
className="mt-1 block w-full rounded-md border-gray-300 text-sm"
/>
</div>
{errorMessage && (
@@ -1,5 +1,3 @@
import { UserIcon } from "@heroicons/react/outline";
import { InformationCircleIcon } from "@heroicons/react/solid";
import { MembershipRole } from "@prisma/client";
import React, { useState, SyntheticEvent, useMemo } from "react";
@@ -7,6 +5,7 @@ import { TeamWithMembers } from "@calcom/lib/server/queries/teams";
import { trpc } from "@calcom/trpc/react";
import Button from "@calcom/ui/Button";
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { TextField } from "@calcom/ui/form/fields";
import { useLocale } from "@lib/hooks/useLocale";
@@ -75,7 +74,7 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps)
<DialogContent>
<div className="mb-4 sm:flex sm:items-start">
<div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10">
<UserIcon className="text-brandcontrast h-6 w-6" />
<Icon.FiUser className="text-brandcontrast h-6 w-6" />
</div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
@@ -104,7 +103,7 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps)
options={props.currentMember !== MembershipRole.OWNER ? options.slice(0, 2) : options}
id="role"
name="role"
className="mt-1 block w-full rounded-sm border-gray-300 sm:text-sm"
className="mt-1 block w-full rounded-sm border-gray-300 text-sm"
/>
</div>
<div className="relative flex items-start">
@@ -114,7 +113,7 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps)
name="sendInviteEmail"
defaultChecked
id="sendInviteEmail"
className="rounded-sm border-gray-300 text-black sm:text-sm"
className="rounded-sm border-gray-300 text-sm text-black"
/>
</div>
<div className="text-sm ltr:ml-2 rtl:mr-2">
@@ -124,7 +123,7 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps)
</div>
</div>
<div className="flex flex-row rounded-sm bg-gray-50 px-3 py-2">
<InformationCircleIcon className="h-5 w-5 flex-shrink-0 fill-gray-400" aria-hidden="true" />
<Icon.FiInfo className="h-5 w-5 flex-shrink-0 fill-gray-400" aria-hidden="true" />
<span className="ml-2 text-sm leading-tight text-gray-500">
Note: This will cost an extra seat ($12/m) on your subscription if this invitee does not have
a pro account.{" "}
+12 -13
View File
@@ -1,15 +1,15 @@
import { LockClosedIcon, PencilIcon, UserRemoveIcon } from "@heroicons/react/outline";
import { ClockIcon, DotsHorizontalIcon, ExternalLinkIcon } from "@heroicons/react/solid";
import { MembershipRole } from "@prisma/client";
import { signIn } from "next-auth/react";
import Link from "next/link";
import { useState } from "react";
import TeamAvailabilityModal from "@calcom/features/ee/teams/components/TeamAvailabilityModal";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
import Button from "@calcom/ui/Button";
import ConfirmationDialogContent from "@calcom/ui/ConfirmationDialogContent";
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
import Dropdown, {
DropdownMenuContent,
@@ -17,12 +17,11 @@ import Dropdown, {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
import TeamAvailabilityModal from "@ee/components/team/availability/TeamAvailabilityModal";
import useCurrentUserId from "@lib/hooks/useCurrentUserId";
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
import Avatar from "@components/ui/Avatar";
import ModalContainer from "@components/ui/ModalContainer";
@@ -101,7 +100,7 @@ export default function MemberListItem(props: Props) {
{props.member.role && <TeamRole role={props.member.role} />}
</div>
</div>
<div className="flex">
<div className="flex space-x-2">
<Tooltip side="top" content={t("team_view_user_availability")}>
<Button
// Disabled buttons don't trigger Tooltips
@@ -113,19 +112,19 @@ export default function MemberListItem(props: Props) {
disabled={!props.member.accepted}
onClick={() => (props.member.accepted ? setShowTeamAvailabilityModal(true) : null)}
color="minimal"
className="group hidden h-10 w-10 items-center justify-center border border-transparent px-0 py-0 text-neutral-400 hover:border-gray-200 hover:bg-white sm:flex">
<ClockIcon className="h-5 w-5 group-hover:text-gray-800" />
size="icon">
<Icon.FiClock className="h-5 w-5 group-hover:text-gray-800" />
</Button>
</Tooltip>
<Dropdown>
<DropdownMenuTrigger className="group h-10 w-10 border border-transparent p-0 text-neutral-400 hover:border-gray-200 hover:bg-white">
<DotsHorizontalIcon className="h-5 w-5 group-hover:text-gray-800" />
<DropdownMenuTrigger asChild>
<Button type="button" color="minimal" size="icon" StartIcon={Icon.FiMoreHorizontal} />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<Link href={"/" + props.member.username}>
<a target="_blank">
<Button color="minimal" StartIcon={ExternalLinkIcon} className="w-full font-normal">
<Button color="minimal" StartIcon={Icon.FiExternalLink} className="w-full font-normal">
{t("view_public_page")}
</Button>
</a>
@@ -143,7 +142,7 @@ export default function MemberListItem(props: Props) {
<Button
onClick={() => setShowChangeMemberRoleModal(true)}
color="minimal"
StartIcon={PencilIcon}
StartIcon={Icon.FiEdit2}
className="w-full flex-shrink-0 font-normal">
{t("edit_role")}
</Button>
@@ -159,7 +158,7 @@ export default function MemberListItem(props: Props) {
<Button
onClick={() => setShowImpersonateModal(true)}
color="minimal"
StartIcon={LockClosedIcon}
StartIcon={Icon.FiLock}
className="w-full flex-shrink-0 font-normal">
{t("impersonate")}
</Button>
@@ -175,7 +174,7 @@ export default function MemberListItem(props: Props) {
e.stopPropagation();
}}
color="warn"
StartIcon={UserRemoveIcon}
StartIcon={Icon.FiUserMinus}
className="w-full font-normal">
{t("remove_member")}
</Button>
@@ -1,10 +1,6 @@
import React from "react";
import { SkeletonText } from "@calcom/ui";
import { ShellSubHeading } from "@components/Shell";
function SkeletonLoaderTeamList({ className }: { className?: string }) {
function SkeletonLoaderTeamList() {
return (
<>
<ul className="-mx-4 animate-pulse divide-y divide-neutral-200 rounded-sm border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden">
+3 -3
View File
@@ -1,4 +1,3 @@
import { UsersIcon } from "@heroicons/react/outline";
import { useRef, useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -6,6 +5,7 @@ import { trpc } from "@calcom/trpc/react";
import { Button } from "@calcom/ui";
import { Alert } from "@calcom/ui/Alert";
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
interface Props {
isOpen: boolean;
@@ -39,7 +39,7 @@ export default function TeamCreate(props: Props) {
<DialogContent>
<div className="mb-4 sm:flex sm:items-start">
<div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10">
<UsersIcon className="text-brandcontrast h-6 w-6" />
<Icon.FiUsers className="text-brandcontrast h-6 w-6" />
</div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
@@ -62,7 +62,7 @@ export default function TeamCreate(props: Props) {
id="name"
placeholder="Acme Inc."
required
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 sm:text-sm"
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 text-sm"
/>
</div>
{errorMessage && <Alert severity="error" title={errorMessage} />}
+6
View File
@@ -1,3 +1,5 @@
import { useState } from "react";
import showToast from "@calcom/lib/notification";
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
@@ -10,6 +12,8 @@ interface Props {
export default function TeamList(props: Props) {
const utils = trpc.useContext();
const [hideDropdown, setHideDropdown] = useState(false);
function selectAction(action: string, teamId: number) {
switch (action) {
case "disband":
@@ -40,6 +44,8 @@ export default function TeamList(props: Props) {
team={team}
onActionSelect={(action: string) => selectAction(action, team?.id as number)}
isLoading={deleteTeamMutation.isLoading}
hideDropdown={hideDropdown}
setHideDropdown={setHideDropdown}
/>
))}
</ul>
+23 -28
View File
@@ -1,20 +1,13 @@
import { LogoutIcon } from "@heroicons/react/outline";
import {
DotsHorizontalIcon,
ExternalLinkIcon,
LinkIcon,
PencilIcon,
TrashIcon,
CheckIcon,
XIcon,
} from "@heroicons/react/solid";
import { MembershipRole } from "@prisma/client";
import Link from "next/link";
import classNames from "@calcom/lib/classNames";
import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
import Button from "@calcom/ui/Button";
import ConfirmationDialogContent from "@calcom/ui/ConfirmationDialogContent";
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
import Dropdown, {
DropdownMenuContent,
@@ -22,12 +15,9 @@ import Dropdown, {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
import classNames from "@lib/classNames";
import { getPlaceholderAvatar } from "@lib/getPlaceholderAvatar";
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
import Avatar from "@components/ui/Avatar";
import { TeamRole } from "./TeamPill";
@@ -37,6 +27,8 @@ interface Props {
key: number;
onActionSelect: (text: string) => void;
isLoading?: boolean;
hideDropdown: boolean;
setHideDropdown: (value: boolean) => void;
}
export default function TeamListItem(props: Props) {
@@ -49,18 +41,21 @@ export default function TeamListItem(props: Props) {
utils.invalidateQueries(["viewer.teams.list"]);
},
});
function acceptOrLeave(accept: boolean) {
acceptOrLeaveMutation.mutate({
teamId: team?.id as number,
accept,
});
}
const acceptInvite = () => acceptOrLeave(true);
const declineInvite = () => acceptOrLeave(false);
const isOwner = props.team.role === MembershipRole.OWNER;
const isInvitee = !props.team.accepted;
const isAdmin = props.team.role === MembershipRole.OWNER || props.team.role === MembershipRole.ADMIN;
const { hideDropdown, setHideDropdown } = props;
if (!team) return <></>;
@@ -110,8 +105,8 @@ export default function TeamListItem(props: Props) {
</div>
<div className="block sm:hidden">
<Dropdown>
<DropdownMenuTrigger className="group h-10 w-10 border border-transparent p-0 text-neutral-400 hover:border-gray-200 ">
<DotsHorizontalIcon className="h-5 w-5 group-hover:text-gray-800" />
<DropdownMenuTrigger asChild>
<Button type="button" color="minimal" size="icon" StartIcon={Icon.FiMoreHorizontal} />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
@@ -119,7 +114,7 @@ export default function TeamListItem(props: Props) {
color="minimal"
size="sm"
className="w-full rounded-none font-medium"
StartIcon={CheckIcon}
StartIcon={Icon.FiCheck}
onClick={acceptInvite}>
{t("accept")}
</Button>
@@ -129,7 +124,7 @@ export default function TeamListItem(props: Props) {
color="warn"
size="sm"
className="w-full rounded-none font-medium"
StartIcon={XIcon}
StartIcon={Icon.FiX}
onClick={declineInvite}>
{t("reject")}
</Button>
@@ -140,7 +135,7 @@ export default function TeamListItem(props: Props) {
</>
)}
{!isInvitee && (
<div className="flex rtl:space-x-reverse">
<div className="flex space-x-2 rtl:space-x-reverse">
<TeamRole role={team.role} />
<Tooltip side="top" content={t("copy_link_team")}>
@@ -153,14 +148,14 @@ export default function TeamListItem(props: Props) {
size="icon"
color="minimal"
type="button">
<LinkIcon className="h-5 w-5 group-hover:text-gray-600" />
<Icon.FiLink className="h-5 w-5 group-hover:text-gray-600" />
</Button>
</Tooltip>
<Dropdown>
<DropdownMenuTrigger className="group h-10 w-10 border border-transparent p-0 text-neutral-400 hover:border-gray-200 ">
<DotsHorizontalIcon className="h-5 w-5 group-hover:text-gray-800" />
<DropdownMenuTrigger asChild>
<Button type="button" color="minimal" size="icon" StartIcon={Icon.FiMoreHorizontal} />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuContent hidden={hideDropdown}>
{isAdmin && (
<DropdownMenuItem>
<Link href={"/settings/teams/" + team.id}>
@@ -169,7 +164,7 @@ export default function TeamListItem(props: Props) {
color="minimal"
size="sm"
className="w-full rounded-none font-medium"
StartIcon={PencilIcon}>
StartIcon={Icon.FiEdit2}>
{t("edit_team")}
</Button>
</a>
@@ -183,7 +178,7 @@ export default function TeamListItem(props: Props) {
color="minimal"
size="sm"
className="w-full rounded-none font-medium"
StartIcon={ExternalLinkIcon}>
StartIcon={Icon.FiExternalLink}>
{t("preview_team")}
</Button>
</a>
@@ -192,7 +187,7 @@ export default function TeamListItem(props: Props) {
<DropdownMenuSeparator className="h-px bg-gray-200" />
{isOwner && (
<DropdownMenuItem>
<Dialog>
<Dialog open={hideDropdown} onOpenChange={setHideDropdown}>
<DialogTrigger asChild>
<Button
onClick={(e) => {
@@ -201,7 +196,7 @@ export default function TeamListItem(props: Props) {
color="warn"
size="sm"
className="w-full rounded-none font-medium"
StartIcon={TrashIcon}>
StartIcon={Icon.FiTrash}>
{t("disband_team")}
</Button>
</DialogTrigger>
@@ -227,7 +222,7 @@ export default function TeamListItem(props: Props) {
type="button"
color="warn"
size="lg"
StartIcon={LogoutIcon}
StartIcon={Icon.FiLogOut}
className="w-full rounded-none"
onClick={(e) => {
e.stopPropagation();
+8 -8
View File
@@ -1,4 +1,3 @@
import { HashtagIcon, InformationCircleIcon, LinkIcon, PhotographIcon } from "@heroicons/react/solid";
import React, { useRef, useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -8,6 +7,7 @@ import { TeamWithMembers } from "@calcom/lib/server/queries/teams";
import { trpc } from "@calcom/trpc/react";
import { Alert } from "@calcom/ui/Alert";
import Button from "@calcom/ui/Button";
import { Icon } from "@calcom/ui/Icon";
import { TextField } from "@calcom/ui/form/fields";
import ImageUploader from "@components/ImageUploader";
@@ -82,7 +82,7 @@ export default function TeamSettings(props: Props) {
<div className="flex flex-col lg:flex-row">
<div className="flex-grow space-y-6">
<SettingInputContainer
Icon={LinkIcon}
Icon={Icon.FiLink}
label="Team URL"
htmlFor="team-url"
Input={
@@ -100,7 +100,7 @@ export default function TeamSettings(props: Props) {
}
/>
<SettingInputContainer
Icon={HashtagIcon}
Icon={Icon.FiHash}
label="Team Name"
htmlFor="name"
Input={
@@ -111,7 +111,7 @@ export default function TeamSettings(props: Props) {
id="name"
placeholder={t("your_team_name")}
required
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 sm:text-sm"
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 text-sm"
defaultValue={team?.name as string}
/>
}
@@ -119,7 +119,7 @@ export default function TeamSettings(props: Props) {
<hr />
<div>
<SettingInputContainer
Icon={InformationCircleIcon}
Icon={Icon.FiInfo}
label={t("about")}
htmlFor="about"
Input={
@@ -130,7 +130,7 @@ export default function TeamSettings(props: Props) {
name="about"
rows={3}
defaultValue={team?.bio as string}
className="mt-1 block w-full rounded-sm border-gray-300 sm:text-sm"
className="mt-1 block w-full rounded-sm border-gray-300 text-sm"
/>
<p className="mt-2 text-sm text-gray-500">{t("team_description")}</p>
</>
@@ -139,7 +139,7 @@ export default function TeamSettings(props: Props) {
</div>
<div>
<SettingInputContainer
Icon={PhotographIcon}
Icon={Icon.FiImage}
label="Logo"
htmlFor="avatar"
Input={
@@ -151,7 +151,7 @@ export default function TeamSettings(props: Props) {
name="avatar"
id="avatar"
placeholder="URL"
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 sm:text-sm"
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 text-sm"
defaultValue={team?.logo ?? undefined}
/>
<ImageUploader
@@ -1,4 +1,3 @@
import { ClockIcon, ExternalLinkIcon, LinkIcon, LogoutIcon, TrashIcon } from "@heroicons/react/solid";
import { MembershipRole } from "@prisma/client";
import Link from "next/link";
import { useRouter } from "next/router";
@@ -7,11 +6,12 @@ import React from "react";
import showToast from "@calcom/lib/notification";
import { TeamWithMembers } from "@calcom/lib/server/queries/teams";
import { trpc } from "@calcom/trpc/react";
import ConfirmationDialogContent from "@calcom/ui/ConfirmationDialogContent";
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { useLocale } from "@lib/hooks/useLocale";
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
import CreateEventTypeButton from "@components/eventtype/CreateEventType";
import LinkIconButton from "@components/ui/LinkIconButton";
@@ -66,11 +66,11 @@ export default function TeamSettingsRightSidebar(props: { team: TeamWithMembers;
<div className="space-y-1">
<Link href={permalink} passHref={true}>
<a target="_blank">
<LinkIconButton Icon={ExternalLinkIcon}>{t("preview")}</LinkIconButton>
<LinkIconButton Icon={Icon.FiExternalLink}>{t("preview")}</LinkIconButton>
</a>
</Link>
<LinkIconButton
Icon={LinkIcon}
Icon={Icon.FiLink}
onClick={() => {
navigator.clipboard.writeText(permalink);
showToast("Copied to clipboard", "success");
@@ -84,7 +84,7 @@ export default function TeamSettingsRightSidebar(props: { team: TeamWithMembers;
onClick={(e) => {
e.stopPropagation();
}}
Icon={TrashIcon}>
Icon={Icon.FiTrash}>
{t("disband_team")}
</LinkIconButton>
</DialogTrigger>
@@ -100,7 +100,7 @@ export default function TeamSettingsRightSidebar(props: { team: TeamWithMembers;
<Dialog>
<DialogTrigger asChild>
<LinkIconButton
Icon={LogoutIcon}
Icon={Icon.FiLogOut}
onClick={(e) => {
e.stopPropagation();
}}>
@@ -120,7 +120,7 @@ export default function TeamSettingsRightSidebar(props: { team: TeamWithMembers;
{props.team?.id && props.role !== MembershipRole.MEMBER && (
<Link href={`/settings/teams/${props.team.id}/availability`}>
<div className="mt-5 hidden space-y-1 sm:block">
<LinkIconButton Icon={ClockIcon}>View Availability</LinkIconButton>
<LinkIconButton Icon={Icon.FiClock}>View Availability</LinkIconButton>
<p className="mt-2 text-sm text-gray-500">See your team members availability at a glance.</p>
</div>
</Link>
+3 -4
View File
@@ -1,5 +1,3 @@
import { ArrowRightIcon } from "@heroicons/react/outline";
import { ArrowLeftIcon } from "@heroicons/react/solid";
import classnames from "classnames";
import Link from "next/link";
import { TeamPageProps } from "pages/team/[slug]";
@@ -7,6 +5,7 @@ import React from "react";
import { WEBAPP_URL } from "@calcom/lib/constants";
import Button from "@calcom/ui/Button";
import { Icon } from "@calcom/ui/Icon";
import { useLocale } from "@lib/hooks/useLocale";
@@ -38,7 +37,7 @@ const Team = ({ team }: TeamPageProps) => {
return (
<Link key={member.id} href={`/${member.username}`}>
<div className={classes}>
<ArrowRightIcon
<Icon.FiArrowRight
className={classnames(
"text-black dark:text-white",
"absolute top-4 right-4",
@@ -85,7 +84,7 @@ const Team = ({ team }: TeamPageProps) => {
<Members members={team.members} />
{team.eventTypes.length > 0 && (
<aside className="mt-8 text-center dark:text-white">
<Button color="secondary" href={`/team/${team.slug}`} shallow={true} StartIcon={ArrowLeftIcon}>
<Button color="secondary" href={`/team/${team.slug}`} shallow={true} StartIcon={Icon.FiArrowLeft}>
{t("go_back")}
</Button>
</aside>
+3 -2
View File
@@ -1,6 +1,7 @@
import { PencilIcon } from "@heroicons/react/solid";
import { useState } from "react";
import { Icon } from "@calcom/ui/Icon";
const EditableHeading = ({
title,
onChange,
@@ -24,7 +25,7 @@ const EditableHeading = ({
{title}
</h1>
{!readOnly ? (
<PencilIcon className="ml-1 -mt-1 inline h-4 w-4 text-gray-700 group-hover:text-gray-500" />
<Icon.FiEdit2 className="ml-1 -mt-1 inline h-4 w-4 text-gray-700 group-hover:text-gray-500" />
) : null}
</>
) : (
+2 -3
View File
@@ -1,5 +1,4 @@
import { InformationCircleIcon } from "@heroicons/react/solid";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
export default function InfoBadge({ content }: { content: string }) {
@@ -7,7 +6,7 @@ export default function InfoBadge({ content }: { content: string }) {
<>
<Tooltip side="top" content={content}>
<span title={content}>
<InformationCircleIcon className="relative top-px left-1 right-1 mt-px h-4 w-4 text-gray-500" />
<Icon.FiInfo className="relative top-px left-1 right-1 mt-px h-4 w-4 text-gray-500" />
</span>
</Tooltip>
</>
+1 -1
View File
@@ -8,7 +8,7 @@ const PoweredByCal = () => {
const { t } = useLocale();
const isEmbed = useIsEmbed();
return (
<div className={"p-1 text-center text-xs sm:text-right" + (isEmbed ? " max-w-3xl" : "")}>
<div className={"p-2 text-center text-xs sm:text-right" + (isEmbed ? " max-w-3xl" : "")}>
<Link href={POWERED_BY_URL}>
<a target="_blank" className="text-bookinglight opacity-50 hover:opacity-100 dark:text-white">
{t("powered_by")}{" "}
@@ -5,7 +5,7 @@ export default function SettingInputContainer({
htmlFor,
}: {
Input: React.ReactNode;
Icon: (props: React.SVGProps<SVGSVGElement>) => JSX.Element;
Icon: (props: React.SVGProps<SVGSVGElement>) => JSX.Element | null;
label: string;
htmlFor?: string;
}) {
+4 -4
View File
@@ -1,8 +1,8 @@
import { ChevronDownIcon, DotsHorizontalIcon } from "@heroicons/react/solid";
import React, { FC } from "react";
import Button from "@calcom/ui/Button";
import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
import { SVGComponent } from "@lib/types/SVGComponent";
@@ -37,8 +37,8 @@ const DropdownActions = ({
return (
<Dropdown>
{!actionTrigger ? (
<DropdownMenuTrigger className="h-[38px] w-[38px] cursor-pointer rounded-sm border border-transparent text-neutral-500 hover:border-gray-300 hover:text-neutral-900">
<DotsHorizontalIcon className="h-5 w-5 group-hover:text-gray-800" />
<DropdownMenuTrigger asChild>
<Button type="button" color="minimal" size="icon" StartIcon={Icon.FiMoreHorizontal} />
</DropdownMenuTrigger>
) : (
<DropdownMenuTrigger asChild>{actionTrigger}</DropdownMenuTrigger>
@@ -84,7 +84,7 @@ const TableActions: FC<Props> = ({ actions }) => {
onClick={action.onClick || defaultAction}
StartIcon={action.icon}
startIconClassName={action.iconClassName}
{...(action?.actions ? { EndIcon: ChevronDownIcon } : null)}
{...(action?.actions ? { EndIcon: Icon.FiChevronDown } : null)}
disabled={action.disabled}
color={action.color || "secondary"}>
{action.label}
@@ -1,4 +1,3 @@
import { CheckIcon, ExternalLinkIcon, PencilAltIcon, StarIcon, XIcon } from "@heroicons/react/solid";
import classNames from "classnames";
import { debounce } from "lodash";
import { MutableRefObject, useCallback, useEffect, useState } from "react";
@@ -12,6 +11,7 @@ import { trpc } from "@calcom/trpc/react";
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import Button from "@calcom/ui/Button";
import { Dialog, DialogClose, DialogContent, DialogHeader } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { Input, Label } from "@calcom/ui/form/fields";
export enum UsernameChangeStatusEnum {
@@ -145,6 +145,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
<div className="flex flex-row">
<Button
type="button"
color="primary"
className="mx-2"
onClick={() => setOpenDialogSaveUsername(true)}
data-testid={`update-username-btn-${index}`}>
@@ -152,7 +153,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
</Button>
<Button
type="button"
color="minimal"
color="secondary"
className="mx-2"
onClick={() => {
if (currentUsername) {
@@ -223,8 +224,8 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
premiumUsername ? "text-orange-500" : "",
usernameIsAvailable ? "" : ""
)}>
{premiumUsername ? <StarIcon className="mt-[4px] w-6" /> : <></>}
{!premiumUsername && usernameIsAvailable ? <CheckIcon className="mt-[4px] w-6" /> : <></>}
{premiumUsername ? <Icon.FiStar className="mt-[4px] w-6" /> : <></>}
{!premiumUsername && usernameIsAvailable ? <Icon.FiCheck className="mt-[4px] w-6" /> : <></>}
</span>
</div>
)}
@@ -244,23 +245,18 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
)}
{(usernameIsAvailable || premiumUsername) && currentUsername !== inputUsernameValue && (
<div className="mt-2 flex justify-end md:hidden">
<div className="mt-2 flex justify-end sm:hidden">
<ActionButtons index="mobile" />
</div>
)}
<Dialog open={openDialogSaveUsername}>
<DialogContent>
<DialogClose asChild>
<div className="fixed top-1 right-1 flex h-8 w-8 justify-center rounded-full hover:bg-gray-200">
<XIcon className="w-4" />
</div>
</DialogClose>
<div style={{ display: "flex", flexDirection: "row" }}>
<div className="xs:hidden flex h-10 w-10 flex-shrink-0 justify-center rounded-full bg-[#FAFAFA]">
<PencilAltIcon className="m-auto h-6 w-6" />
<Icon.FiEdit2 className="m-auto h-6 w-6" />
</div>
<div className="mb-4 w-full px-4 pt-1">
<DialogHeader title="Confirm username change" />
<DialogHeader title={t("confirm_username_change_dialog_title")} />
{usernameChangeCondition && usernameChangeCondition !== UsernameChangeStatusEnum.NORMAL && (
<p className="-mt-4 mb-4 text-sm text-gray-800">
{usernameChangeCondition === UsernameChangeStatusEnum.UPGRADE &&
@@ -297,7 +293,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
data-testid="go-to-billing"
href={`/api/integrations/stripepayment/subscription?intentUsername=${inputUsernameValue}`}>
<>
{t("go_to_stripe_billing")} <ExternalLinkIcon className="ml-1 h-4 w-4" />
{t("go_to_stripe_billing")} <Icon.FiExternalLink className="ml-1 h-4 w-4" />
</>
</Button>
)}
@@ -1,4 +1,3 @@
import { CheckIcon, PencilAltIcon, XIcon } from "@heroicons/react/solid";
import classNames from "classnames";
import { debounce } from "lodash";
import { MutableRefObject, useCallback, useEffect, useState } from "react";
@@ -10,6 +9,7 @@ import { trpc } from "@calcom/trpc/react";
import { AppRouter } from "@calcom/trpc/server/routers/_app";
import Button from "@calcom/ui/Button";
import { Dialog, DialogClose, DialogContent, DialogHeader } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { Input, Label } from "@calcom/ui/form/fields";
interface ICustomUsernameProps {
@@ -137,7 +137,7 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
{currentUsername !== inputUsernameValue && (
<div className="absolute right-[2px] top-0 flex flex-row">
<span className={classNames("mx-2 py-1")}>
{usernameIsAvailable ? <CheckIcon className="mt-[4px] w-6" /> : <></>}
{usernameIsAvailable ? <Icon.FiCheck className="mt-[4px] w-6" /> : <></>}
</span>
</div>
)}
@@ -149,20 +149,15 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
{markAsError && <p className="mt-1 text-xs text-red-500">Username is already taken</p>}
{usernameIsAvailable && currentUsername !== inputUsernameValue && (
<div className="mt-2 flex justify-end md:hidden">
<div className="mt-2 flex justify-end sm:hidden">
<ActionButtons index="mobile" />
</div>
)}
<Dialog open={openDialogSaveUsername}>
<DialogContent>
<DialogClose asChild>
<div className="fixed top-1 right-1 flex h-8 w-8 justify-center rounded-full hover:bg-gray-200">
<XIcon className="w-4" />
</div>
</DialogClose>
<div style={{ display: "flex", flexDirection: "row" }}>
<div className="xs:hidden flex h-10 w-10 flex-shrink-0 justify-center rounded-full bg-[#FAFAFA]">
<PencilAltIcon className="m-auto h-6 w-6" />
<Icon.FiEdit2 className="m-auto h-6 w-6" />
</div>
<div className="mb-4 w-full px-4 pt-1">
<DialogHeader title={t("confirm_username_change_dialog_title")} />
@@ -1,8 +1,8 @@
import { CheckIcon, XIcon } from "@heroicons/react/outline";
import React from "react";
import { Props } from "react-select";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Icon } from "@calcom/ui/Icon";
import Avatar from "@components/ui/Avatar";
import Select from "@components/ui/form/Select";
@@ -41,7 +41,7 @@ export const CheckedSelect = ({
{label}
{disabled && (
<div className="flex-grow">
<CheckIcon className="float-right h-6 w-6 text-neutral-500" />
<Icon.FiCheck className="float-right h-6 w-6 text-neutral-500" />
</div>
)}
</div>
@@ -59,7 +59,7 @@ export const CheckedSelect = ({
alt={option.label}
/>
{option.label}
<XIcon
<Icon.FiX
onClick={() => props.onChange(value.filter((item) => item.value !== option.value))}
className="float-right mt-0.5 h-5 w-5 cursor-pointer text-neutral-500"
/>
+4 -3
View File
@@ -1,9 +1,10 @@
import { CalendarIcon } from "@heroicons/react/solid";
import React from "react";
import "react-calendar/dist/Calendar.css";
import "react-date-picker/dist/DatePicker.css";
import PrimitiveDatePicker from "react-date-picker/dist/entry.nostyle";
import { Icon } from "@calcom/ui/Icon";
import classNames from "@lib/classNames";
type Props = {
@@ -18,11 +19,11 @@ export const DatePicker = ({ minDate, disabled, date, onDatesChange, className }
return (
<PrimitiveDatePicker
className={classNames(
"focus:ring-primary-500 focus:border-primary-500 rounded-sm border border-gray-300 p-1 pl-2 sm:text-sm",
"focus:ring-primary-500 focus:border-primary-500 rounded-sm border border-gray-300 p-1 pl-2 text-sm",
className
)}
clearIcon={null}
calendarIcon={<CalendarIcon className="h-5 w-5 text-gray-500" />}
calendarIcon={<Icon.FiCalendar className="h-5 w-5 text-gray-500" />}
value={date}
minDate={minDate}
disabled={disabled}
@@ -1,10 +1,11 @@
// @see: https://github.com/wojtekmaj/react-daterange-picker/issues/91
import { ArrowRightIcon, CalendarIcon } from "@heroicons/react/solid";
import "@wojtekmaj/react-daterange-picker/dist/DateRangePicker.css";
import PrimitiveDateRangePicker from "@wojtekmaj/react-daterange-picker/dist/entry.nostyle";
import React from "react";
import "react-calendar/dist/Calendar.css";
import { Icon } from "@calcom/ui/Icon";
type Props = {
startDate: Date;
endDate: Date;
@@ -14,10 +15,10 @@ type Props = {
export const DateRangePicker = ({ startDate, endDate, onDatesChange }: Props) => {
return (
<PrimitiveDateRangePicker
className="rounded-sm border-gray-300 sm:text-sm"
className="rounded-sm border-gray-300 text-sm"
clearIcon={null}
calendarIcon={<CalendarIcon className="h-5 w-5 text-gray-500" />}
rangeDivider={<ArrowRightIcon className="h-4 w-4 text-gray-400 ltr:mr-2 rtl:ml-2" />}
calendarIcon={<Icon.FiCalendar className="h-5 w-5 text-gray-500" />}
rangeDivider={<Icon.FiArrowRight className="h-4 w-4 text-gray-400 ltr:mr-2 rtl:ml-2" />}
value={[startDate, endDate]}
onChange={([startDate, endDate]) => {
if (typeof onDatesChange === "function") onDatesChange({ startDate, endDate });
+2 -2
View File
@@ -22,12 +22,12 @@ const MinutesField = forwardRef<HTMLInputElement, Props>(({ label, ...rest }, re
ref={ref}
type="number"
className={classNames(
"block w-full rounded-sm border-gray-300 pl-2 pr-12 sm:text-sm",
"block w-full rounded-sm border-gray-300 pl-2 pr-12 text-sm",
rest.className
)}
/>
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<span className="text-gray-500 sm:text-sm" id="duration">
<span className="text-sm text-gray-500" id="duration">
mins
</span>
</div>
+59 -4
View File
@@ -1,7 +1,8 @@
import React, { useCallback, useEffect, useState } from "react";
import ReactSelect, { components, GroupBase, Props, InputProps, SingleValue, MultiValue } from "react-select";
import classNames from "@lib/classNames";
import classNames from "@calcom/lib/classNames";
import useTheme from "@calcom/lib/hooks/useTheme";
export type SelectProps<
Option,
@@ -27,6 +28,54 @@ function Select<
IsMulti extends boolean = false,
Group extends GroupBase<Option> = GroupBase<Option>
>({ className, ...props }: SelectProps<Option, IsMulti, Group>) {
const { resolvedTheme, forcedTheme, isReady } = useTheme();
const hasDarkTheme = !forcedTheme && resolvedTheme === "dark";
const darkThemeColors = {
/** Dark Theme starts */
//primary - Border when selected and Selected Option background
primary: "rgb(41 41 41 / var(--tw-border-opacity))",
neutral0: "rgb(62 62 62 / var(--tw-bg-opacity))",
// Down Arrow hover color
neutral5: "white",
neutral10: "rgb(41 41 41 / var(--tw-border-opacity))",
// neutral20 - border color + down arrow default color
neutral20: "rgb(41 41 41 / var(--tw-border-opacity))",
// neutral30 - hover border color
neutral30: "rgb(41 41 41 / var(--tw-border-opacity))",
neutral40: "white",
danger: "white",
// Cross button in multiselect
dangerLight: "rgb(41 41 41 / var(--tw-border-opacity))",
// neutral50 - MultiSelect - "Select Text" color
neutral50: "white",
// neutral60 - Down Arrow color
neutral60: "white",
neutral70: "red",
// neutral80 - Selected option
neutral80: "white",
neutral90: "blue",
primary50: "rgba(209 , 213, 219, var(--tw-bg-opacity))",
primary25: "rgba(244, 245, 246, var(--tw-bg-opacity))",
/** Dark Theme ends */
};
// Till we know in JS the theme is ready, we can't render react-select as it would render with light theme instead
if (!isReady) {
return <input type="text" className={className} />;
}
return (
<ReactSelect
theme={(theme) => ({
@@ -34,10 +83,16 @@ function Select<
borderRadius: 2,
colors: {
...theme.colors,
primary: "var(--brand-color)",
...(hasDarkTheme
? darkThemeColors
: {
/** Light Theme starts */
primary: "var(--brand-color)",
primary50: "rgba(209 , 213, 219, var(--tw-bg-opacity))",
primary25: "rgba(244, 245, 246, var(--tw-bg-opacity))",
primary50: "rgba(209 , 213, 219, var(--tw-bg-opacity))",
primary25: "rgba(244, 245, 246, var(--tw-bg-opacity))",
/** Light Theme Ends */
}),
},
})}
styles={{
@@ -1,7 +1,8 @@
import { ChevronDownIcon } from "@heroicons/react/solid";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
import React from "react";
import { Icon } from "@calcom/ui/Icon";
import classNames from "@lib/classNames";
import { useLocale } from "@lib/hooks/useLocale";
@@ -34,11 +35,11 @@ export const Select = function RadioAreaSelect(props: RadioAreaSelectProps) {
type="button"
disabled={disabled}
className={classNames(
"border-1 focus:ring-primary-500 mb-1 block w-full cursor-pointer rounded-sm border border-gray-300 bg-white p-2 text-left sm:text-sm",
"border-1 focus:ring-primary-500 mb-1 block w-full cursor-pointer rounded-sm border border-gray-300 bg-white p-2 text-left text-sm",
disabled && "cursor-default bg-gray-200 focus:ring-0 "
)}>
{getLabel(props.value) ?? placeholder}
<ChevronDownIcon className="float-right h-5 w-5 text-neutral-500" />
<Icon.FiChevronDown className="float-right h-5 w-5 text-neutral-500" />
</CollapsibleTrigger>
<CollapsibleContent>
<RadioAreaGroup className="space-y-2 text-sm" name={props.name} onChange={props.onChange}>
@@ -1,15 +1,16 @@
import { PlusIcon } from "@heroicons/react/solid";
import { useRouter } from "next/router";
import { useState } from "react";
import { trpc } from "@calcom/trpc/react";
import Button from "@calcom/ui/Button";
import { Dialog, DialogContent } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { List } from "@calcom/ui/List";
import { ShellSubHeading } from "@calcom/ui/Shell";
import SkeletonLoader from "@calcom/ui/apps/SkeletonLoader";
import { QueryCell } from "@lib/QueryCell";
import { List } from "@components/List";
import { ShellSubHeading } from "@components/Shell";
import SkeletonLoader from "@components/apps/SkeletonLoader";
import WebhookDialogForm from "@components/webhook/WebhookDialogForm";
import WebhookListItem, { TWebhook } from "@components/webhook/WebhookListItem";
@@ -21,10 +22,12 @@ export type WebhookListContainerType = {
};
export default function WebhookListContainer(props: WebhookListContainerType) {
const router = useRouter();
const query = trpc.useQuery(
["viewer.webhook.list", { eventTypeId: props.eventTypeId, appId: props.appId }],
{
suspense: true,
enabled: router.isReady,
}
);
const [newWebhookModal, setNewWebhookModal] = useState(false);
@@ -42,9 +45,9 @@ export default function WebhookListContainer(props: WebhookListContainerType) {
subtitle={props.subtitle}
actions={
<Button
color="secondary"
color="minimal"
size="icon"
StartIcon={PlusIcon}
StartIcon={Icon.FiPlus}
onClick={() => setNewWebhookModal(true)}
data-testid="new_webhook"
/>
@@ -1,16 +1,14 @@
import { PencilAltIcon, TrashIcon } from "@heroicons/react/outline";
import classNames from "@calcom/lib/classNames";
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
import Button from "@calcom/ui/Button";
import ConfirmationDialogContent from "@calcom/ui/ConfirmationDialogContent";
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { ListItem } from "@calcom/ui/List";
import { Tooltip } from "@calcom/ui/Tooltip";
import { useLocale } from "@lib/hooks/useLocale";
import { ListItem } from "@components/List";
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
export type TWebhook = inferQueryOutput<"viewer.webhook.list">[number];
export default function WebhookListItem(props: { webhook: TWebhook; onEditWebhook: () => void }) {
@@ -56,7 +54,7 @@ export default function WebhookListItem(props: { webhook: TWebhook; onEditWebhoo
onClick={() => props.onEditWebhook()}
color="minimal"
size="icon"
StartIcon={PencilAltIcon}
StartIcon={Icon.FiEdit2}
className="ml-4 w-full self-center p-2"
/>
</Tooltip>
@@ -69,7 +67,7 @@ export default function WebhookListItem(props: { webhook: TWebhook; onEditWebhoo
}}
color="minimal"
size="icon"
StartIcon={TrashIcon}
StartIcon={Icon.FiTrash}
className="ml-2 w-full self-center p-2"
/>
</DialogTrigger>
@@ -1,4 +1,3 @@
import { ChevronRightIcon, SwitchHorizontalIcon } from "@heroicons/react/solid";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
import { useState } from "react";
import { useWatch } from "react-hook-form";
@@ -7,6 +6,7 @@ import classNames from "@calcom/lib/classNames";
import showToast from "@calcom/lib/notification";
import { trpc } from "@calcom/trpc/react";
import Button from "@calcom/ui/Button";
import { Icon } from "@calcom/ui/Icon";
import { InputGroupBox } from "@calcom/ui/form/fields";
import { useLocale } from "@lib/hooks/useLocale";
@@ -25,7 +25,7 @@ export default function WebhookTestDisclosure() {
return (
<Collapsible open={open} onOpenChange={() => setOpen(!open)}>
<CollapsibleTrigger type="button" className="flex w-full cursor-pointer">
<ChevronRightIcon className={`${open ? "rotate-90 transform" : ""} h-5 w-5 text-neutral-500`} />
<Icon.FiChevronRight className={`${open ? "rotate-90 transform" : ""} h-5 w-5 text-neutral-500`} />
<span className="text-sm font-medium text-gray-700">{t("webhook_test")}</span>
</CollapsibleTrigger>
<CollapsibleContent>
@@ -33,7 +33,6 @@ export default function WebhookTestDisclosure() {
<div className="flex justify-between bg-gray-50 p-2">
<h3 className="self-center text-gray-700">{t("webhook_response")}</h3>
<Button
StartIcon={SwitchHorizontalIcon}
type="button"
color="minimal"
disabled={mutation.isLoading}
-42
View File
@@ -1,42 +0,0 @@
The Cal.com Enterprise Edition (EE) license (the “EE License”)
Copyright (c) 2020-present Cal.com, Inc
With regard to the Cal.com Software:
This software and associated documentation files (the "Software") may only be
used in production, if you (and any entity that you represent) have agreed to,
and are in compliance with, the Cal.com Subscription Terms available
at https://cal.com/terms (the “EE Terms”), or other agreements governing
the use of the Software, as mutually agreed by you and Cal.com, Inc ("Cal.com"),
and otherwise have a valid Cal.com Enterprise Edition subscription ("EE Subscription")
for the correct number of hosts as defined in the EE Terms ("Hosts"). Subject to the foregoing sentence,
you are free to modify this Software and publish patches to the Software. You agree
that Cal.com and/or its licensors (as applicable) retain all right, title and interest in
and to all such modifications and/or patches, and all such modifications and/or
patches may only be used, copied, modified, displayed, distributed, or otherwise
exploited with a valid EE Subscription for the correct number of hosts.
Notwithstanding the foregoing, you may copy and modify the Software for development
and testing purposes, without requiring a subscription. You agree that Cal.com and/or
its licensors (as applicable) retain all right, title and interest in and to all such
modifications. You are not granted any other rights beyond what is expressly stated herein.
Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
and/or sell the Software.
This EE License applies only to the part of this Software that is not distributed under
the AGPLv3 license. Any part of this Software distributed under the AGPLv3 license or which
is served client-side as an image, font, cascading stylesheet (CSS), file which produces
or is compiled, arranged, augmented, or combined into client-side JavaScript, in whole or
in part, is copyrighted under the AGPLv3 license. The full text of this EE License shall
be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
For all third party components incorporated into the Cal.com Software, those
components are licensed under the original license provided by the owner of the
applicable component.
-38
View File
@@ -1,38 +0,0 @@
<!-- PROJECT LOGO -->
<div align="center">
<a href="https://cal.com/enterprise">
<img src="https://user-images.githubusercontent.com/8019099/133430653-24422d2a-3c8d-4052-9ad6-0580597151ee.png" alt="Logo">
</a>
<a href="https://cal.com/enterprise">Get Started with Enterprise</a>
</div>
# Enterprise Edition
Welcome to the Enterprise Edition ("/ee") of Cal.com.
The [/ee](https://github.com/calcom/cal.com/tree/main/apps/web/ee) subfolder is the place for all the **Pro** features from our [hosted](https://cal.com/pricing) plan and [enterprise-grade](https://cal.com/enterprise) features such as SSO, SAML, ADFS, OIDC, SCIM, SIEM, HRIS and much more.
> _❗ WARNING: This repository is copyrighted (unlike our [main repo](https://github.com/calcom/cal.com)). You are not allowed to use this code to host your own version of app.cal.com without obtaining a proper [license](https://cal.com/enterprise) first❗_
## Setting up Stripe
1. Create a stripe account or use an existing one. For testing, you should use all stripe dashboard functions with the Test-Mode toggle in the top right activated.
2. Open [Stripe ApiKeys](https://dashboard.stripe.com/apikeys) save the token starting with `pk_...` to `NEXT_PUBLIC_STRIPE_PUBLIC_KEY` and `sk_...` to `STRIPE_PRIVATE_KEY` in the .env file.
3. Open [Stripe Connect Settings](https://dashboard.stripe.com/settings/connect) and activate OAuth for Standard Accounts
4. Add `<CALENDSO URL>/api/integrations/stripepayment/callback` as redirect URL.
5. Copy your client*id (`ca*...`) to `STRIPE_CLIENT_ID` in the .env file.
6. Open [Stripe Webhooks](https://dashboard.stripe.com/webhooks) and add `<CALENDSO URL>/api/integrations/stripepayment/webhook` as webhook for connected applications.
7. Select all `payment_intent` events for the webhook.
8. Copy the webhook secret (`whsec_...`) to `STRIPE_WEBHOOK_SECRET` in the .env file.
## Setting up SAML login
1. Set SAML_DATABASE_URL to a postgres database. Please use a different database than the main Cal instance since the migrations are separate for this database. For example `postgresql://postgres:@localhost:5450/cal-saml`
2. Set SAML_ADMINS to a comma separated list of admin emails from where the SAML metadata can be uploaded and configured.
3. Create a SAML application with your Identity Provider (IdP) using the instructions here - [SAML Setup](../docs/saml-setup.md)
4. Remember to configure access to the IdP SAML app for all your users (who need access to Cal).
5. You will need the XML metadata from your IdP later, so keep it accessible.
6. Log in to one of the admin accounts configured in SAML_ADMINS and then navigate to Settings -> Security.
7. You should see a SAML configuration section, copy and paste the XML metadata from step 5 and click on Save.
8. Your provisioned users can now log into Cal using SAML.
@@ -1,13 +0,0 @@
import HelpscoutMenuItem from "@ee/lib/helpscout/HelpscoutMenuItem";
import IntercomMenuItem from "@ee/lib/intercom/IntercomMenuItem";
import ZendeskMenuItem from "@ee/lib/zendesk/ZendeskMenuItem";
export default function HelpMenuItem() {
return (
<>
<IntercomMenuItem />
<ZendeskMenuItem />
<HelpscoutMenuItem />
</>
);
}
@@ -1,39 +0,0 @@
import twilio from "twilio";
let TWILIO_SID, TWILIO_TOKEN, TWILIO_MESSAGING_SID: string, client: twilio.Twilio;
// Only assign the API keys if they exist in .env
if (process.env.TWILIO_SID && process.env.TWILIO_TOKEN && process.env.TWILIO_MESSAGING_SID) {
TWILIO_SID = process.env.TWILIO_SID;
TWILIO_TOKEN = process.env.TWILIO_TOKEN;
TWILIO_MESSAGING_SID = process.env.TWILIO_MESSAGING_SID;
client = twilio(TWILIO_SID, TWILIO_TOKEN);
} else {
console.error("Twilio credentials are missing from the .env file");
}
export const sendSMS = async (phoneNumber: string, body: string) => {
const response = await client.messages.create({
body: body,
messagingServiceSid: TWILIO_MESSAGING_SID,
to: phoneNumber,
});
return response;
};
export const scheduleSMS = async (phoneNumber: string, body: string, scheduledDate: Date) => {
const response = await client.messages.create({
body: body,
messagingServiceSid: TWILIO_MESSAGING_SID,
to: phoneNumber,
scheduleType: "fixed",
sendAt: scheduledDate,
});
return response;
};
export const cancelSMS = async (referenceId: string) => {
await client.messages(referenceId).update({ status: "canceled" });
};
@@ -1,31 +0,0 @@
import dayjs from "@calcom/dayjs";
const emailReminderTemplate = (
startTime: string,
eventName: string,
timeZone: string,
attendee: string,
name: string
) => {
const emailSubject = `Reminder: ${eventName} on ${dayjs(startTime)
.tz(timeZone)
.format("YYYY MMM D")} at ${dayjs(startTime).tz(timeZone).format("h:mmA")} ${timeZone}.`;
const templateBodyText = `Hi ${name}, this is a reminder that your meeting (${eventName}) with ${attendee} is on ${dayjs(
startTime
)
.tz(timeZone)
.format("YYYY MMM D")} at ${dayjs(startTime).tz(timeZone).format("h:mmA")} ${timeZone}.`;
const templateBodyHtml = `<body>Hi ${name},<br><br>This is a reminder that your meeting (${eventName}) with ${attendee} is on ${dayjs(
startTime
)
.tz(timeZone)
.format("YYYY MMM D")} at ${dayjs(startTime).tz(timeZone).format("h:mmA")} ${timeZone}.<body>`;
const emailBody = { text: templateBodyText, html: templateBodyHtml };
return { emailSubject, emailBody };
};
export default emailReminderTemplate;
+2 -3
View File
@@ -9,8 +9,8 @@ import {
} from "react-query";
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
import { trpc } from "@calcom/trpc/react";
import type { UseTRPCQueryOptions } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import type {
inferHandlerInput,
inferProcedureInput,
@@ -19,8 +19,7 @@ import type {
} from "@calcom/trpc/server";
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import { Alert } from "@calcom/ui/Alert";
import Loader from "@components/Loader";
import Loader from "@calcom/ui/Loader";
type ErrorLike = {
message: string;
+38 -6
View File
@@ -1,11 +1,15 @@
import { SessionProvider } from "next-auth/react";
import { EventCollectionProvider } from "next-collect/client";
import { appWithTranslation } from "next-i18next";
import { ThemeProvider } from "next-themes";
import type { AppProps as NextAppProps, AppProps as NextJsAppProps } from "next/app";
import { NextRouter } from "next/router";
import { ComponentProps, ReactNode } from "react";
import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic";
import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic";
import { ContractsProvider } from "@calcom/features/ee/web3/contexts/contractsContext";
import { trpc } from "@calcom/trpc/react";
import DynamicHelpscoutProvider from "@ee/lib/helpscout/providerDynamic";
import DynamicIntercomProvider from "@ee/lib/intercom/providerDynamic";
import usePublicPage from "@lib/hooks/usePublicPage";
@@ -15,7 +19,11 @@ const I18nextAdapter = appWithTranslation<NextJsAppProps & { children: React.Rea
// Workaround for https://github.com/vercel/next.js/issues/8592
export type AppProps = Omit<NextAppProps, "Component"> & {
Component: NextAppProps["Component"] & { requiresLicense?: boolean; isThemeSupported?: boolean };
Component: NextAppProps["Component"] & {
requiresLicense?: boolean;
isThemeSupported?: boolean | ((arg: { router: NextRouter }) => boolean);
getLayout?: (page: React.ReactElement) => ReactNode;
};
/** Will be defined only is there was an error */
err?: Error;
};
@@ -48,10 +56,34 @@ const AppProviders = (props: AppPropsWithChildren) => {
const session = trpc.useQuery(["viewer.public.session"]).data;
// No need to have intercom on public pages - Good for Page Performance
const isPublicPage = usePublicPage();
const isThemeSupported =
typeof props.Component.isThemeSupported === "function"
? props.Component.isThemeSupported({ router: props.router })
: props.Component.isThemeSupported;
const forcedTheme = isThemeSupported ? undefined : "light";
// Use namespace of embed to ensure same namespaced embed are displayed with same theme. This allows different embeds on the same website to be themed differently
// One such example is our Embeds Demo and Testing page at http://localhost:3100
// Having `getEmbedNamespace` defined on window before react initializes the app, ensures that embedNamespace is available on the first mount and can be used as part of storageKey
const embedNamespace = typeof window !== "undefined" ? window.getEmbedNamespace() : null;
const storageKey = typeof embedNamespace === "string" ? `embed-theme-${embedNamespace}` : "theme";
const RemainingProviders = (
<SessionProvider session={session || undefined}>
<CustomI18nextProvider {...props}>{props.children}</CustomI18nextProvider>
</SessionProvider>
<EventCollectionProvider options={{ apiPath: "/api/collect-events" }}>
<ContractsProvider>
<SessionProvider session={session || undefined}>
<CustomI18nextProvider {...props}>
{/* color-scheme makes background:transparent not work which is required by embed. We need to ensure next-theme adds color-scheme to `body` instead of `html`(https://github.com/pacocoursey/next-themes/blob/main/src/index.tsx#L74). Once that's done we can enable color-scheme support */}
<ThemeProvider
enableColorScheme={false}
storageKey={storageKey}
forcedTheme={forcedTheme}
attribute="class">
{props.children}
</ThemeProvider>
</CustomI18nextProvider>
</SessionProvider>
</ContractsProvider>
</EventCollectionProvider>
);
if (isPublicPage) {
+5
View File
@@ -1,19 +1,24 @@
/** @deprecated use zod instead */
export function asStringOrNull(str: unknown) {
return typeof str === "string" ? str : null;
}
/** @deprecated use zod instead */
export function asStringOrUndefined(str: unknown) {
return typeof str === "string" ? str : undefined;
}
/** @deprecated use zod instead */
export function asNumberOrUndefined(str: unknown) {
return typeof str === "string" ? parseInt(str) : undefined;
}
/** @deprecated use zod instead */
export function asNumberOrThrow(str: unknown) {
return parseInt(asStringOrThrow(str));
}
/** @deprecated use zod instead */
export function asStringOrThrow(str: unknown): string {
if (typeof str !== "string") {
throw new Error(`Expected "string" - got ${typeof str}`);
+1 -2
View File
@@ -1,9 +1,8 @@
// handles logic related to user clock display using 24h display / timeZone options.
import dayjs from "@calcom/dayjs";
import { isBrowserLocale24h } from "@calcom/lib/timeFormat";
import { localStorage } from "@calcom/lib/webstorage";
import { isBrowserLocale24h } from "./timeFormat";
interface TimeOptions {
is24hClock: boolean;
inviteeTimeZone: string;
+1 -1
View File
@@ -1,10 +1,10 @@
import parser from "accept-language-parser";
import { IncomingMessage } from "http";
import prisma from "@calcom/prisma";
import { Maybe } from "@calcom/trpc/server";
import { getSession } from "@lib/auth";
import prisma from "@lib/prisma";
import { i18n } from "../../../next-i18next.config";
-16
View File
@@ -1,16 +0,0 @@
import { User } from "@prisma/client";
import dayjs from "@calcom/dayjs";
export const ONBOARDING_INTRODUCED_AT = dayjs("September 1 2021").toISOString();
export const ONBOARDING_NEXT_REDIRECT = {
redirect: {
permanent: false,
destination: "/getting-started",
},
} as const;
export const shouldShowOnboarding = (user: Pick<User, "createdDate" | "completedOnboarding">) => {
return !user.completedOnboarding && dayjs(user.createdDate).isAfter(ONBOARDING_INTRODUCED_AT);
};
-36
View File
@@ -1,36 +0,0 @@
import { TFunction } from "next-i18next";
import { LocationType } from "./location";
/**
* Use this function for translating event location to a readable string
* @param location
* @param t
* @returns string
*/
export const LocationOptionsToString = (location: string, t: TFunction) => {
switch (location) {
case LocationType.InPerson:
return t("set_address_place");
case LocationType.Link:
return t("set_link_meeting");
case LocationType.Phone:
return t("cal_invitee_phone_number_scheduling");
case LocationType.GoogleMeet:
return t("cal_provide_google_meet_location");
case LocationType.Zoom:
return t("cal_provide_zoom_meeting_url");
case LocationType.Daily:
return t("cal_provide_video_meeting_url");
case LocationType.Jitsi:
return t("cal_provide_jitsi_meeting_url");
case LocationType.Huddle01:
return t("cal_provide_huddle01_meeting_url");
case LocationType.Tandem:
return t("cal_provide_tandem_meeting_url");
case LocationType.Teams:
return t("cal_provide_teams_meeting_url");
default:
return null;
}
};
+2 -3
View File
@@ -2,10 +2,9 @@ import { I18n } from "next-i18next";
import { RRule } from "rrule";
import dayjs, { Dayjs } from "@calcom/dayjs";
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
import { inferQueryOutput } from "@calcom/trpc/react";
import { RecurringEvent } from "@calcom/types/Calendar";
import { detectBrowserTimeFormat } from "@lib/timeFormat";
import type { RecurringEvent } from "@calcom/types/Calendar";
import { parseZone } from "./parseZone";
@@ -1,11 +0,0 @@
import { EventType } from "@prisma/client";
import * as fetch from "@lib/core/http/fetch-wrapper";
type GetEventsResponse = { message: string; data: EventType[] };
const getEventTypes = async () => {
const response = await fetch.get<GetEventsResponse>("/api/event-type");
return response.data;
};
export default getEventTypes;
+1 -1
View File
@@ -1,6 +1,6 @@
import { WebhookTriggerEvents } from "@prisma/client";
import prisma from "@lib/prisma";
import prisma from "@calcom/prisma";
export type GetSubscriberOptions = {
userId: number;
+18 -1
View File
@@ -1,8 +1,25 @@
import { collectEvents } from "next-collect/server";
// eslint-disable-next-line @next/next/no-server-import-in-page
import { NextMiddleware, NextResponse } from "next/server";
import { extendEventData, nextCollectBasicSettings } from "@lib/telemetry";
import { extendEventData, nextCollectBasicSettings } from "@calcom/lib/telemetry";
const V2_WHITELIST = ["/settings/admin"];
const middleware: NextMiddleware = async (req) => {
const url = req.nextUrl;
/** Display available V2 pages to users who opted-in to early access */
if (req.cookies.has("calcom-v2-early-access") && V2_WHITELIST.some((p) => url.pathname.startsWith(p))) {
// rewrite to the current subdomain under the pages/sites folder
url.pathname = `/v2${url.pathname}`;
}
return NextResponse.rewrite(url);
};
export default collectEvents({
middleware,
...nextCollectBasicSettings,
cookieName: "__clnds",
extend: extendEventData,
+4 -2
View File
@@ -4,17 +4,18 @@ const withTM = require("next-transpile-modules")([
"@calcom/app-store",
"@calcom/core",
"@calcom/dayjs",
"@calcom/ee",
"@calcom/emails",
"@calcom/embed-core",
"@calcom/embed-react",
"@calcom/embed-snippet",
"@calcom/features",
"@calcom/lib",
"@calcom/prisma",
"@calcom/stripe",
"@calcom/trpc",
"@calcom/ui",
]);
const { withAxiom } = require("next-axiom");
const { i18n } = require("./next-i18next.config");
if (!process.env.NEXTAUTH_SECRET) throw new Error("Please set NEXTAUTH_SECRET");
@@ -71,6 +72,7 @@ if (process.env.ANALYZE === "true") {
}
plugins.push(withTM);
plugins.push(withAxiom);
/** @type {import("next").NextConfig} */
const nextConfig = {
+9 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "1.8.2",
"version": "1.8.5",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
@@ -28,17 +28,16 @@
},
"dependencies": {
"@boxyhq/saml-jackson": "0.3.6",
"@calcom/app-store-cli": "*",
"@calcom/app-store": "*",
"@calcom/app-store-cli": "*",
"@calcom/core": "*",
"@calcom/dayjs": "*",
"@calcom/ee": "*",
"@calcom/embed-core": "*",
"@calcom/embed-react": "*",
"@calcom/embed-snippet": "*",
"@calcom/features": "*",
"@calcom/lib": "*",
"@calcom/prisma": "*",
"@calcom/stripe": "*",
"@calcom/trpc": "*",
"@calcom/tsconfig": "*",
"@calcom/ui": "*",
@@ -46,7 +45,7 @@
"@glidejs/glide": "^3.5.2",
"@heroicons/react": "^1.0.6",
"@hookform/error-message": "^2.0.0",
"@hookform/resolvers": "^2.8.9",
"@hookform/resolvers": "^2.9.7",
"@metamask/providers": "^8.1.1",
"@next-auth/prisma-adapter": "^1.0.3",
"@next/bundle-analyzer": "12.1.6",
@@ -77,11 +76,13 @@
"kbar": "^0.1.0-beta.36",
"libphonenumber-js": "^1.9.53",
"lodash": "^4.17.21",
"markdown-it": "^13.0.1",
"memory-cache": "^0.2.0",
"micro": "^9.3.4",
"mime-types": "^2.1.35",
"next": "^12.2.0",
"next-auth": "^4.9.0",
"next-auth": "^4.10.3",
"next-axiom": "^0.10.0",
"next-collect": "^0.2.0",
"next-i18next": "^11.0.0",
"next-mdx-remote": "^4.0.3",
@@ -131,6 +132,7 @@
"@types/glidejs__glide": "^3.4.2",
"@types/jest": "^27.5.1",
"@types/lodash": "^4.14.182",
"@types/markdown-it": "^12.2.3",
"@types/memory-cache": "^0.2.2",
"@types/micro": "7.3.6",
"@types/mime-types": "^2.1.1",
@@ -147,7 +149,7 @@
"autoprefixer": "^10.4.7",
"babel-jest": "^27.3.1",
"env-cmd": "10.1.0",
"eslint": "^8.15.0",
"eslint": "^8.16.0",
"jest": "^26.0.0",
"mockdate": "^3.0.5",
"module-alias": "^2.2.2",
+12 -13
View File
@@ -1,11 +1,10 @@
import { BookOpenIcon, CheckIcon, DocumentTextIcon } from "@heroicons/react/outline";
import { ChevronRightIcon } from "@heroicons/react/solid";
import { GetStaticPropsContext } from "next";
import Link from "next/link";
import { useRouter } from "next/router";
import React, { useEffect, useState } from "react";
import { DOCS_URL, JOIN_SLACK, WEBSITE_URL } from "@calcom/lib/constants";
import { Icon } from "@calcom/ui/Icon";
import { useLocale } from "@lib/hooks/useLocale";
@@ -23,13 +22,13 @@ export default function Custom404() {
{
title: t("documentation"),
description: t("documentation_description"),
icon: DocumentTextIcon,
icon: Icon.FiFileText,
href: DOCS_URL,
},
{
title: t("blog"),
description: t("blog_description"),
icon: BookOpenIcon,
icon: Icon.FiBookOpen,
href: `${WEBSITE_URL}/blog`,
},
];
@@ -78,7 +77,7 @@ export default function Custom404() {
className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0">
<span className="flex h-12 w-12 items-center justify-center rounded-lg bg-green-50">
<CheckIcon className="h-6 w-6 text-green-500" aria-hidden="true" />
<Icon.FiCheck className="h-6 w-6 text-green-500" aria-hidden="true" />
</span>
</div>
<div className="min-w-0 flex-1">
@@ -93,7 +92,7 @@ export default function Custom404() {
<p className="text-base text-gray-500">{t("the_infrastructure_plan")}</p>
</div>
<div className="flex-shrink-0 self-center">
<ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
<Icon.FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div>
</a>
</li>
@@ -105,7 +104,7 @@ export default function Custom404() {
<a className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0">
<span className="flex h-12 w-12 items-center justify-center rounded-lg bg-gray-50">
<DocumentTextIcon className="h-6 w-6 text-gray-700" aria-hidden="true" />
<Icon.FiFileText className="h-6 w-6 text-gray-700" aria-hidden="true" />
</span>
</div>
<div className="min-w-0 flex-1">
@@ -118,7 +117,7 @@ export default function Custom404() {
<p className="text-base text-gray-500">{t("prisma_studio_tip_description")}</p>
</div>
<div className="flex-shrink-0 self-center">
<ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
<Icon.FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div>
</a>
</Link>
@@ -164,7 +163,7 @@ export default function Custom404() {
<p className="text-base text-gray-500">{t("join_our_community")}</p>
</div>
<div className="flex-shrink-0 self-center">
<ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
<Icon.FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div>
</a>
</li>
@@ -216,7 +215,7 @@ export default function Custom404() {
<a className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0">
<span className="flex h-12 w-12 items-center justify-center rounded-lg bg-green-50">
<CheckIcon className="h-6 w-6 text-green-500" aria-hidden="true" />
<Icon.FiCheck className="h-6 w-6 text-green-500" aria-hidden="true" />
</span>
</div>
<div className="min-w-0 flex-1">
@@ -233,7 +232,7 @@ export default function Custom404() {
</p>
</div>
<div className="flex-shrink-0 self-center">
<ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
<Icon.FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div>
</a>
</Link>
@@ -261,7 +260,7 @@ export default function Custom404() {
<p className="text-base text-gray-500">{link.description}</p>
</div>
<div className="flex-shrink-0 self-center">
<ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
<Icon.FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div>
</a>
</Link>
@@ -308,7 +307,7 @@ export default function Custom404() {
<p className="text-base text-gray-500">{t("join_our_community")}</p>
</div>
<div className="flex-shrink-0 self-center">
<ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
<Icon.FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div>
</a>
</li>
+15 -13
View File
@@ -1,5 +1,3 @@
import { ArrowRightIcon } from "@heroicons/react/outline";
import { BadgeCheckIcon } from "@heroicons/react/solid";
import { UserPlan } from "@prisma/client";
import classNames from "classnames";
import { GetServerSidePropsContext } from "next";
@@ -16,6 +14,7 @@ import {
useEmbedStyles,
useIsEmbed,
} from "@calcom/embed-core/embed-iframe";
import type { CryptoSectionProps } from "@calcom/features/ee/web3/components/CryptoSection";
import CustomBranding from "@calcom/lib/CustomBranding";
import defaultEvents, {
getDynamicEventDescription,
@@ -24,12 +23,13 @@ import defaultEvents, {
getUsernameSlugLink,
} from "@calcom/lib/defaultEvents";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { baseEventTypeSelect } from "@calcom/prisma/selects/event-types";
import useTheme from "@calcom/lib/hooks/useTheme";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import prisma from "@calcom/prisma";
import { baseEventTypeSelect } from "@calcom/prisma/selects";
import { BadgeCheckIcon, Icon } from "@calcom/ui/Icon";
import { useExposePlanGlobally } from "@lib/hooks/useExposePlanGlobally";
import useTheme from "@lib/hooks/useTheme";
import prisma from "@lib/prisma";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
import { inferSSRProps } from "@lib/types/inferSSRProps";
import AvatarGroup from "@components/ui/AvatarGroup";
@@ -39,7 +39,9 @@ import { ssrInit } from "@server/lib/ssr";
const EventTypeDescription = dynamic(() => import("@components/eventtype/EventTypeDescription"));
const HeadSeo = dynamic(() => import("@components/seo/head-seo"));
const CryptoSection = dynamic(() => import("../ee/components/web3/CryptoSection"));
const CryptoSection = dynamic<CryptoSectionProps>(
() => import("@calcom/features/ee/web3/components/CryptoSection")
);
interface EvtsToVerify {
[evtId: string]: boolean;
@@ -67,8 +69,8 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
{eventTypes.map((type, index) => (
<li
key={index}
className="hover:border-brand group relative rounded-sm border border-neutral-200 bg-white hover:bg-gray-50 dark:border-neutral-700 dark:bg-gray-800 dark:hover:border-neutral-600">
<ArrowRightIcon className="absolute right-3 top-3 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
className="hover:border-brand group relative rounded-sm border border-neutral-200 bg-white dark:border-neutral-700 dark:bg-gray-800 dark:hover:border-neutral-600">
<Icon.FiArrowRight className="absolute right-3 top-3 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
<Link href={getUsernameSlugLink({ users: props.users, slug: type.slug })}>
<a className="flex justify-between px-6 py-4" data-testid="event-type-link">
<div className="flex-shrink">
@@ -145,7 +147,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
<p className="text-neutral-500 dark:text-white">{user.bio}</p>
</div>
)}
<div className="space-y-6" data-testid="event-types">
<div className="space-y-3" data-testid="event-types">
{user.away ? (
<div className="overflow-hidden rounded-sm border dark:border-gray-900">
<div className="p-8 text-center text-gray-400 dark:text-white">
@@ -162,8 +164,8 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
<div
key={type.id}
style={{ display: "flex", ...eventTypeListItemEmbedStyles }}
className="hover:border-brand group relative rounded-sm border border-neutral-200 bg-white hover:bg-gray-50 dark:border-neutral-700 dark:bg-gray-800 dark:hover:border-neutral-600">
<ArrowRightIcon className="absolute right-3 top-3 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
className="hover:border-brand group relative rounded border border-neutral-200 bg-white hover:bg-white dark:border-neutral-700 dark:bg-gray-800 dark:hover:border-neutral-600">
<Icon.FiArrowRight className="absolute right-4 top-4 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" />
{/* Don't prefetch till the time we drop the amount of javascript in [user][type] page which is impacting score for [user] page */}
<Link
prefetch={false}
@@ -187,7 +189,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
});
}
}}
className="block w-full px-6 py-4"
className="block w-full p-5"
data-testid="event-type-link">
<h2 className="grow font-semibold text-neutral-900 dark:text-white">{type.title}</h2>
<EventTypeDescription eventType={type} />
+1 -1
View File
@@ -11,10 +11,10 @@ import {
} from "@calcom/lib/defaultEvents";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { bookEventTypeSelect } from "@calcom/prisma";
import prisma from "@calcom/prisma";
import { asStringOrNull, asStringOrThrow } from "@lib/asStringOrNull";
import getBooking, { GetBookingType } from "@lib/getBooking";
import prisma from "@lib/prisma";
import { inferSSRProps } from "@lib/types/inferSSRProps";
import BookingPage from "@components/booking/pages/BookingPage";

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