Compare commits

...
549 Commits
Author SHA1 Message Date
Alex van Andel a810a538e9 v3.2.5
release.yml / release (push) Canceled after 0s
2023-08-25 17:23:22 +01:00
sean-brydon 1cd0d7b5ca use team id :/ (#10957) 2023-08-25 16:14:28 +00:00
Alex van Andel 4540c79580 v3.2.4
release.yml / release (push) Canceled after 0s
2023-08-25 15:32:55 +01:00
Udit Takkar 00d01882d4 fix: private team regression (#10914)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-08-25 14:43:48 +01:00
Omar López 6ce4122528 fix: allows patching booking and period limits from API (#10935) 2023-08-25 14:29:04 +01:00
7ddbcfcdcf feat: Adds filter-by-email on /users/_get endpoint for admin calls (#10928)
* adds filter by email on users get for admins

* swagger update

* Update apps/api/pages/api/users/_get.ts

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

* .parse is required, also fixed some other issues

---------

Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-08-25 14:15:13 +01:00
b1b01ed383 remove close button from TopBanner (#10705)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
2023-08-25 15:50:39 +03:00
6f5d9dc41f test: Create unit tests for react components in packages/ui/components/form/select (#10459)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-08-25 12:18:53 +00:00
GitStart-Cal.comandgitstart-calcom ac190b89dd chore: add UnpublishedEntity in storybook (CALCOM-10760) (#10864)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-08-25 15:12:19 +03:00
990441295c chore: add ImageUploader in storybook (CALCOM-10760) (#10865)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-08-25 15:06:55 +03:00
1e4c20073c chore: fix all @typescript-eslint/no-non-null-assertion warnings (#10635)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-08-25 14:56:20 +03:00
80353d67a0 chore: fix all @typescript-eslint/no-unused-vars warnings (#10645)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-08-25 11:48:24 +00:00
Keith Williams d55b4b051a perf: Fix slowest db query (again) (#10954) 2023-08-25 14:27:37 +03:00
Hariom Balhara 25d90e3e12 test: Upcoming bookings sorting order (#10950)
* Fix order by for bookings array
Fix orderBy for bookings

* Test

* Text change

* Add comments
2023-08-25 12:55:41 +03:00
Hariom Balhara 1727eb5625 fix: Unordered bookings (#10943) 2023-08-25 15:06:57 +05:30
sean-brydon 58f4224c66 Fix invite flow (#10951) 2023-08-25 09:19:03 +00:00
Udit Takkar 5d491e0a12 fix: unauthorized after email change (#10821) 2023-08-24 16:47:34 -07:00
alannncandOmar López 1eef5a56c3 fix: include orgs slug in getUserByUsername just like [user]/[type] page (#10723)
Co-authored-by: Omar López <zomars@me.com>
2023-08-24 16:41:06 -07:00
d6740503be feat: Column View Available Slots Fix (#9815)
Co-authored-by: Vinoth Kumar V <vinoth_kumar_v@Vinoths-MacBook-Pro.local>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2023-08-24 16:39:45 -07:00
David Oksman 9f647ead46 fix: missing timezones in dropdown list (#10823) 2023-08-24 13:57:11 -07:00
Thibault Mathian f480afcace fix: caching and fetching of feature flags (#10837) 2023-08-24 13:43:52 -07:00
Hariom Balhara 8d73d15b8a perf: Lazy load framer motion stuff as much as we can (#10876) 2023-08-24 13:40:01 -07:00
Hariom Balhara e8370ef950 perf: Remove non booker apps from booker page (#10905) 2023-08-24 13:38:15 -07:00
Hariom Balhara e859c8dc58 test: Add attendee phone number test (#10932) 2023-08-24 11:21:49 -07:00
Keith Williams b4b38b9bc3 v3.2.3
release.yml / release (push) Canceled after 0s
2023-08-24 17:16:02 +03:00
Crowdin Bot d32719ad8b New Crowdin translations by Github Action 2023-08-24 12:47:12 +00:00
Hariom Balhara 99d0816d9f phoneSchema missing async parsing (#10929) 2023-08-24 12:43:52 +00:00
95f4b89fe8 fix: #10783 (UI updated on API Creation Form ) (#10786)
* Fix #10783

* Update the changes #10783

* Updated the changes on the another dialog footer

* Updated the changes

---------

Co-authored-by: Hem Pun <hemthegentlmen@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-08-24 11:20:48 +01:00
Abhijeet SinghandAbhijeet Singh 3e63c2b717 fix: Italics not being shown in the description after saving (#10848)
* added ignore rule for em tag in turndown

* added ignore rule for em tag in turndown

---------

Co-authored-by: Abhijeet Singh <asing9829@gmail.com>
2023-08-24 11:20:00 +01:00
Omar López 42cd56578d fix: Reduced boilerplate for publicViewer router (#10849) 2023-08-24 11:19:24 +01:00
Ujwal KumarandBailey Pumfleet 31d4cb4eb7 Transform # character to - in slug (#10896)
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-08-24 11:19:10 +01:00
sean-brydonandBailey Pumfleet fde3e8bea9 Invalidate and add success message (#10907)
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-08-24 11:18:59 +01:00
GitStart-Cal.comandgitstart-calcom 874ad29676 chore: add Table in storybook (CALCOM-10760) (#10869)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-08-24 11:07:57 +01:00
Hariom Balhara 5503d9d432 perf: bookings query improvement (#10687) 2023-08-24 11:14:10 +02:00
Keith WilliamsandShivam Kalra 43b3d68447 chore: Don't render certain components when running meticulous tests (#10772)
* chore: Don't render credits when running meticulous tests

* fix: type window Meticulous

* fix: window reference error

* chore: window interface

* chore: disable tzdialog popup on meticulous

* chore: IS_VISUAL_REGRESSION_TESTING

* fix: constants

* fix: only run on preview builds

---------

Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
2023-08-24 13:42:53 +05:30
GitStart-Cal.comandgitstart-calcom e3c747a867 test: Create unit tests for react components is packages/ui/components/navigation (#10698)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-08-24 13:23:54 +05:30
sean-brydonandOmar López e020a2c9a4 feat: Availability Slider enabled for teams (#10902)
Co-authored-by: Omar López <zomars@me.com>
2023-08-23 21:23:24 +00:00
Leo Giovanetti e6e6f09547 feat: admin org list now shows all, dns check added (#10875)
* chore: admin org list now shows all, dns check added

* Updating email template

* type fixes
2023-08-23 21:01:12 +00:00
Leo GiovanettiandJoe Au-Yeung 68ec946c59 fix: add org avatar to org booking pages [CAL-2333] (#10847)
* Add org avatar to org booking pages

* Using web app url for org avatars + missing i18n string

---------

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2023-08-23 20:10:02 +00:00
Omar López 5a6e5ce427 v3.2.2
release.yml / release (push) Canceled after 0s
2023-08-23 11:49:37 -07:00
Carina WollendorferandCarinaWolli b2382211db fix: adding a new workflow step that requires kyc verification (#10912)
* fix verificaiton check when step is added

* add verify badge with dialog also when adding action

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-08-23 20:32:14 +02:00
Joe Au-Yeung 12148506bd fix: Remove multiple durations for seated events (#10910)
* Remove multiple durations for seated events

* Clean up
2023-08-23 18:25:44 +00:00
MontoandSMLukwiya b5666fd4c3 fix: team availability shows only to the member owning it (#10738)
Co-authored-by: SMLukwiya <sundaymorganl@gmail.com>
2023-08-23 12:27:19 -04:00
Omar López bc7da563d8 fix: Prevents mixed server and client code (#10909) 2023-08-23 16:25:43 +00:00
d3acf4160d fix: data collection for headless router (#9183)
Co-authored-by: Wesley <100464352+ologbonowiwi@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-08-23 11:12:51 -04:00
Crowdin Bot 65e0c0e4c5 New Crowdin translations by Github Action 2023-08-23 12:16:51 +00:00
nicktrn 2dda6a509b chore: cron workflow tidying (#10127) 2023-08-23 17:43:22 +05:30
Crowdin Bot 7238414864 New Crowdin translations by Github Action 2023-08-23 09:11:55 +00:00
Shivam Kalra bab41ca8a6 chore: expect emails e2e (#10881)
* refactor: emails e2e

* fix: email subject with nameless organizer

* fix: comment on NEXT_PUBLIC_IS_E2E

* chore: E2E_TEST_MAILHOG_ENABLED env

* feat: IS_MAILHOG_ENABLED

* doc: E2E_TEST_MAILHOG_ENABLED

* fix: comment

* fix: env order

* chore: add E2E_TEST_MAILHOG_ENABLED to e2e env
2023-08-23 11:08:14 +02:00
Carina WollendorferandCarinaWolli 376f023e0f fix: scheduling workflow emails (#10890)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-08-22 12:29:06 -07:00
zomars 0cbf525a78 fix: Disables es-419 until translated 2023-08-22 12:01:50 -07:00
zomars f56fefec4d fix: Disables es-419 until translated 2023-08-22 12:00:20 -07:00
Crowdin Bot 66eccd6dd8 New Crowdin translations by Github Action 2023-08-22 18:12:25 +00:00
Omar López c4b9a9e2f4 fix: client locale inference (#10893)
* fix: client locale inference

* Update I18nLanguageHandler.tsx

* More fixes

* Update I18nLanguageHandler.tsx

* Update I18nLanguageHandler.tsx

* Addressing feedback

* Fix: first render issues

* Update Credits.tsx

* Fixed tests

* fix: try without loose locale pick

* Revert "fix: try without loose locale pick"

This reverts commit 5ed649c13117fee8bc507098353de0f18e98d971.

* fix: sorts locales so specifics are chosen first

* fix: switches client locale matcher
2023-08-22 20:08:34 +02:00
Crowdin Bot 38921e58cd New Crowdin translations by Github Action 2023-08-22 13:46:36 +00:00
Crowdin Bot 2c9c6c77f7 New Crowdin translations by Github Action 2023-08-22 13:43:30 +00:00
Syed Ali Shahbaz 46605e049d fix: Improves the Login button for SAML/OIDC (#10885) 2023-08-22 15:39:50 +02:00
Crowdin Bot 01e1e82ced New Crowdin translations by Github Action 2023-08-22 13:20:24 +00:00
Crowdin Bot 8e17716094 New Crowdin translations by Github Action 2023-08-22 13:17:20 +00:00
Crowdin Bot 7c028b6479 New Crowdin translations by Github Action 2023-08-22 13:14:18 +00:00
Crowdin Bot cba4bae5cd New Crowdin translations by Github Action 2023-08-22 13:10:46 +00:00
Omar López 55046f3017 chore: add constant for production builds (#10762) 2023-08-22 13:07:32 +00:00
Crowdin Bot 4836621c31 New Crowdin translations by Github Action 2023-08-22 12:37:46 +00:00
Omar López 6743aa4609 fix: client locale inference (#10850) 2023-08-22 14:34:55 +02:00
Crowdin Bot bbad0fbc18 New Crowdin translations by Github Action 2023-08-22 12:32:40 +00:00
Crowdin Bot 8f6812ecb2 New Crowdin translations by Github Action 2023-08-22 12:30:01 +00:00
Crowdin Bot 303b09f341 New Crowdin translations by Github Action 2023-08-22 12:27:07 +00:00
Crowdin Bot 1f3ee41eb4 New Crowdin translations by Github Action 2023-08-22 12:24:09 +00:00
Crowdin Bot 01c711a81e New Crowdin translations by Github Action 2023-08-22 12:21:37 +00:00
Aswath SandHariom Balhara 16bb5c3384 feat: Added more query params to successRedirect page (#8271)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-08-22 12:18:36 +00:00
Crowdin Bot c55d97368e New Crowdin translations by Github Action 2023-08-22 11:09:29 +00:00
Crowdin Bot ef193aca31 New Crowdin translations by Github Action 2023-08-22 11:06:46 +00:00
Nate GriftandUdit Takkar c36a6797a9 test: added team & user having same name test (#10868)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-08-22 13:03:00 +02:00
f393fd4ad4 fix: Resolved Typescript Error in PR #9598 created by @vijeet-shah. (#10871)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: vijeet-shah <93502358+vijeet-shah@users.noreply.github.com>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-08-22 13:02:40 +02:00
Crowdin Bot 8987b9bdbc New Crowdin translations by Github Action 2023-08-22 10:33:33 +00:00
Crowdin Bot 68bf426af0 New Crowdin translations by Github Action 2023-08-22 10:30:43 +00:00
Crowdin Bot 1125900013 New Crowdin translations by Github Action 2023-08-22 10:27:47 +00:00
Crowdin Bot d272e823c3 New Crowdin translations by Github Action 2023-08-22 10:24:43 +00:00
Crowdin Bot 3ea11d4f38 New Crowdin translations by Github Action 2023-08-22 10:21:55 +00:00
Crowdin Bot b4dc64594c New Crowdin translations by Github Action 2023-08-22 10:19:05 +00:00
Crowdin Bot 6a763b3720 New Crowdin translations by Github Action 2023-08-22 10:16:14 +00:00
Crowdin Bot 252ac1c704 New Crowdin translations by Github Action 2023-08-22 10:13:06 +00:00
Crowdin Bot c51e42a061 New Crowdin translations by Github Action 2023-08-22 10:10:14 +00:00
Crowdin Bot e3f9e477fe New Crowdin translations by Github Action 2023-08-22 10:07:12 +00:00
Crowdin Bot b265970b01 New Crowdin translations by Github Action 2023-08-22 10:03:58 +00:00
Crowdin Bot 7b7b2ffc9a New Crowdin translations by Github Action 2023-08-22 10:00:41 +00:00
Crowdin Bot d019062709 New Crowdin translations by Github Action 2023-08-22 09:58:00 +00:00
Crowdin Bot ad9d728314 New Crowdin translations by Github Action 2023-08-22 09:55:10 +00:00
Crowdin Bot b564fb9a2f New Crowdin translations by Github Action 2023-08-22 09:52:15 +00:00
Crowdin Bot 9c05f6839c New Crowdin translations by Github Action 2023-08-22 09:49:20 +00:00
Crowdin Bot 4dea38f06d New Crowdin translations by Github Action 2023-08-22 09:46:30 +00:00
Crowdin Bot c4fc222204 New Crowdin translations by Github Action 2023-08-22 09:43:37 +00:00
Crowdin Bot 00c3a2dd80 New Crowdin translations by Github Action 2023-08-22 09:40:44 +00:00
Crowdin Bot 46fa0088f2 New Crowdin translations by Github Action 2023-08-22 09:37:44 +00:00
Crowdin Bot a19fd9b889 New Crowdin translations by Github Action 2023-08-22 09:34:39 +00:00
Crowdin Bot d379c10510 New Crowdin translations by Github Action 2023-08-22 09:31:11 +00:00
GitStart-Cal.comandgitstart-calcom f3a58145ee chore: add datePicker in storybook (CALCOM-10760) (#10784)
* Update datepicker.stories.mdx

* Update datepicker.stories.mdx

* Requested changes

---------

Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-08-22 10:27:27 +01:00
Crowdin Bot e6125dc361 New Crowdin translations by Github Action 2023-08-21 23:15:41 +00:00
zomars b78f718e9e Update yarn.lock 2023-08-21 16:12:41 -07:00
zomars 53e87f7769 fix: Removes duplicate locale keys 2023-08-21 16:11:04 -07:00
Crowdin Bot 86eb40e0fd New Crowdin translations by Github Action 2023-08-21 17:15:42 +00:00
Hariom BalharaandUdit Takkar 51caa6834a fix: Make identifier conform to RHF field requirement (#10860)
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-08-21 13:11:47 -04:00
7a7a5b5285 fix: Removing payment fee on stripe (#10652)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2023-08-21 12:33:34 +02:00
f14ffb9e44 chore: add Dialog component in storybook (#10822)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-08-21 09:58:26 +00:00
Harry Dobrev 001b524005 fix: icon in emails is considered a tracking pixel (#10807) 2023-08-21 11:48:06 +02:00
GitStart-Cal.comandgitstart-calcom a8cf4fb309 chore: add Editor in storybook (CALCOM-10760) (#10820)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-08-21 11:45:30 +02:00
Joe Au-Yeung 155775556d perf: GCal reduce calendar payload (#10853) 2023-08-21 11:27:18 +02:00
d66095bf9c test: adding confirmed payment test cases on Stripe (#10243)
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2023-08-21 11:12:39 +02:00
0ddab30340 chore: fix all @typescript-eslint/no-explicit-any warnings (#10656)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-08-21 11:01:56 +02:00
Moon Patel 2a9e061575 fix: Next/previous buttons on week view move 8 instead of 7 days (#10742) 2023-08-21 11:01:27 +02:00
Crowdin Bot 351dce5cfd New Crowdin translations by Github Action 2023-08-21 08:31:02 +00:00
Koushik Sai 3255d9012c fix: missing border-subtle class (#10838) 2023-08-21 09:28:34 +01:00
Crowdin Bot 226dcb3fcf New Crowdin translations by Github Action 2023-08-19 20:07:17 +00:00
Jon@1599andLeo Giovanetti f80e9b2558 feat: add basecamp integration to cal.com (#9195)
* feat: installing the app works

* Update yarn.lock

* feat: /api/callback now gets user auth info from basecamp

* feat: updated basecamp logo

* feat: added project dropdown on event apps page

* feat: basecamp event creation and deletion working

* feat: basecamp event rescheduling now works

* refactor(CalendarService): basecamp CaldendarService code clean up

* refactor: code cleanup for basecamp app API

* feat: updated event summary text sent to basecamp

* chore: updated basecamp images and contact info

* fix: fixed typescript errors and added logic to refresh tokens on event settings

* refactor(CaldendarService): used refreshAccessToken from helpers.ts instead

* chore: updated basecamp description

* fix: fixed incorrect import

* fix: accidentally deleted props to toggle app for event

* chore: updated .env.appStore.example and added README for app

* Update .env.appStore.example

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

* feat: added basecamp userAgent in env instead of hardcoded value

* feat: updated README to include how to set basecamp user agent env

* fix: removed unused import

* feat: used URLSearchParams to construct url params

* fix: fixed typescript errors

* chore: updated README to include an example on how to set basecamp user-agent

* feat: using TRPC instead of REST

* chore: removed old projects REST code

---------

Co-authored-by: Leo Giovanetti <hello@leog.me>
2023-08-19 17:04:56 -03:00
Keith Williamsandzomars 3f273235a6 perf: Cache translations per language/per release (#10843)
Co-authored-by: zomars <zomars@me.com>
2023-08-18 17:46:17 -07:00
zomars e8554ed5c5 fix: key warning 2023-08-18 15:50:44 -07:00
zomars a03744008c chore: Update orgs README 2023-08-18 14:26:33 -07:00
Leo GiovanettiandOmar López fe8b13885c fix: org admin has team member privileges [CAL-2338] (#10809)
Co-authored-by: Omar López <zomars@me.com>
2023-08-18 14:10:17 -07:00
Leo Giovanettiandzomars 8995dcc82a fix: org username check [CAL-2352] (#10773)
Co-authored-by: zomars <zomars@me.com>
2023-08-18 14:03:55 -07:00
zomars e8e0d03265 chore: Adds basic README for orgs 2023-08-18 12:26:59 -07:00
Crowdin Bot 56031f0358 New Crowdin translations by Github Action 2023-08-18 18:19:11 +00:00
Joe Au-Yeung 1ef0f830d3 fix: Username suffix on production [CAL-2262] (#10825) 2023-08-18 11:15:58 -07:00
Hariom Balhara 59e2f6e4a7 perf: Booker-Bundle Size Improvement - Lazy load react-select, remove lodash and Dom Purify (#10814) 2023-08-18 11:13:21 -07:00
+15 67bb2ed798 feat: Availability slider for orgs (#10740)
* Init + get timezone + offset data agh

* Add 12/24h mode - style correctly

* User users timezone + working hours. Still some stuff to figure out

* Multiple working hours

* move calc to once per day

* Demo with two users and differnt timezones

* availabillity control tab via search params

* WIP hover overlay

* THIS WORKS ISH

* fix: multiple duration getSchedule calls [CAL-2336] (#10709)

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

* New Crowdin translations by Github Action

* fix: If the input type "Name" is selected, the label can't be changed from our default label "Your Name" (#10618)

Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>

* New Crowdin translations by Github Action

* test: Create unit tests for react components in packages/ui/components/form/step (#10442)

Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>

* feat: element call app added (#10585)

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

* New Crowdin translations by Github Action

* New Crowdin translations by Github Action

* fix: e2e test for rescheduling overlapping time (#10721)

Co-authored-by: CarinaWolli <wollencarina@gmail.com>

* feat: mailhog fixture (#10606)

* feat: mailhog fixture

* fix: nodemailer to dispatch emails with e2e env

* fix: remove space from email subject

* feat: fixture getFirstEventAsOwner

* feat: assert email subjects

* fix: and enable dynamic booking test (#10642)

* fix and enable dynamic booking test

* remove page pause

---------

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

* fix: Broken team events if a user with the same name exists (#10724)

* fix: Broken team events if a user with the same name exists

* Fix tests + fix usernameList optionality

* Try to list calendars, if not continue (#10720)

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

* v3.1.9

* link to org settings (#10718)

* feat: app paypal payment (#8797)

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>

* fix: RTL issues on booking pages + email confirmation (#10526)

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

* fix: merge conflict

* Fixing org slug (#10538)

* fix: paypal build fixes

* Fix avatar for org in Shell top (#10712)

* feat: add range of dates for availability over-ride (#10462)

* feat: add range of dates for availability over-ride

* chore: changed range select to multiple select

---------

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

* fix: border issue for time slots (#10577)

Co-authored-by: Raghul D <v-raghuld@microsoft.com>

* style: Fix text wrapping issue in button (#10725)

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

* New Crowdin translations by Github Action

* fix: App Install Dropdown Sort Properly [CAL-2285] (#10672)

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

* fix: link escaping in booking page (#10360)

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

* chore: fix refund i18n message (#10731)

* chore: remove tailwind-scrollbar warning (#10523)

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>

* New Crowdin translations by Github Action

* chore: Simplified date overrides (#10728)

* chore: Simplified date overrides

* Fixed a test that had a date override that wasn't at midnight utc

* Wrote test that showed a fixed Europe/Brussels

* Lint fix

* New Crowdin translations by Github Action

* Fix offset time + fetching correct dates

* Deal with awkward minute offsets

* remove store overhead

* Format H based on tz

* Cleanup store logic

* Cleanup

* Remove comments

* Remove comments

* Remove yarn.lock

* Dark mode & v-align text fixes

* Move ButtonGroup to the left to prevent chevron from jumping

* Shift based on timezone (non-hour) and have 15min granularity in hour display background color

---------

Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Zain Gulbaz <zaingulbaz8@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: GitStart-Cal.com <121884634+gitstart-calcom@users.noreply.github.com>
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Suyash Srivastava <suyashsrivastava5053@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
Co-authored-by: Pradumn Kumar <pradumn@tealfeed.com>
Co-authored-by: Raghul <123321540+Raghul18@users.noreply.github.com>
Co-authored-by: Raghul D <v-raghuld@microsoft.com>
Co-authored-by: ABDERRAHMANI IDRISSI HAMZA <97639117+idrissi-hamza@users.noreply.github.com>
Co-authored-by: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Co-authored-by: Danila <daniil.demidovich@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-08-18 16:32:24 +00:00
Crowdin Bot ea4618a244 New Crowdin translations by Github Action 2023-08-18 15:39:49 +00:00
Keith Williams 201a9d9356 perf: Fix slowest db query (#10842) 2023-08-18 17:36:57 +02:00
Crowdin Bot 40b0d57024 New Crowdin translations by Github Action 2023-08-18 11:01:22 +00:00
761f66fc5a Create RTL tests for Meta component (#10478)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-08-18 16:28:30 +05:30
2403d77166 test: Create unit tests for react components in packages/ui/components/createButton (#10644)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
2023-08-18 14:22:14 +05:30
Anik Dhabal Babu b44f4d66f9 fix text align (#10799) 2023-08-18 12:03:04 +05:30
Joe Au-Yeung d178962c37 Fix readonly check (#10824) 2023-08-18 06:30:59 +02:00
Crowdin Bot cf4d2b4ef5 New Crowdin translations by Github Action 2023-08-17 18:00:26 +00:00
Hariom Balhara 3e4139ac23 perf: Lazy load libphonenumber-js only when validation is needed. (#10812) 2023-08-17 10:57:30 -07:00
Crowdin Bot 9fb736f67a New Crowdin translations by Github Action 2023-08-17 16:57:12 +00:00
Alex van Andel afc738141f v3.2.1
release.yml / release (push) Canceled after 0s
2023-08-17 17:36:48 +01:00
Syed Ali Shahbaz efa195059e feat: Add scheduleId to POST and PATCH requests in event-type endpoint (#10816)
* adds scheduleId to PATCH

* adds scheduleID to POST

* updated docs
2023-08-17 15:42:29 +00:00
nicktrn 0f2b6bbe1a fix: add 2fa and email verification grace period (#10771) 2023-08-17 16:13:04 +02:00
Alex van Andelandsean-brydon 28b94a865f perf: Create a seperate component for dependencies of BookEventForm (#10808)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-08-17 16:12:33 +02:00
GitStart-Cal.comandgitstart-calcom f45803fcfa chore: add Steps in storybook (CALCOM-10760) (#10810)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-08-17 09:26:08 +01:00
alannncandJoe Au-Yeung 053da1ec32 fix: currency for event types (#10792)
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
2023-08-16 14:58:28 -07:00
nicktrnandSyed Ali Shahbaz f0dcfc83c8 feat: add support for apple travel time (#10660)
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
2023-08-16 13:32:04 -07:00
5eb3af54b3 test: add apple calendar testing (#10137)
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2023-08-16 12:49:10 -07:00
Crowdin Bot 3552bbc6cf New Crowdin translations by Github Action 2023-08-16 19:46:15 +00:00
Koushik Sai 028bd77c91 feat: rank apps with most installed for a chosen category (#10756) 2023-08-16 12:43:37 -07:00
Crowdin Bot 65a1aa6100 New Crowdin translations by Github Action 2023-08-16 18:06:30 +00:00
Hariom Balhara a09401c203 perf: Booker bundle size optimization - Remove sanitize-html dependency on client (#10801) 2023-08-16 11:03:21 -07:00
sean-brydon 7c23773636 Line clamp event type bio (#10795) 2023-08-16 10:59:42 +02:00
Udit TakkarandShivam Kalra 56ba8c0a07 fix: sucess page time (#10743)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
2023-08-16 13:35:31 +05:30
alannnc 5924bc0990 fix update profile input metadata (#10791) 2023-08-15 17:27:48 -07:00
+27 10ffd9bacd feat: Org user table - bulk actions (#10504)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
Co-authored-by: cherish2003 <saicherissh90@gmail.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: rkreddy99 <rreddy@e2clouds.com>
Co-authored-by: varun thummar <Varun>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Pradumn Kumar <47187878+Pradumn27@users.noreply.github.com>
Co-authored-by: Richard Poelderl <richard.poelderl@gmail.com>
Co-authored-by: mohammed hussam <hussamkhatib20@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
Co-authored-by: nicktrn <55853254+nicktrn@users.noreply.github.com>
Co-authored-by: sydwardrae <94979838+sydwardrae@users.noreply.github.com>
Co-authored-by: Janakiram Yellapu <jyellapu@vmware.com>
Co-authored-by: GitStart-Cal.com <121884634+gitstart-calcom@users.noreply.github.com>
Co-authored-by: sajanlamsal <saznlamsal@gmail.com>
Co-authored-by: Cherish <88829894+cherish2003@users.noreply.github.com>
Co-authored-by: Danila <daniil.demidovich@gmail.com>
Co-authored-by: Neel Patel <29038590+N-NeelPatel@users.noreply.github.com>
Co-authored-by: Rama Krishna Reddy <49095575+rkreddy99@users.noreply.github.com>
Co-authored-by: Varun Thummar <110765105+VARUN949@users.noreply.github.com>
Co-authored-by: Bhargav <bhargavtenali@gmail.com>
Co-authored-by: Pratik Kumar <kpratik1929@gmail.com>
Co-authored-by: Ritesh Patil <riteshsp2000@gmail.com>
2023-08-15 14:07:38 -07:00
Leo Giovanetti 20c45b5d88 fix: vercel subdomain version (#10790)
* Standarizing org subdomain url

* fix: upgrading vercel subdomain version
2023-08-15 20:44:48 +00:00
3cab37ed8d feat: api return schedule (#10769)
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: Omar López <zomars@me.com>
2023-08-15 19:18:25 +00:00
Crowdin Bot 98799b50f0 New Crowdin translations by Github Action 2023-08-15 19:02:03 +00:00
zomars 8071016848 chore: prettier 2023-08-15 11:59:16 -07:00
Crowdin Bot aa5e2ac9a5 New Crowdin translations by Github Action 2023-08-15 14:48:59 +00:00
Keith Williams 06857a4a69 perf: Remove duplicate call for user event types (#10770)
* perf: Remove duplicate call for user events

* Removed unneeded .map() call

* Remove yarn.lock change

* Renamed variable for clarity
2023-08-15 15:46:24 +01:00
Anshuman PandeyandOmar López b0d1720d92 feat: Allow / disallow SEO indexing on booking pages (#10566)
Co-authored-by: Omar López <zomars@me.com>
2023-08-15 00:44:09 +00:00
zomars e148ee7823 chore: Removes all traces of ".prisma/client" 2023-08-14 17:28:43 -07:00
Omar López bb9a039931 fix: paypal setup and key validation on server (#10761)
* fixes: paypal setup and key validation on server

* Renamed for legiblity

* Prefer early returns

* Update getEnabledAppsFromCredentials.ts

* Updated getEnabledAppsFromCredentials references

* Update getEnabledAppsFromCredentials.ts
2023-08-14 17:18:26 -07:00
GitStart-Cal.comandgitstart-calcom 053f93ea0a Extract to a separate test (#10699)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-08-15 00:57:35 +05:30
2a982c6b6d Fix lower text of new button availability refresh (#10701)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2023-08-14 14:31:09 -04:00
MehulandUdit Takkar 85490fef4d fix: 150min duration time for scheduling not possibe (#10715)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-08-14 14:11:34 -04:00
Crowdin Bot 57afd7a2b1 New Crowdin translations by Github Action 2023-08-14 15:05:57 +00:00
Hariom BalharaandJoe Au-Yeung 0f9c3571fe fix: Show messaging app in locations list (#10741)
* Get messaging app as well through query because a messaging app is also a location app

* Fix type error

---------

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2023-08-14 15:02:29 +00:00
Peer Richelsen 1b3f676e57 Update package.json
release.yml / release (push) Canceled after 0s
2023-08-14 15:48:05 +02:00
Crowdin Bot 3e17b1c5ef New Crowdin translations by Github Action 2023-08-14 08:40:53 +00:00
Alex van Andel a466e71d4c chore: Simplified date overrides (#10728)
* chore: Simplified date overrides

* Fixed a test that had a date override that wasn't at midnight utc

* Wrote test that showed a fixed Europe/Brussels

* Lint fix
2023-08-14 09:38:08 +01:00
Crowdin Bot 5a7030d277 New Crowdin translations by Github Action 2023-08-14 06:44:24 +00:00
DanilaandUdit Takkar e72c25f02e chore: remove tailwind-scrollbar warning (#10523)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-08-14 08:41:56 +02:00
Peer Richelsen 06bc5facf3 chore: fix refund i18n message (#10731) 2023-08-13 14:26:25 +01:00
Nafees NazikandHariom Balhara 9c8dc1b92c fix: link escaping in booking page (#10360)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-08-12 21:31:43 +00:00
Joe Au-YeungandPeer Richelsen cfef92a7bc fix: App Install Dropdown Sort Properly [CAL-2285] (#10672)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-08-12 21:30:15 +00:00
Crowdin Bot 26149e2b04 New Crowdin translations by Github Action 2023-08-12 21:23:20 +00:00
ABDERRAHMANI IDRISSI HAMZAandOmar López 3ca43ce3f3 style: Fix text wrapping issue in button (#10725)
Co-authored-by: Omar López <zomars@me.com>
2023-08-12 23:21:09 +02:00
RaghulandRaghul D 7603ea6184 fix: border issue for time slots (#10577)
Co-authored-by: Raghul D <v-raghuld@microsoft.com>
2023-08-12 23:20:49 +02:00
Pradumn KumarandAlex van Andel 9c45da565e feat: add range of dates for availability over-ride (#10462)
* feat: add range of dates for availability over-ride

* chore: changed range select to multiple select

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-08-12 07:22:44 +00:00
Hariom Balhara f84f1f5dda Fix avatar for org in Shell top (#10712) 2023-08-12 00:19:27 +00:00
zomars 07f803e758 fix: paypal build fixes 2023-08-11 17:02:36 -07:00
Leo Giovanetti 268ab882cd Fixing org slug (#10538) 2023-08-11 23:57:22 +00:00
zomars 8052e6f6d4 fix: merge conflict 2023-08-11 16:56:38 -07:00
Anik Dhabal BabuandPeer Richelsen 99e9425257 fix: RTL issues on booking pages + email confirmation (#10526)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-08-11 16:39:25 -07:00
471b2687c4 feat: app paypal payment (#8797)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
2023-08-11 23:29:48 +00:00
Peer Richelsen 9f7152c4eb link to org settings (#10718) 2023-08-11 21:27:29 +00:00
Alex van Andel e819a31dd6 v3.1.9
release.yml / release (push) Canceled after 0s
2023-08-11 22:22:34 +01:00
Joe Au-YeungandOmar López 8356cb7d34 Try to list calendars, if not continue (#10720)
Co-authored-by: Omar López <zomars@me.com>
2023-08-11 22:19:26 +01:00
Alex van Andel ceb18167cf fix: Broken team events if a user with the same name exists (#10724)
* fix: Broken team events if a user with the same name exists

* Fix tests + fix usernameList optionality
2023-08-11 22:06:24 +01:00
alannncandAlex van Andel 4d5697f5a5 fix: and enable dynamic booking test (#10642)
* fix and enable dynamic booking test

* remove page pause

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-08-11 13:33:24 -07:00
Shivam Kalra afe180a0ec feat: mailhog fixture (#10606)
* feat: mailhog fixture

* fix: nodemailer to dispatch emails with e2e env

* fix: remove space from email subject

* feat: fixture getFirstEventAsOwner

* feat: assert email subjects
2023-08-11 18:56:27 +00:00
Carina WollendorferandCarinaWolli 851a388853 fix: e2e test for rescheduling overlapping time (#10721)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-08-11 11:45:53 -07:00
Crowdin Bot 07e20144df New Crowdin translations by Github Action 2023-08-11 07:05:51 +00:00
Crowdin Bot 1535e48ce5 New Crowdin translations by Github Action 2023-08-11 07:02:52 +00:00
Suyash SrivastavaandHariom Balhara 0dce391f46 feat: element call app added (#10585)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-08-11 06:59:20 +00:00
cfe7ea45ec test: Create unit tests for react components in packages/ui/components/form/step (#10442)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-08-11 12:06:55 +05:30
Crowdin Bot c244f0fc8e New Crowdin translations by Github Action 2023-08-11 06:36:30 +00:00
Zain GulbazandCarina Wollendorfer 5301af2e9f fix: If the input type "Name" is selected, the label can't be changed from our default label "Your Name" (#10618)
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-08-11 12:03:38 +05:30
Crowdin Bot a4e7ca91b5 New Crowdin translations by Github Action 2023-08-10 21:04:24 +00:00
Leo GiovanettiandOmar López 64e6e1a19a fix: multiple duration getSchedule calls [CAL-2336] (#10709)
Co-authored-by: Omar López <zomars@me.com>
2023-08-10 21:00:46 +00:00
Carina WollendorferandCarinaWolli a6daf17909 fix: allow new booking to overlap old one when rescheduling (#10424)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-08-10 19:07:57 +00:00
Carina WollendorferandCarinaWolli c7dfa7bc89 fix: add extra security layers for sending messages to attendees (#10636)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-08-10 11:52:36 -07:00
Alex van Andel 633d85e81b v3.1.8
release.yml / release (push) Canceled after 0s
2023-08-10 19:05:29 +01:00
Leo Giovanetti 49a61207d5 fix: profile banner delay for orgs + small DX chore (#10546)
* Fixing profile banner delay for orgs

* Separating into other useEffect
2023-08-10 17:50:43 +00:00
alannnc f3eb490e3b Remove bookingIds map and conditional using in: [ ] (#10655) 2023-08-10 17:36:29 +00:00
Joe Au-Yeung 2de5f57437 perf: Reduce Outlook Calendar & Availability Response Size (#10570)
* Getting availability reduce the response size

* Add list calendar filter param

* Clean up
2023-08-10 18:28:56 +01:00
Udit Takkar 8bdd835a68 fix: event type filter by team (#10632)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-08-10 18:27:02 +01:00
aku019andasishupadhyay24@gmail.com 9c7d0ea604 docs: fixed grammar in docs (#10669)
* fixed grammar and typos

* fixed grammar and typos

* docs:fixed grammar and typos

---------

Co-authored-by: asishupadhyay24@gmail.com <India@wc2019>
2023-08-10 18:23:25 +01:00
Rohit Saini a3aa53fe6e fix: #10614 - made nodemailer compatible with mailhog smtp server. (#10615)
* mailhog server auth problem solved

* put condition on transport auth

* fixe type-check error
2023-08-10 18:19:21 +01:00
57384eb921 perf: Replace un-needed context fetching (#10657)
* Replace ctx.user.credentials to fetch in their own usecases

* Remove rawavatar from return

* Remove avatar rawAvatar from handler

* Fix fallback avatars

* fix: profile.slug already includes /team

* perf: Deprecate useAvatarQuery hook

* Extract to reusable credential func

* Fix type errors for credentials

* credentialOwner was inferred incorrectly, string non-nullable

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-08-10 18:07:09 +01:00
Leo Giovanetti a153f9627d chore: Relying on CALCOM_ENV (#10098)
* Relying on CALCOM_ENV

* Restoring unneeded changes

* Relyon more on IS_PRODUCTION

* Relyon more on IS_PRODUCTION
2023-08-10 12:32:20 -04:00
GitStart-Cal.comandgitstart-calcom a54539d211 test: Create unit tests for react components in packages/ui/components/form/datepicker (#10671)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-08-10 12:31:52 -04:00
Udit TakkarandCarina Wollendorfer 8d736cfb67 chore: add helper text in round robin (#10674)
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-08-10 16:30:49 +00:00
Leo Giovanetti 3c3564f884 fix: copy or view profile URL for org (#10702) 2023-08-10 17:30:33 +01:00
Alex van Andel cc75899b01 fix: count without where clause is total users regardless of selection (#10703) 2023-08-10 17:29:47 +01:00
Udit TakkarandCarinaWolli 1a818ab0c3 fix: reschedule timezone (#10697)
* fix: reschedule timezone

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: use original rescheduled booking

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* only when organizer reschedules

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-08-10 17:05:35 +01:00
Joe Au-YeungandLeo Giovanetti 7dafaf1381 Fix redirect URLs (#10680)
Co-authored-by: Leo Giovanetti <hello@leog.me>
2023-08-10 12:06:31 -03:00
Crowdin Bot 74f354abd2 New Crowdin translations by Github Action 2023-08-10 14:53:43 +00:00
Anik Dhabal Babu f281131bf6 fix: Improve onboarding Availability (#10685)
* improve onboarding

* Update Schedule.tsx
2023-08-10 14:49:41 +00:00
22b4bcdeeb fix: showing default apps by Cal like Cal-Video in installed apps (#10611)
Co-authored-by: Satya Nishanth <satya@Satyas-MacBook-Pro.local>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2023-08-10 16:05:26 +05:30
sean-brydon 5f59ef2acf Transform + usernames to - on token initiation (#10692) 2023-08-10 11:50:43 +02:00
sean-brydon d6d4e246f9 chore: Fix UI NITs (#10691) 2023-08-10 08:49:17 +00:00
Hariom Balharaandzomars a49c34e733 perf: Avoid unmounting of Shell on navigation and thus reduce number of paints (#10646)
Co-authored-by: zomars <zomars@me.com>
2023-08-09 15:54:51 -07:00
37ce8860b5 fix: all booking and duration limits (#10480)
Co-authored-by: rkreddy99 <rreddy@e2clouds.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2023-08-09 15:51:38 -07:00
Leo GiovanettiandOmar López 4dfbc46247 Fixing fetching team with parent (#10677)
Co-authored-by: Omar López <zomars@me.com>
2023-08-09 21:05:28 +00:00
Carina WollendorferandCarinaWolli 4c38ea3329 fix: DST issue in the month of DST change (#10664)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-08-09 13:31:48 -07:00
John Victor 24e60498f4 fix: add check mark to selected option in destionation calendar selector (#10583) 2023-08-09 20:23:47 +00:00
Udit Takkar fa9a8cca89 fix: add support email (#10605) 2023-08-09 15:57:22 -04:00
Crowdin Bot 38bb01a9f0 New Crowdin translations by Github Action 2023-08-09 19:49:47 +00:00
Omar López 3cf6fef159 chore: disable locale detection (#10676) 2023-08-09 12:46:02 -07:00
MehulandCarina Wollendorfer 26dac453e9 fix: Offset start times event setting resets after save (#10612)
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-08-09 15:13:25 -04:00
Crowdin Bot 52db023f76 New Crowdin translations by Github Action 2023-08-09 18:36:34 +00:00
df9e02d138 fix: phone selector dropdown hidden behind logo on booking page (#10617)
Co-authored-by: mohammed gehad <mohammed.gehad.1998@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-08-09 18:32:28 +00:00
Crowdin Bot d7be40f67b New Crowdin translations by Github Action 2023-08-09 16:59:49 +00:00
Joe Au-YeungandLeo Giovanetti 68d2a97e92 fix: Orgs publishing with billing (#10662)
Co-authored-by: Leo Giovanetti <hello@leog.me>
2023-08-09 12:56:53 -04:00
cde9b24a9b fix: Cleanup after removing connected calendar (#10012)
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2023-08-09 10:22:51 -04:00
Udit TakkarandSean Brydon cf89598279 fix: Calendar View (#10514)
Co-authored-by: Sean Brydon <sean@cal.com>
2023-08-09 12:19:01 +02:00
alannncandAlex van Andel fcd892bfa0 perf: no wait for session when calling getschedule 10552 cal 2311 (#10607)
* No batching on getting session

* Fix usePublicPage hook to use new router search params

* Move things so getSchedule data can be load as soon as we are rendering BookerComponent

* pre fetch session

* Removed custom code in favour of useTimePreferences

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-08-09 09:55:27 +01:00
Joe Au-Yeung bee5011ec1 fix/ Remove getUserAdminTeams endpoint (#10648) 2023-08-08 21:10:57 -04:00
Omar López e907ac8a9a v3.1.7
release.yml / release (push) Canceled after 0s
2023-08-08 16:12:09 -07:00
c22dfdd54e fix: date and month format issue on booking page for weekly view (#10512)
Co-authored-by: Raghul D <v-raghuld@microsoft.com>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-08-08 23:37:52 +02:00
Nadeem AshrafandUdit Takkar 2aa0188418 fix: #10553 - "Apps" child items not highlighted when selected (#10565)
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-08-08 23:33:58 +02:00
MontoandlemmyMwaura 59ea4d624b feat: add loading state for buttons on settings page (#10643)
* fix: add spinner to general settings section

* feat: add and manage spinner in the emptyScreen component

* feat: add spinner to two-factor auth modal buttons

* feat: add spinner to password and impersonation setting buttons

* feat: add spinner to webhookform(edit and create)

* chore: clean up code

---------

Co-authored-by: lemmyMwaura <lemmymwauracodes@gmail.com>
2023-08-08 19:32:22 +00:00
975420181d fix: smtp send email fix for self hosted instance (#10593)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-08-08 19:07:09 +00:00
Raju kadelandCarinaWolli 4d9a7c1b69 feat: Added loading animation in button while performing update mutation (#10622)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-08-08 19:06:09 +00:00
382cbe0a86 fix: rescheduling dynamic booking (#10597)
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-08-08 18:51:54 +00:00
nicktrnandJoe Au-Yeung 0d1ab6ade9 fix: don't send request reminder for unpaid bookings (#10325)
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2023-08-08 11:45:16 -04:00
Shivam Kalra 428e330301 feat: container init in app-store and embeds workflow (#10637) 2023-08-08 13:45:51 +00:00
Hosmel Quintana f5350b3a2b Fix overflow (#10641) 2023-08-08 13:51:43 +01:00
Hariom Balhara 4cd8935375 fix: SWR cache header on Vercel (#10624) 2023-08-08 13:50:30 +01:00
05020dfa9d test: Create unit tests for react components in packages/ui/components/toast (#10578)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-08-08 09:47:30 +01:00
Alex van AndelandCarina Wollendorfer e5f15cb1df perf: Improved buildDateRanges performance + cleanup (#10610)
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-08-07 19:03:01 -07:00
Omar López 6b536030fa fix: API build (#10639) 2023-08-08 01:01:23 +00:00
zomars 46de1a8518 fixme: disable workflow test to unclog pipeline 2023-08-07 17:50:34 -07:00
Omar LópezandPeer Richelsen 4b78e91384 chore: upgrades to prisma v5 (#10073)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-08-07 22:08:13 +00:00
Crowdin Bot b9cec94880 New Crowdin translations by Github Action 2023-08-07 21:22:02 +00:00
sean-brydon 7b375ce814 perf: Correct use of next-auth session (#10638) 2023-08-07 14:19:14 -07:00
Zahid DodiyaandPeer Richelsen 29d4348e7c fix: #10587 - added a right margin to VTI. (#10613)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-08-07 15:16:32 +00:00
Udit TakkarandPeer Richelsen 826a8856e6 fix: installation dropdown (#10555)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-08-07 15:14:45 +00:00
Alex van AndelandKeith Williams 2f23b5de9b perf: Remove organisation as it's unused in getBusyTimes (?) (#10619)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-08-07 10:06:48 -04:00
df674532a4 fix: added bottom padding to Dialog component (#10629)
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-08-07 14:00:31 +00:00
Nischal Gautam 4d9047974e chore: Renamed getSchedule to getAvailableSlots (#10626)
* refactor: renamed getScheldude to getAvailableSlots

* fix ts error

* romoved comment
2023-08-07 15:00:01 +01:00
Leo Giovanetti ffa3a454bb fix: team unaccepted members (#10582) 2023-08-07 08:32:35 -03:00
Udit TakkarandHariom Balhara 0d62311b41 fix: auth redirect (#10556)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-08-07 16:51:02 +05:30
Moon Patel d36002d24c typo fix in app/storybook/intro.stories.mdx (#10623) 2023-08-07 15:43:21 +05:30
Crowdin Bot 7b837d48f1 New Crowdin translations by Github Action 2023-08-05 14:15:39 +00:00
sean-brydonandPeer Richelsen 8dc9f1c929 fix: Fix auto scroll (#10591)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-08-05 19:42:45 +05:30
Crowdin Bot b006f7f1df New Crowdin translations by Github Action 2023-08-05 06:47:19 +00:00
Alex van Andel 9a08fbb22d perf: Use Intl.DateTimeFormat instead of Dayjs.tz().format() (#10604) 2023-08-05 07:44:47 +01:00
359779cabb fix: {ATTENDEE} workflow variable always uses first attendee for for seated event (#10468)
Co-authored-by: rkreddy99 <rreddy@e2clouds.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-08-04 13:53:58 +00:00
d22d8095e1 fix: cancellation email for seated event for 2nd attendee onwards (#10177)
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-08-04 09:47:16 -04:00
123ecf3700 feat: show dialog when changing email after using Google Login (#9611)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2023-08-03 17:26:40 -07:00
Omar López 710bfc0d7e fix: random test failure when booking are on same date (#10581) 2023-08-03 22:33:49 +00:00
Joe Au-Yeungandalannnc c7d8d435f1 fix: Only enforce min seats for orgs (#10572)
* Only check subscription count for orgs

* Undo constants change

---------

Co-authored-by: alannnc <alannnc@gmail.com>
2023-08-03 15:17:57 -04:00
Shivam KalraandKeith Williams 506ef2078f chore: mailhog (#10521)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-08-03 17:46:25 +01:00
Crowdin Bot e2cb1773ff New Crowdin translations by Github Action 2023-08-03 16:28:27 +00:00
Hariom Balhara 89808cb4f6 fix: Remove Email embed from Routing Form[Stacked PR] (#10389) 2023-08-03 09:25:31 -07:00
Hariom Balhara bf45dcf139 fix: More instances of wrong avatar URL fixed (#10519)
* Fix user.avatar

* Fix avtars in admin users listing

* Handle unpublished org
2023-08-03 15:32:38 +00:00
Hariom Balhara 5491821baf fix: Redact prisma errors (#10536) 2023-08-03 16:22:38 +01:00
Monto 669065cebd feat: add attendee first name and last name tokens to workflows (#10509) 2023-08-03 11:10:59 -04:00
Alex van Andel 91af873db9 perf: When a Google access token is expired and can't be refreshed, w… (#10557) 2023-08-03 13:17:38 +01:00
2de4abba65 feat: Modify sort with if/elseif/else randomize when the dates are equal (#10448)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-08-03 09:03:23 +00:00
Hariom Balharaandzomars 1b6d1b3a5a fix: Embed Code Generator - Fixes 404 in preview of embed for orgs (#10417)
Co-authored-by: zomars <zomars@me.com>
2023-08-02 13:03:22 -07:00
Hariom Balharaandzomars a56b19e618 fix: Broken Embed Dialog in Routing Forms due to recent rewrite (#10434)
Co-authored-by: zomars <zomars@me.com>
2023-08-02 19:40:53 +00:00
ea7853789c fix: welcome-new-contributors action (#10425)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-08-02 19:20:30 +00:00
alannncandOmar López 871e17a865 feat: org owner-admin cant see the teams created by other owner-admin 9982 cal 2120 (#10430)
* WIP

* New files for other teams

* Update code domain model location

* Fixes for finding teams in orgs

* VerticalTabs remove other teams appearance and only display if org admin

* Added conditional to consider orgs admin when updating team profile

* team-member-list wip

* Update memberInviteModal with disableCopyLink prop

* cleaning up

* Undo changes to files

* remove logs

* clean up

* undo changes

* undo change

* Undo changes

* Reset files to main

* typo on import

* Update packages/features/ee/organizations/pages/components/OtherTeamListItem.tsx

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

* useSearchParams hook instead of useRouter

* remove unused code and clean up

* Fix type

---------

Co-authored-by: Omar López <zomars@me.com>
2023-08-02 21:09:43 +02:00
Alex van Andel 95dee6dd37 perf: Turns out the tz() function is very slow, 4ms (#10545) 2023-08-02 13:53:19 -04:00
Alex van Andel 23f97bdd3c use dayjs utc mode to improve before/after performance (#10544) 2023-08-02 17:03:04 +00:00
Alex van Andel bf050b498c Revert "Revert "fix: weekly limits (#10404)""
This reverts commit 83678dfea2.
2023-08-02 16:27:30 +01:00
Alex van Andel 83678dfea2 Revert "fix: weekly limits (#10404)"
This reverts commit 1d5b383529.
2023-08-02 16:15:32 +01:00
Leo Giovanetti 02c5aa02df fix: random multiple duration value input [CAL-887] (#10513) 2023-08-02 10:37:59 -04:00
b7851e6e53 refactor: next/router hooks with next/navigation hooks (#9105)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-08-02 11:35:48 +02:00
Udit Takkar 4ca9138e01 fix: react hook can't be used conditionally (#10439) 2023-08-02 12:22:44 +05:30
nicktrn e44a07b67c fix: add booking form prefill e2e test (#10293)
* fix: add booking form prefill e2e test

* fix: booking e2e type issue

* fix: revert user fixture default name

* fix: breakage due to rebase
2023-08-02 07:40:33 +01:00
GitStart-Cal.comandgitstart-calcom 86690a4c32 test: Create unit tests for react components in packages/ui/components/list (#10501)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-08-02 07:13:26 +01:00
GitStart-Cal.comandgitstart-calcom c8beb6fba0 test: Create unit tests for react components in packages/ui/components/scrollable (#10489)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-08-02 07:11:37 +01:00
Peer Richelsen a97e145cdb Update common.json (#10507) 2023-08-02 06:52:04 +01:00
617e665004 feat: Enable Conferencing Apps for Team Events [CAL-1925] (#10383)
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-08-02 09:24:28 +05:30
Alex van Andel a9344115d2 v3.1.6
release.yml / release (push) Canceled after 0s
2023-08-01 17:25:39 +01:00
GitStart-Cal.comandgitstart-calcom 81c11f61dc test: Create unit tests for react components in packages/ui/components/form/input (#10506)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-08-01 17:19:54 +01:00
Danila 7c64b6a6e2 Fix type name clashing for eslint parser (#10491) 2023-08-01 15:55:31 +00:00
17855251ff fix: Stop sending Reminders when a 'Seat' cancels the Event (#10405)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-08-01 10:13:28 -04:00
a0fc045852 fix: Modified event type public api hander (#9508)
* Modified event type public api hander

* Update apps/api/pages/api/event-types/_get.ts

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Leo Giovanetti <hello@leog.me>
2023-08-01 13:13:24 +00:00
Prateek JainandHariom Balhara 1b0e4b0127 refactor: No name typo fix (#10493)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-08-01 13:02:06 +00:00
60a559940c test: Create unit tests for react components in packages/ui/components/form/daterangepicker (#10479)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
2023-08-01 13:02:50 +01:00
Anik Dhabal Babu b893afb8b8 fix: Address translation problem (#10492) 2023-08-01 16:56:26 +05:30
6fa54aba46 perf: Refactor getEnabledApps to filter on credentials passed (#10353)
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-08-01 16:40:52 +05:30
Crowdin Bot 88f542cb0d New Crowdin translations by Github Action 2023-08-01 08:54:53 +00:00
acc4909981 fix: embed hides floating button (#9165)
* fix: embed - hides floating button on click to prevent overlapping over modal

* revert changes on yarn.lock

* z-index for Modal wasnt highest

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-08-01 08:51:45 +00:00
Leo Giovanettiandzomars 4a6dc50909 fix: Unpublished screens (#10453)
* Implementation

* Changes and e2e

* Reverting launch.json

* Reverting org create handler

* Reverting yarn.lock

* DRYness and nitpicks

* Default org domain to undefined

* Applying zomars suggestion

* Suggestions

* Fixing seed and type in suggestion

* Fixing types

---------

Co-authored-by: zomars <zomars@me.com>
2023-07-31 21:27:22 +01:00
Pratik Kumar 4435451e9b Fix private link glitch (#10456) 2023-07-31 20:43:06 +01:00
12abca65b8 fix: Fixed 10080 settings sidebar scrolling issue (#10471)
* fix: #10080 settings sidebar scrolling issue

* fix: type error

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* Fixed type error

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-07-31 18:51:08 +00:00
e93f1e2329 fix: added validation for empty reset password field (#10464)
* added validation for empty reset password field

* removed useState and useEffect

---------

Co-authored-by: varun thummar <Varun>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-07-31 19:13:45 +01:00
5a430df5d9 feat: Verify email of people setting a meeting with you (#10317)
* booker email verification changes

* name type fix

* use totp and code

* prisma schema styling

* refactor: code

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: book event form

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: type error

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: type errors

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: unit tests

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* refactor: move verifycodedialog from ui and to features/bookings

* fix: type error

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: rkreddy99 <rreddy@e2clouds.com>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-07-31 17:51:11 +00:00
fcd8de43d6 fix: temporarily disable sms/whatsapp to attendee workflow action (#10485)
* disable sms and whatsapp to attendee

* fix type error

* fix prisma enum imports

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-07-31 17:45:48 +00:00
Alex van Andel 9498ad4916 Fixed workflowReminder test + refactored reminderSchedule (#10487) 2023-07-31 18:35:48 +01:00
Rama Krishna Reddyandrkreddy99 d21135e076 fix: do not delete last schedule (#10444)
Co-authored-by: rkreddy99 <rreddy@e2clouds.com>
2023-07-31 14:48:50 +01:00
GitStart-Cal.comandgitstart-calcom 65384d7b1f test: Create unit tests for react components in packages/ui/components/layout (#10458)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-07-31 12:07:21 +05:30
Neel PatelandLeo Giovanetti e68ace87e2 fix: changes for showing error message in case of INTERNAL_SERVER_ERROR in event types (#10305)
* fix: changes for showing error message in case of INTERNAL_SERVER_ERROR

* changes for edge case

---------

Co-authored-by: Leo Giovanetti <hello@leog.me>
2023-07-29 10:14:40 -03:00
sean-brydon 8f9f096723 feat: Auto join team if already in org [CAL-2248] (#10450)
* Auto join if in org.

* Only handle accepted org memberships

* Add comment

* TODO: fix prisma mock
2023-07-28 16:43:35 +00:00
Udit TakkarandPeer Richelsen 0f84102f1e perf: reduce number of api calls in event types (#10381)
* perf: reduce number of api calls in event types

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* chore

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* perf: use staleTime and cacheTime

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-07-28 17:27:44 +01:00
Keith Williams e6ae69a20f chore: Remove env file cache (#10297) 2023-07-28 08:24:53 -07:00
sean-brydon 878f074cb6 use cloudflare real IP (#10449) 2023-07-28 15:11:44 +00:00
2c2fd14050 fix: use time format (#8787)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-07-28 16:22:19 +02:00
Crowdin Bot 5a919acd22 New Crowdin translations by Github Action 2023-07-28 14:09:29 +00:00
Udit Takkar 063c7f6244 fix: width and avatar in orgs app install dropdown (#10447) 2023-07-28 16:04:49 +02:00
Shivam Kalra 344b9e9b2b chore: team booking test (#10440)
* feat: add team name in the createTeam fixture

* chore: remove unnecessary variables

* test: e2e booking for Round Robin EventType

* feat: teammates in user fixtures

* chore: DRY

* fix: remove redundant todo

* fix: add teamEvent title and slug in scenario

* test: booking title, RR hostname
2023-07-28 13:51:15 +01:00
Danilaandsean-brydon e8339030c2 Add optimistic update for away status toggling (#10395)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-07-28 12:18:29 +00:00
GitStart-Cal.comandgitstart-calcom 4bc4ff8193 test: Create unit tests for react components in packages/ui/components/table (#10437)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-07-28 12:57:44 +01:00
GitStart-Cal.com 6cff76cc3b test: Create unit tests for react components in packages/ui/components/form/colorpicker (#10426)
## DEMO

Run 
```
yarn test colorpicker
```
![image](https://github.com/calcom/cal.com/assets/121884634/31f52d82-615d-4926-bf44-0c1742a66526)
2023-07-28 09:44:52 +01:00
Crowdin Bot d47e8b48a6 New Crowdin translations by Github Action 2023-07-27 20:35:50 +00:00
Peer Richelsen 96fb3a357e fixed email embed illustraion (#10427) 2023-07-27 20:31:55 +00:00
nicktrnandUdit Takkar 1d5b383529 fix: weekly limits (#10404)
* fix: correctly apply duration limits

* fix: weekly limits for weeks in two months

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-07-27 17:33:24 +01:00
Richard PoelderlandAlex van Andel 766a64c46e chore: add datoCMS secret for handling webhooks (#10421)
* regen yarn.lock?

* chore: enable i18n-unused GH action in `website`

* Generated yarn.lock with submodules

* Added yarn.lock auth

* add datocms webhook secret

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-07-27 16:01:27 +00:00
Anik Dhabal Babu 65600b76f2 fix: Set the limit of Occurrences (#10269) 2023-07-27 11:49:21 -04:00
Cherishandcherish2003 0c37f8a712 fix: Booking Confirmation Dialogbox (#10397)
Co-authored-by: cherish2003 <saicherissh90@gmail.com>
2023-07-27 11:31:15 -04:00
Alex van Andel aa21217cfb chore: Remove extra params from the users prop. (#10411)
* Removes extra params from the users prop.

* context no longer requires explicit type

* Remove user.avatar prop
2023-07-27 16:06:39 +02:00
Alex van Andel 32068a646e Remove IP ban and tweak matcher accordingly (#10418) 2023-07-27 15:02:16 +01:00
Crowdin Bot 697e80966b New Crowdin translations by Github Action 2023-07-27 13:55:50 +00:00
sajanlamsal 35d55b2960 fix: changed Japanese translations on booking page to better match the scenario. (#10416) 2023-07-27 15:52:34 +02:00
Crowdin Bot c1a562af97 New Crowdin translations by Github Action 2023-07-27 13:52:03 +00:00
e81985c80d test: Create unit tests for react components in packages/ui/components/TokenHandle (#10407)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
2023-07-27 15:48:23 +02:00
GitStart-Cal.comandgitstart-calcom a1e6e8ff32 Update package.json (#10045)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-07-27 15:47:48 +02:00
GitStart-Cal.comandgitstart-calcom 54cf49cfd6 test: Create unit tests for react components in packages/ui/components/form/checkbox (#10409)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-07-27 15:46:55 +02:00
Shivam Kalra e4a075b5e2 test: Round Robin Team Booking (#10413) 2023-07-27 17:18:08 +05:30
Peer Richelsen 660d28b286 Update README.md 2023-07-27 13:47:08 +02:00
Crowdin Bot 2b1c600d99 New Crowdin translations by Github Action 2023-07-27 11:21:03 +00:00
Hariom Balhara 48b79b618f fix: middleware logic Routing Forms (#10412)
* Match /apps/routing_forms

* Next.js config.matcher doesnt support spread operator
2023-07-27 12:18:17 +01:00
98a156311d feat: Add full names in the description of dynamic events. (#10251)
* Add full names in the descripiton of dynamic events.

* Initialising dynamic event desc after the check.

* Revert "Initialising dynamic event desc after the check."

This reverts commit ddd89c4e252f439a5875bfc34b20050a29b68de8.

* Revert "Add full names in the descripiton of dynamic events."

This reverts commit ce232d6256b4fe03c5b0665133be6ea524349c5c.

* Added booking title for dynamic events.

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

* Fix typo.

* Fix lint error.

* Fix redundant booking information.

* Fix type script lint.

* Addressed review suggestions.

---------

Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-07-27 08:52:46 +00:00
sean-brydon 8cdddfa1d0 fix: ratelimit - updates (#10347) 2023-07-27 10:41:43 +02:00
Udit TakkarandKeith Williams 4f488be654 fix: email update bug (#10306)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-07-27 10:41:33 +02:00
cdb78b686b fix: spelling errors (#10376)
* fixed spelling errors

* chore: undo cancelled

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-07-27 10:41:03 +02:00
Hariom Balharaandzomars f1acedff77 fix: Make routing-form a preinstalled app (#9836)
Co-authored-by: zomars <zomars@me.com>
2023-07-26 16:44:19 +00:00
Alex van Andel 9c389c307c v3.1.5
release.yml / release (push) Canceled after 0s
2023-07-26 17:37:11 +01:00
Leo Giovanetti dae4627ada chore: Skipping totp code for org dev (#10403) 2023-07-26 18:15:37 +02:00
Hariom BalharaandJoe Au-Yeung 83d98a1ac4 fix: Apps (#10394)
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2023-07-26 17:43:33 +02:00
Leo Giovanetti bd40dcfa4d fix: Missing subteam URL [CAL-2240] (#10399) 2023-07-26 17:41:38 +02:00
nicktrn 9dc4c84cfd fix: make husky install compatible with yarn v3 (#10393) 2023-07-26 13:00:32 +01:00
Udit TakkarandAlex van Andel 69c01b69f6 fix: responsive upload avatar (#10392)
* fix: responsive upload avatar

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* Update ImageUploader.tsx

Move title to the DialogContent title prop instead of the content.

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-07-26 11:52:43 +00:00
sean-brydonandAlex van Andel 424edc7437 fix: app.cal.com signup changes (#10369)
* Adds premium username check

* Refactor

* fix usernames

* Return a response so the request does not stall

* Fix conditional

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-07-26 12:46:25 +01:00
Anik Dhabal BabuandPeer Richelsen 50d2dad62c fix: Toast Onclose is removing specific toast (#9960)
* toast behaviour fixed

* Update showToast.tsx

* Adjust E2E test for toast

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-07-25 22:27:14 +00:00
Crowdin Bot 06dcaaef3b New Crowdin translations by Github Action 2023-07-25 22:03:48 +00:00
sean-brydonandzomars ad3bdbbda7 fix: Feature flag user cache (#10359)
Co-authored-by: zomars <zomars@me.com>
2023-07-25 22:00:21 +00:00
533371da20 add missing translation (#10380)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-07-25 22:58:26 +01:00
mohammed hussamandAlex van Andel 94b4507901 feat: dynamic group links + flexible schedule (#9931)
* redirect user to /[user]/[type] for dynamic booking in /[user]

* feat: dynamic group links

* fix: type errors

* fix: more type errors

* Booker components cannot rely on useRouter

* Rename 15min/30/60 to dynamic

* Fixed a blocking test, unclear what it was waiting for

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-07-25 22:48:09 +01:00
Richard PoelderlandAlex van Andel 829a103f6b chore: enable i18n-unused GH action in website (#10296)
* regen yarn.lock?

* chore: enable i18n-unused GH action in `website`

* Generated yarn.lock with submodules

* Added yarn.lock auth

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-07-25 18:30:18 +01:00
Udit TakkarandLeo Giovanetti db80afce40 fix: show event type setup tab in managed event types [CAL-2225] (#10367)
Co-authored-by: Leo Giovanetti <hello@leog.me>
2023-07-25 19:07:09 +02:00
6b9d2331fc fix: Managed Event Type Webhooks (#10300)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-07-25 13:05:02 -04:00
Crowdin Bot 2011a1bd38 New Crowdin translations by Github Action 2023-07-25 16:58:14 +00:00
Leo Giovanetti 6a16366941 fix: Username check for sign-up with invitation in org context (#10375) 2023-07-25 18:54:49 +02:00
Crowdin Bot 5a9ee2047f New Crowdin translations by Github Action 2023-07-25 11:40:25 +00:00
Hariom Balhara 3a80c82023 chore: make Embed and AppStore Tests required (#10358)
* Now, all tests are required

* Add matrix support
2023-07-25 13:37:29 +02:00
Crowdin Bot 1d94552f90 New Crowdin translations by Github Action 2023-07-25 10:51:22 +00:00
Crowdin Bot 620a485fd0 New Crowdin translations by Github Action 2023-07-25 10:48:44 +00:00
Keith Williams eb93e81baf chore: Updated Docker release workflow to use environment variable (#10345) 2023-07-25 16:15:56 +05:30
Hariom Balhara 47739a7636 Fix booker top margin embed (#10357) 2023-07-25 10:58:47 +01:00
Crowdin Bot 10bd332405 New Crowdin translations by Github Action 2023-07-25 09:31:46 +00:00
Hariom BalharaandLeo Giovanetti 35be4182e9 fix: Routing Form and other org related fixes (#10320)
Co-authored-by: Leo Giovanetti <hello@leog.me>
2023-07-25 14:58:57 +05:30
Crowdin Bot 30095d0428 New Crowdin translations by Github Action 2023-07-25 08:42:40 +00:00
Crowdin Bot 5a9084332f New Crowdin translations by Github Action 2023-07-25 08:39:46 +00:00
Crowdin Bot d897e72ce2 New Crowdin translations by Github Action 2023-07-25 08:37:04 +00:00
Crowdin Bot 582a545e67 New Crowdin translations by Github Action 2023-07-25 08:34:18 +00:00
Crowdin Bot 6340cb9de1 New Crowdin translations by Github Action 2023-07-25 08:31:53 +00:00
Crowdin Bot 89107d82e7 New Crowdin translations by Github Action 2023-07-25 08:29:02 +00:00
Syed Ali Shahbaz 1851a3c1cf update responses.location (#10356) 2023-07-25 10:26:11 +02:00
Crowdin Bot 53cda97c6e New Crowdin translations by Github Action 2023-07-25 06:58:46 +00:00
Crowdin Bot f92f0ce1fa New Crowdin translations by Github Action 2023-07-25 06:55:54 +00:00
Crowdin Bot 018eb59c9d New Crowdin translations by Github Action 2023-07-25 06:52:57 +00:00
Crowdin Bot 03d8ef9c03 New Crowdin translations by Github Action 2023-07-25 06:49:57 +00:00
Crowdin Bot d10071a4a2 New Crowdin translations by Github Action 2023-07-25 06:46:57 +00:00
Crowdin Bot 27aaf3f75a New Crowdin translations by Github Action 2023-07-25 06:43:58 +00:00
Crowdin Bot 6ff4e07d8f New Crowdin translations by Github Action 2023-07-25 06:41:19 +00:00
Crowdin Bot b740423eb8 New Crowdin translations by Github Action 2023-07-25 06:38:46 +00:00
Crowdin Bot c0f21be255 New Crowdin translations by Github Action 2023-07-25 06:35:38 +00:00
Joe Au-YeungandOmar López 0e62c411f8 fix: Conferencing Label When Installing Apps (#10351)
Co-authored-by: Omar López <zomars@me.com>
2023-07-25 12:03:00 +05:30
Crowdin Bot c89b217c92 New Crowdin translations by Github Action 2023-07-24 21:13:57 +00:00
Leo Giovanetti d3bbe8784f Onboarding wizard and verif email (#10346) 2023-07-24 18:11:25 -03:00
Afrie Irham f168ec8df6 fix: [CAL-2040] add spinner when bookerState is loading (#9873) 2023-07-24 13:20:42 -04:00
Alex van Andel 0138fdd971 feat: Don't run middleware on _next, .*avatar.png & favicon.ico (#10344) 2023-07-24 09:55:55 -07:00
Colin GriffinandKeith Williams 365b95e28a chore: Add remote calcom/docker release workflow (#9841)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-07-24 18:09:12 +02:00
sean-brydon 0ea76332db fix: organization list members error (#10340) 2023-07-24 15:01:16 +00:00
Alex van Andel 43e3ef170a fix: webhook fetch calls should be no-follow (#10336) 2023-07-24 07:36:45 -07:00
Crowdin Bot 9202b53e49 New Crowdin translations by Github Action 2023-07-24 14:17:39 +00:00
Meet Patel 41850c7f59 fix: routing-forms protected with LicenseRequired (#9521) 2023-07-24 19:44:28 +05:30
bd4f99a850 fix: Fixed Issue #10291 removed brand color for tooltip (#10327)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Bhargav Tenali <t.bhargav@kayrros.com>
2023-07-24 15:21:18 +02:00
Crowdin Bot 771f0b67db New Crowdin translations by Github Action 2023-07-24 11:42:34 +00:00
Vachmara 0f0b55488f fix: app store cli windows compatibility issue #10324 (#10326)
* fix: app-store-cli windows support issue #10324

* fix: app-store-cli windows support issue #10324 - remove yarn.lock

* fix: app-store-cli windows support issue #10324

* refactor: instead of repeating code, use constant to know if it is windows platform
2023-07-24 13:39:28 +02:00
Hariom Balharaandsean-brydon 4e1e586541 fix: nameless in sidebar for orgs (#10318)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-07-24 10:25:55 +00:00
80a1458538 fix: Update ImageUploader.tsx (#10319)
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-07-24 10:13:58 +00:00
8de75aebf0 feat: Added feature of embeding availability to emails (#10183)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-07-24 10:09:03 +00:00
Aditya Nikam a9cc3649e0 fix: fixed typo email res.statusCode(200) (#10331) 2023-07-24 11:55:51 +02:00
GitStart-Cal.comandgitstart-calcom 2b5577772b fix: Fix UI issues on embed popup footer (#10286)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-07-24 11:47:25 +05:30
Hariom Balhara f081c733b8 test: Add Embed close modal test (#10323)
* Add close modal test

* Update packages/embeds/embed-core/playwright/lib/testUtils.ts
2023-07-23 12:45:05 +00:00
Hariom Balhara 3ed5a5552a fix: Fix embed iframe still visible even after closing the embed. (#10321)
* Reset visibility

* Update packages/embeds/embed-core/src/embed.ts
2023-07-23 10:55:40 +00:00
Hariom Balhara 61f1a3b21e feat: Support adding links to checkbox type question using markdown[Blocked on another PR] (#8943)
* Support adding links in boolean checkbox

* Fix and comment

* remove textarea, it was accidentally pushed.
2023-07-21 23:30:28 +05:30
d353904c7c test: Create unit tests for react components in packages/ui/components/dialog (#10273)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-07-21 18:54:57 +02:00
Crowdin Bot 8e192ac358 New Crowdin translations by Github Action 2023-07-21 16:47:12 +00:00
GitStart-Cal.comandgitstart-calcom 85c3c6dfc0 test: Create unit tests for react components in packages/ui/components/credits (#10044)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-07-21 18:44:12 +02:00
1aa3077706 test: e2e for workflows (#9657)
* create test files

* add first e2e test

* finish first e2e test

* code clean up

* fix event type create button data-testid

* enable sandbox mode for non production environments

* comment out code for testing

* Revert "comment out code for testing"

This reverts commit 5553dacce23cd85c27e41b76f99fc31ce69499b7.

* clean up comments

* prettier

* use NEXT_PUBLIC_IS_E2E

* use todo fn

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
2023-07-21 21:06:39 +05:30
Crowdin Bot 337192697f New Crowdin translations by Github Action 2023-07-21 15:18:41 +00:00
Hariom Balhara 299f74c1fb name can be undefined for existing name type fields (#10298) 2023-07-21 15:14:39 +00:00
d55404b3aa test: Create unit tests for react components in packages/ui/components/errorBoundary (#10275)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-07-21 14:22:17 +02:00
Shivam Kalraandalannnc 1bb5b0f76c test: team booking (#10267)
Co-authored-by: alannnc <alannnc@gmail.com>
2023-07-21 13:58:52 +02:00
Hariom Balhara 2a8eca7885 chore: Update version in main for embeds that are already released. (#9822)
* Release latest embeds

* Release latest embeds

* Ensure that unpublished packages arent imported

* linter is important to catch unexpected imports

* Create new release

* Add a new patch
2023-07-21 16:46:18 +05:30
Alex van Andel 1566a80e40 v3.1.4
release.yml / release (push) Canceled after 0s
2023-07-21 10:02:01 +01:00
Nafees NazikandCarina Wollendorfer 797d3a7f98 fix: set up organization field validation (#10154)
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-07-21 08:48:43 +00:00
c7a276eb1d fix: remove app dialog while removing installed calendar (#10234)
* Fix remove app dialog while removing installed calendar

* fix: dialog in webhook, otp

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: more dialogs

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: margin

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-07-21 08:46:16 +00:00
Crowdin Bot 1709e23135 New Crowdin translations by Github Action 2023-07-21 08:35:03 +00:00
Leo Giovanetti d1778fdce8 fix: 2FA from CAL to GOOGLE (#10288)
* Fixing 2FA from CAL to GOOGLE

* Missing change

* Missing logic for converted providers

* Fixing edge case and applying feedback

* Leftovers
2023-07-21 09:32:03 +01:00
Joe Au-Yeung b49952b8d8 Fix prettier error on App component (#10287) 2023-07-21 09:31:02 +01:00
Alex van Andel 5b280b2188 fix: team event emails; sending & email subject (#10289) 2023-07-20 19:29:08 -04:00
Alex van Andel 3818093652 fix: Add attendees to getLuckyUser algorithm (#10285)
* Add attendees to getLuckyUser algorithm

* Fix unit test for getLuckyUser
2023-07-20 22:51:15 +01:00
Crowdin Bot 56f4310c24 New Crowdin translations by Github Action 2023-07-20 20:31:50 +00:00
Peer Richelsen 7ec7195ae9 fixed empty screen width (#10278) 2023-07-20 20:29:05 +00:00
Joe Au-Yeung 514db3466f fix: Flaky 2FA tests (#10283) 2023-07-20 19:40:35 +00:00
alannncandPeer Richelsen cd88b688f4 fix: including parentId when filter by eventTypeId happens (#10262)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-07-20 12:19:13 -07:00
Omar López 939a32813f Revert "chore: Make embed and app-store tests required" (#10282) 2023-07-20 11:58:07 -07:00
Joe Au-Yeung 66c1e29d91 fix: Default to Daily if Viable (#10279)
* Default to daily if viable

* Add Zod schema parsing
2023-07-20 12:57:10 -04:00
Crowdin Bot 3c57082fa6 New Crowdin translations by Github Action 2023-07-20 12:10:47 +00:00
Hariom Balhara fc4a2bd186 make embed and app-store tests required (#10271) 2023-07-20 14:08:09 +02:00
Hariom Balhara 3f900966b0 Fix flash of non-transparent background (#10265) 2023-07-20 09:17:46 +02:00
Leo GiovanettiandOmar López 8ba4b4d50e feat: Pending members selection (#10168)
* Pending members selection

* Discard changes to .vscode/launch.json

* revert yarn.lock

---------

Co-authored-by: Omar López <zomars@me.com>
2023-07-20 11:23:15 +05:30
GitStart-Cal.comandgitstart-calcom d2104dac5a test: Create unit tests for react components in packages/ui/components/breadcrumb (CALCOM-614) (#10256)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-07-20 07:13:23 +02:00
GitStart-Cal.comandgitstart-calcom 9ad2731358 test: add unit tests for packages/ui/components/badge (CALCOM-613) (#10255)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-07-20 07:11:53 +02:00
GitStart-Cal.comandgitstart-calcom c34fdab5af test: Create unit tests for react components in packages/ui/components/card (CALCOM-617) (#10257)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-07-20 07:06:15 +02:00
Hariom BalharaandCarina Wollendorfer f1c981fdcf feat: firstAndLastName variant for name field (#8671)
* Add split full name variant

* Share propsType across fieldTypes and components

* Simplify Components

* Add assertions for required field indicator

* Fix test as name cant be used as a custom field right now

* Make it disabled during reschedule

* Fix UI issues in dark mode

* Support adding links in boolean checkbox

* Revert "Support adding links in boolean checkbox"

This reverts commit 31252f8a5f445f8289a09445124109a4a9c3d33b.

* Make sure getBookingFields isnt import client side

* PR feedback addressed from Carina

* DRY Code

* Fix Dialog

---------

Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2023-07-20 07:03:50 +02:00
Anik Dhabal Babu 47f65bb19f fix: More Brand color for calendar view (#10252) 2023-07-20 00:32:25 +02:00
GitStart-Cal.comandgitstart-calcom 587d3c1088 fix: duplicate event-type dialog not using the new dialog footer (CALCOM-10258) (#10260)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-07-20 00:15:03 +02:00
sean-brydon 610c86837a feat: orgs - user management v2
figma: https://www.figma.com/file/ZJNAFHCAq6rnuGRgUFxjzQ/Organizations----New-features?type=design&node-id=4304-330348&mode=design&t=nKonHZpucwxnqwQI-4 (Not all features are in this PR)
loom: https://www.loom.com/share/30e8ef20a5b34a708dca160d8c04ae6f

shoutout @shadcn for the base of this table <3 

Member View: 
![CleanShot 2023-07-14 at 14 09 02](https://github.com/calcom/cal.com/assets/55134778/5db6eedb-bece-4955-91f0-9f2b3b042300)

Admin view:
![CleanShot 2023-07-14 at 14 09 07](https://github.com/calcom/cal.com/assets/55134778/708c624d-1990-4075-8b32-d4587d09deff)

How to test 
- Create ORG
- add members
- view settings -> orgs -> members
2023-07-19 12:45:13 -07:00
Crowdin Bot 676452b4f4 New Crowdin translations by Github Action 2023-07-19 18:47:31 +00:00
Carina WollendorferandCarinaWolli 8a432b45a4 fix: issue with date overrides in some timezones (#10254)
* fix date overrides

* add test

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-07-19 19:44:41 +01:00
Crowdin Bot 92ad652666 New Crowdin translations by Github Action 2023-07-19 16:15:23 +00:00
Mingjie Jiang 617ddcaa3b chore: Improve and standardize simplified and traditional Chinese localizations (#9902) 2023-07-19 16:12:09 +00:00
Crowdin Bot ef7f3e7900 New Crowdin translations by Github Action 2023-07-19 16:10:58 +00:00
4e0a549a50 fix: Fix bookingPage UI issues in dark mode (#10203)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-07-19 16:07:07 +00:00
Crowdin Bot 8f95d27469 New Crowdin translations by Github Action 2023-07-19 15:44:41 +00:00
8902f7efa1 fix: /event-type /apps - doesn't match designs (CALCOM-9180) (#9549)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-07-19 17:41:37 +02:00
GitStart-Cal.comandgitstart-calcom bd7eadd721 test: Create unit tests for react components in packages/ui/components/alert (tests-alert-component) (#9897)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-07-19 17:22:41 +02:00
Crowdin Bot 02e34a7bde New Crowdin translations by Github Action 2023-07-19 15:21:40 +00:00
sean-brydonandShivam Kalra d51e1ab47d fix: Fixes error for unknown JSON when creating an account (#10248)
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
2023-07-19 15:18:31 +00:00
Crowdin Bot ebe60c36bd New Crowdin translations by Github Action
release.yml / release (push) Canceled after 0s
2023-07-19 14:33:31 +00:00
Monto 26e458be6d feat: dynamic time format rendering in workflows based on user settings (#10229) 2023-07-19 10:30:37 -04:00
Crowdin Bot 1ad4f7247e New Crowdin translations by Github Action 2023-07-19 12:37:55 +00:00
Crowdin Bot e0237acac9 New Crowdin translations by Github Action 2023-07-19 12:35:34 +00:00
Crowdin Bot 2dc0bb4888 New Crowdin translations by Github Action 2023-07-19 12:33:09 +00:00
sean-brydon dfffd8b577 fix: revert i18n hook changes (#10249) 2023-07-19 12:30:25 +00:00
Crowdin Bot e6f2ba2c25 New Crowdin translations by Github Action 2023-07-19 09:22:27 +00:00
sean-brydonandKeith Williams 628a29e89c chore: fixing some initial re-renders (#10185)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-07-19 09:19:53 +00:00
Crowdin Bot 2fd9ef2733 New Crowdin translations by Github Action 2023-07-19 09:05:08 +00:00
Crowdin Bot c9f2d6bc93 New Crowdin translations by Github Action 2023-07-19 09:01:24 +00:00
Peer Richelsen a83aea30da Update package.json 2023-07-19 10:58:27 +02:00
Hariom BalharaandShivam Kalra b516a2af44 Get OAuth working for local (#10054)
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
2023-07-19 09:15:05 +02:00
Crowdin Bot d0e831975d New Crowdin translations by Github Action 2023-07-19 06:40:31 +00:00
Crowdin Bot b906c7caf7 New Crowdin translations by Github Action 2023-07-19 06:37:40 +00:00
Crowdin Bot 152dbe58c9 New Crowdin translations by Github Action 2023-07-19 06:34:49 +00:00
Crowdin Bot b4c154a6af New Crowdin translations by Github Action 2023-07-19 06:32:11 +00:00
Crowdin Bot 99cea7a326 New Crowdin translations by Github Action 2023-07-19 06:29:40 +00:00
Crowdin Bot d0ae519851 New Crowdin translations by Github Action 2023-07-19 06:27:16 +00:00
Crowdin Bot d185c665b8 New Crowdin translations by Github Action 2023-07-19 06:24:21 +00:00
Crowdin Bot baa9ddf46a New Crowdin translations by Github Action 2023-07-19 06:21:52 +00:00
Alex van Andel e8f5090acc Fix: Flaky 2FA test due to waiting redundant Enter (#10236) 2023-07-19 08:19:16 +02:00
Crowdin Bot 0661b96cd2 New Crowdin translations by Github Action 2023-07-19 05:10:16 +00:00
Crowdin Bot 3a64506c9e New Crowdin translations by Github Action 2023-07-19 05:07:31 +00:00
Crowdin Bot f1b705e022 New Crowdin translations by Github Action 2023-07-19 05:05:00 +00:00
Crowdin Bot c768d52539 New Crowdin translations by Github Action 2023-07-19 05:02:24 +00:00
Crowdin Bot 7d3ba5c55c New Crowdin translations by Github Action 2023-07-19 04:59:34 +00:00
Crowdin Bot e643afaa80 New Crowdin translations by Github Action 2023-07-19 04:57:05 +00:00
Crowdin Bot 4cd3479cfd New Crowdin translations by Github Action 2023-07-19 04:54:39 +00:00
Crowdin Bot 7efddfd213 New Crowdin translations by Github Action 2023-07-19 04:51:59 +00:00
Crowdin Bot a8c362dbf3 New Crowdin translations by Github Action 2023-07-19 04:49:27 +00:00
Crowdin Bot f6b3cc0c7c New Crowdin translations by Github Action 2023-07-19 04:46:55 +00:00
Hariom Balhara 902bb71273 Prefill logged in info (#10240) 2023-07-19 06:44:13 +02:00
db021cc696 fix: update list for countries that don't support alphanumeric sender ids (#10232)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-07-18 21:31:45 +00:00
Jaideep GuntupalliandUdit Takkar a2645c2ed4 feat: replaced slack icon with Discord (#10193)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2023-07-18 21:30:17 +00:00
Anik Dhabal Babu 1ec9a6924c feddback added (#10221) 2023-07-18 21:29:56 +00:00
Leo Giovanetti 09068b39e6 perf: Event-type config page (#10238) 2023-07-18 23:18:08 +02:00
Crowdin Bot 9069a2412d New Crowdin translations by Github Action 2023-07-18 20:30:45 +00:00
zomars 6b5c8935c5 fix: formatting 2023-07-18 13:27:54 -07:00
Crowdin Bot d27ff41682 New Crowdin translations by Github Action 2023-07-18 19:53:12 +00:00
Crowdin Bot c6ca736daa New Crowdin translations by Github Action 2023-07-18 19:50:00 +00:00
Carina WollendorferandCarinaWolli cedb2b72e9 fix: add missing booking paid webhook trigger to dropdwon (#10237)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-07-18 19:46:53 +00:00
Hariom Balhara 11081cb5d8 fix: Embed - Non transparent background when color-scheme of embedding document and cal.com iframe don't match (#10033)
## What does this PR do?

Fixes #10032

Before:
https://www.loom.com/share/1890e4eebf41426aaf66d8b4909d0765
After:
https://www.loom.com/share/8b00c41587f2467ab05ef48e4ed523ef


## Type of change

<!-- Please delete bullets that are not relevant. -->

- Bug fix (non-breaking change which fixes an issue)

## How should this be tested?

- See loom

## Mandatory Tasks

- [x] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.

## Checklist
- I haven't added tests that prove my fix is effective or that my feature works
2023-07-18 09:46:35 -07:00
Hariom Balhara 34d65f67a9 fix: Avoid resetting state one every render. (#10210) 2023-07-18 16:42:40 +00:00
Keith Williams e4c49d0e0b fix: Added step to create env file for bundle analysis (#10224) 2023-07-18 09:20:17 -07:00
Peer Richelsen 43a30aa51d Update package.json
release.yml / release (push) Canceled after 0s
2023-07-18 18:14:14 +02:00
Jay Pitroda eb13057157 fix: booking error on invalid timezone (#10209) 2023-07-18 16:09:31 +00:00
Syed Ali Shahbaz bb4d587359 fix: lint errors introduced in PR#10225 (#10230) 2023-07-18 15:58:32 +00:00
Crowdin Bot 3d7c32c0d8 New Crowdin translations by Github Action 2023-07-18 15:41:03 +00:00
39508b3daa fix: booking page: location icon is not showing up (CALCOM-9951) (#9993)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-07-18 15:37:39 +00:00
Richard PoelderlandKeith Williams e79c818edb chore: Add website deps to yarn.lock (#10219)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-07-18 14:37:16 +00:00
Mehul 8a1023087f fix: inconsistent duration labels (#10223) 2023-07-18 15:10:28 +01:00
Crowdin Bot 9343aee778 New Crowdin translations by Github Action 2023-07-18 13:52:15 +00:00
Syed Ali Shahbaz 89f645c450 fix: Hotfix-bookings list breaks on unsupported timezone (#10225)
* Adds a check for supportedTimezone values

* adds timezone support check for attendees
2023-07-18 13:49:23 +00:00
Crowdin Bot da5d90eb07 New Crowdin translations by Github Action 2023-07-18 11:29:18 +00:00
Crowdin Bot 9f30f95dd9 New Crowdin translations by Github Action 2023-07-18 11:26:41 +00:00
Crowdin Bot bc0c1341f6 New Crowdin translations by Github Action 2023-07-18 11:24:09 +00:00
Crowdin Bot 1c0a51cc02 New Crowdin translations by Github Action 2023-07-18 11:21:42 +00:00
Crowdin Bot 7cfe7647f3 New Crowdin translations by Github Action 2023-07-18 11:17:58 +00:00
Crowdin Bot cc91834ddd New Crowdin translations by Github Action 2023-07-18 11:14:53 +00:00
Crowdin Bot 2cb1a62906 New Crowdin translations by Github Action 2023-07-18 11:12:13 +00:00
Crowdin Bot 6135d449ec New Crowdin translations by Github Action 2023-07-18 11:09:31 +00:00
Crowdin Bot 073b29c214 New Crowdin translations by Github Action 2023-07-18 11:06:19 +00:00
Crowdin Bot ea99dc5520 New Crowdin translations by Github Action 2023-07-18 11:03:30 +00:00
Crowdin Bot c3280a62b7 New Crowdin translations by Github Action 2023-07-18 11:00:47 +00:00
Crowdin Bot a41b42359a New Crowdin translations by Github Action 2023-07-18 10:57:56 +00:00
Crowdin Bot cc15fb5285 New Crowdin translations by Github Action 2023-07-18 10:55:10 +00:00
Crowdin Bot e554268592 New Crowdin translations by Github Action 2023-07-18 10:52:35 +00:00
Crowdin Bot f4c2764cbf New Crowdin translations by Github Action 2023-07-18 10:49:58 +00:00
Crowdin Bot ac54ccdd70 New Crowdin translations by Github Action 2023-07-18 10:47:26 +00:00
Hariom BalharaandShivam Kalra b3216634c9 fix: Add missing auth package to yarn.lock (#10214)
Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com>
2023-07-18 12:44:38 +02:00
Crowdin Bot 6c437edc0e New Crowdin translations by Github Action 2023-07-18 10:11:17 +00:00
Crowdin Bot bed153b497 New Crowdin translations by Github Action 2023-07-18 10:08:12 +00:00
2be245eefc add CAL_URL to fix og image issue (#10172)
Co-authored-by: Raghul D <v-raghuld@microsoft.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-07-18 10:05:28 +00:00
Crowdin Bot caf3b40687 New Crowdin translations by Github Action 2023-07-18 09:33:33 +00:00
Crowdin Bot 5650bd6409 New Crowdin translations by Github Action 2023-07-18 09:30:53 +00:00
Crowdin Bot 94a86acdaa New Crowdin translations by Github Action 2023-07-18 09:28:07 +00:00
Crowdin Bot defd49f986 New Crowdin translations by Github Action 2023-07-18 09:25:37 +00:00
Crowdin Bot 679d3da957 New Crowdin translations by Github Action 2023-07-18 09:23:02 +00:00
Crowdin Bot e84fd39e11 New Crowdin translations by Github Action 2023-07-18 09:20:12 +00:00
Crowdin Bot 1d74e9c201 New Crowdin translations by Github Action 2023-07-18 09:17:11 +00:00
Crowdin Bot 3356cbc1df New Crowdin translations by Github Action 2023-07-18 09:13:52 +00:00
Crowdin Bot 4584f666e0 New Crowdin translations by Github Action 2023-07-18 09:10:50 +00:00
Crowdin Bot c9078cfd94 New Crowdin translations by Github Action 2023-07-18 09:07:49 +00:00
Crowdin Bot 4ae1c2aa8c New Crowdin translations by Github Action 2023-07-18 09:05:11 +00:00
Crowdin Bot cf15f5f2a2 New Crowdin translations by Github Action 2023-07-18 09:01:52 +00:00
Crowdin Bot d21f42c257 New Crowdin translations by Github Action 2023-07-18 08:58:31 +00:00
Crowdin Bot 92a0dff2c7 New Crowdin translations by Github Action 2023-07-18 08:55:56 +00:00
Udit Takkar 47add7e7fc fix: modals regression (#10211)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-07-18 09:53:01 +01:00
Hariom Balhara 6dfc19247e fix: Embed theme not working using Embed API (#10163)
## What does this PR do?

Fixes #10187
See [Tests Done](https://www.loom.com/share/f03e0191b60143d8b45a505042dbfa11)

## Type of change
  - [x] Bug fix (non-breaking change which fixes an issue)

## How should this be tested?
- [x] Configure embed to use `dark` theme and verify that dark theme is shown on event booking page(when user has light theme set). This is failing in main
- Additional Tests for embed to avoid any new regression
	- [x] - Configure "auto" theme using embed API and see it reacts to system theme
	- [x] - Don't configure any theme and see that "light" theme is shown even when we switch system theme(Because User has configured light theme in App)
-  Tests outside embed to avoid any new regression
	- [x] - See that light theme is shown even after switching system theme
	- [x] - Now, switch the user theme to dark and see that it reflects the change. 

## Mandatory Tasks

 [x] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.
2023-07-17 18:02:42 -07:00
2db4998eaa fix: wrong slot starting times for half hour times zones (#10161)
* fix timezone zone issue for first slot

* fix failed test in getSchedule

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

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2023-07-18 00:57:34 +00:00
alannncandLeo Giovanetti dad4fd17fb fix: Improve continue button on org-add-team and validations [CAL-2015] (#10122)
* Improve continue button on team add and validations

* Fix server validations for orgs create team

* Remove unused prop form input

* add comment for context

* Fix duplicatedSlugs not working properly

* Fix continue button disabled state, added more examples for team names

* Fix initial state for inputValues on addTeamForm

* Don't allow whitespaces only inputs on create teams

* Use rhf instead of react useState

---------

Co-authored-by: Leo Giovanetti <hello@leog.me>
2023-07-17 17:17:29 -07:00
c26efcabe4 fix: customTemplate() function fails when no locale is given (#10201)
* fallback to en for locale

* remove console.log

* Fix nit changes

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2023-07-17 16:38:37 -07:00
1001 changed files with 34762 additions and 12657 deletions
+9
View File
@@ -1,6 +1,7 @@
# ********** INDEX **********
#
# - APP STORE
# - BASECAMP
# - DAILY.CO VIDEO
# - GOOGLE CALENDAR/MEET/LOGIN
# - HUBSPOT
@@ -20,6 +21,14 @@
# - APP STORE **********************************************************************************************
# ⚠️ ⚠️ ⚠️ THESE WILL BE MIGRATED TO THE DATABASE TO PREVENT AWS's 4KB ENV QUOTA ⚠️ ⚠️ ⚠️
# - BASECAMP
# Used to enable Basecamp integration with Cal.com
# @see https://github.com/calcom/cal.com#obtaining-basecamp-client-id-and-secret
BASECAMP3_CLIENT_ID=
BASECAMP3_CLIENT_SECRET=
BASECAMP3_USER_AGENT=
# - DAILY.CO VIDEO
# Enables Cal Video. to get your key
# 1. Visit our [Daily.co Partnership Form](https://go.cal.com/daily) and enter your information
+12 -2
View File
@@ -126,8 +126,7 @@ TWILIO_WHATSAPP_PHONE_NUMBER=
NEXT_PUBLIC_SENDER_ID=
TWILIO_VERIFY_SID=
# This is used so we can bypass emails in auth flows for E2E testing
# Set it to "1" if you need to run E2E tests locally
# Set it to "1" if you need to run E2E tests locally.
NEXT_PUBLIC_IS_E2E=
# Used for internal billing system
@@ -172,6 +171,12 @@ EMAIL_SERVER_PORT=1025
## You will need to provision an App Password.
## @see https://support.google.com/accounts/answer/185833
# EMAIL_SERVER_PASSWORD='<gmail_app_password>'
# Used for E2E for email testing
# Set it to "1" if you need to email checks in E2E tests locally
# Make sure to run mailhog container manually or with `yarn dx`
E2E_TEST_MAILHOG_ENABLED=
# **********************************************************************************************************
# Set the following value to true if you wish to enable Team Impersonation
@@ -215,3 +220,8 @@ PROJECT_ID_VERCEL=
TEAM_ID_VERCEL=
# Get it from: https://vercel.com/account/tokens
AUTH_BEARER_TOKEN_VERCEL=
# - APPLE CALENDAR
# Used for E2E tests on Apple Calendar
E2E_TEST_APPLE_CALENDAR_EMAIL=""
E2E_TEST_APPLE_CALENDAR_PASSWORD=""
+6 -6
View File
@@ -22,18 +22,18 @@ Any other relevant information. For example, why do you consider this a bug and
### Actual Results
- What's happening right now that is different from what is expected
- What's happening right now that is different from what is expected
### Expected Results
- This is an ideal result that the system should get after the tests are performed
- This is an ideal result that the system should get after the tests are performed
### Technical details
- Browser version, screen recording, console logs, network requests: You can make a recording with [Bird Eats Bug](https://birdeatsbug.com/).
- Node.js version
- Anything else that you think could be an issue.
- Browser version, screen recording, console logs, network requests: You can make a recording with [Bird Eats Bug](https://birdeatsbug.com/).
- Node.js version
- Anything else that you think could be an issue.
### Evidence
- How was this tested? This is quite mandatory in terms of bugs. Providing evidence of your testing with screenshots or/and videos is an amazing way to prove the bug and a troubleshooting chance to find the solution.
- How was this tested? This is quite mandatory in terms of bugs. Providing evidence of your testing with screenshots or/and videos is an amazing way to prove the bug and a troubleshooting chance to find the solution.
+13 -12
View File
@@ -11,31 +11,32 @@ Fixes # (issue)
## Requirement/Documentation
<!-- Please provide all documents that are important to understand the reason of that PR. -->
- If there is a requirement document, please, share it here.
- If there is ab UI/UX design document, please, share it here.
- If there is a requirement document, please, share it here.
- If there is ab UI/UX design document, please, share it here.
## Type of change
<!-- Please delete bullets that are not relevant. -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] Chore (refactoring code, technical debt, workflow improvements)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] Chore (refactoring code, technical debt, workflow improvements)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
## How should this be tested?
<!-- Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. Write details that help to start the tests -->
- Are there environment variables that should be set?
- What are the minimal test data to have?
- What is expected (happy path) to have (input and output)?
- Any other important info that could help to test that PR
- Are there environment variables that should be set?
- What are the minimal test data to have?
- What is expected (happy path) to have (input and output)?
- Any other important info that could help to test that PR
## Mandatory Tasks
- [ ] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.
- [ ] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.
## Checklist
-17
View File
@@ -1,17 +0,0 @@
name: "Set environment variables"
description: "Configures environment variables for a workflow"
runs:
using: "composite"
steps:
- name: Read env file
uses: buildjet/cache@v3
id: env-cache
with:
path: gh.env-${{ github.sha }}
key: env-cache
restore-keys: env-cache-
- name: Set Environment Variables
uses: tw3lveparsecs/github-actions-setvars@latest
with:
envFilePath: gh.env-${{ github.sha }}
@@ -5,16 +5,15 @@ on:
types:
- opened
jobs:
label_on_pr:
runs-on: ubuntu-latest
permissions:
contents: none
issues: read
pull-requests: write
steps:
- name: Apply labels from linked issue to PR
uses: actions/github-script@v5
@@ -1,7 +1,7 @@
# .github/workflows/chromatic.yml
# Workflow name
name: 'Chromatic'
name: "Chromatic"
# Event for the workflow
on:
+3 -3
View File
@@ -4,8 +4,8 @@ on:
# "Scheduled workflows run on the latest commit on the default or base branch."
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
schedule:
# Runs “At minute 0, 15, 30, and 45.” (see https://crontab.guru)
- cron: "0,15,30,45 * * * *"
# Runs "At every 15th minute." (see https://crontab.guru)
- cron: "*/15 * * * *"
jobs:
cron-bookingReminder:
env:
@@ -20,4 +20,4 @@ jobs:
-X POST \
-H 'content-type: application/json' \
-H 'authorization: ${{ secrets.CRON_API_KEY }}' \
--fail
-sSf
+2 -2
View File
@@ -5,7 +5,7 @@ on:
# "Scheduled workflows run on the latest commit on the default or base branch."
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
schedule:
# Runs “Every month at 1st (see https://crontab.guru)
# Runs "At 00:00 on day-of-month 1." (see https://crontab.guru)
- cron: "0 0 1 * *"
jobs:
cron-downgradeUsers:
@@ -21,4 +21,4 @@ jobs:
-X POST \
-H 'content-type: application/json' \
-H 'authorization: ${{ secrets.CRON_API_KEY }}' \
--fail
-sSf
@@ -4,8 +4,8 @@ on:
# "Scheduled workflows run on the latest commit on the default or base branch."
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
schedule:
# Runs “At minute 0, 15, 30, and 45.” (see https://crontab.guru)
- cron: "0,15,30,45 * * * *"
# Runs "At every 15th minute." (see https://crontab.guru)
- cron: "*/15 * * * *"
jobs:
cron-scheduleEmailReminders:
env:
@@ -20,4 +20,4 @@ jobs:
-X POST \
-H 'content-type: application/json' \
-H 'authorization: ${{ secrets.CRON_API_KEY }}' \
--fail
-sSf
@@ -4,8 +4,8 @@ on:
# "Scheduled workflows run on the latest commit on the default or base branch."
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
schedule:
# Runs “At minute 0, 15, 30, and 45.” (see https://crontab.guru)
- cron: "0,15,30,45 * * * *"
# Runs "At every 15th minute." (see https://crontab.guru)
- cron: "*/15 * * * *"
jobs:
cron-scheduleSMSReminders:
env:
@@ -20,4 +20,4 @@ jobs:
-X POST \
-H 'content-type: application/json' \
-H 'authorization: ${{ secrets.CRON_API_KEY }}' \
--fail
-sSf
@@ -4,8 +4,8 @@ on:
# "Scheduled workflows run on the latest commit on the default or base branch."
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
schedule:
# Runs “At minute 0, 15, 30, and 45.” (see https://crontab.guru)
- cron: "0,15,30,45 * * * *"
# Runs "At every 15th minute." (see https://crontab.guru)
- cron: "*/15 * * * *"
jobs:
cron-scheduleWhatsappReminders:
env:
@@ -20,4 +20,4 @@ jobs:
-X POST \
-H 'content-type: application/json' \
-H 'authorization: ${{ secrets.CRON_API_KEY }}' \
--fail
-sSf
+1 -1
View File
@@ -8,7 +8,7 @@ on:
# "Scheduled workflows run on the latest commit on the default or base branch."
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
schedule:
# Runs every day (see https://crontab.guru)
# Runs "At 00:00." every day (see https://crontab.guru)
- cron: "0 0 * * *"
workflow_dispatch:
jobs:
+2 -2
View File
@@ -5,7 +5,7 @@ on:
# "Scheduled workflows run on the latest commit on the default or base branch."
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
schedule:
# Runs “Every month at 1st (see https://crontab.guru)
# Runs "At 00:00 on day-of-month 1." (see https://crontab.guru)
- cron: "0 0 1 * *"
jobs:
cron-syncAppMeta:
@@ -21,4 +21,4 @@ jobs:
-X POST \
-H 'content-type: application/json' \
-H 'authorization: ${{ secrets.CRON_API_KEY }}' \
--fail
-sSf
+38 -4
View File
@@ -5,7 +5,7 @@ on:
jobs:
e2e-app-store:
timeout-minutes: 20
name: E2E App-Store Apps
name: E2E App Store (${{ matrix.shard }}/${{ strategy.job-total }})
runs-on: buildjet-4vcpu-ubuntu-2204
services:
postgres:
@@ -15,6 +15,16 @@ jobs:
POSTGRES_DB: calendso
ports:
- 5432:5432
mailhog:
image: mailhog/mailhog:v1.0.1
ports:
- 8025:8025
- 1025:1025
strategy:
fail-fast: false
matrix:
## There aren't many tests for AppStore. So, just start with 2 shards. Increase if needed.
shard: [1, 2]
steps:
- uses: actions/checkout@v3
@@ -22,16 +32,40 @@ jobs:
- run: echo 'NODE_OPTIONS="--max_old_space_size=4096"' >> $GITHUB_ENV
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/yarn-playwright-install
- uses: ./.github/actions/env-read-file
- uses: ./.github/actions/cache-db
- uses: ./.github/actions/cache-build
- name: Run Tests
run: yarn test-e2e:app-store
run: yarn e2e:app-store --shard=${{ matrix.shard }}/${{ strategy.job-total }}
env:
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
E2E_TEST_MAILHOG_ENABLED: ${{ vars.E2E_TEST_MAILHOG_ENABLED }}
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
GOOGLE_LOGIN_ENABLED: ${{ vars.CI_GOOGLE_LOGIN_ENABLED }}
NEXTAUTH_SECRET: ${{ secrets.CI_NEXTAUTH_SECRET }}
NEXTAUTH_URL: ${{ secrets.CI_NEXTAUTH_URL }}
NEXT_PUBLIC_IS_E2E: ${{ vars.CI_NEXT_PUBLIC_IS_E2E }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
NEXT_PUBLIC_WEBAPP_URL: ${{ vars.CI_NEXT_PUBLIC_WEBAPP_URL }}
NEXT_PUBLIC_WEBSITE_URL: ${{ vars.CI_NEXT_PUBLIC_WEBSITE_URL }}
PAYMENT_FEE_FIXED: ${{ vars.CI_PAYMENT_FEE_FIXED }}
PAYMENT_FEE_PERCENTAGE: ${{ vars.CI_PAYMENT_FEE_PERCENTAGE }}
SAML_ADMINS: ${{ secrets.CI_SAML_ADMINS }}
SAML_DATABASE_URL: ${{ secrets.CI_SAML_DATABASE_URL }}
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
SENDGRID_API_KEY: ${{ secrets.CI_SENDGRID_API_KEY }}
SENDGRID_EMAIL: ${{ secrets.CI_SENDGRID_EMAIL }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
- name: Upload Test Results
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name: test-results
name: app-store-results-${{ matrix.shard }}_${{ strategy.job-total }}
path: test-results
+33 -4
View File
@@ -1,10 +1,11 @@
name: E2E Embed tests and booking flow(for non-embed as well)
name: E2E Embed React tests and booking flow(for non-embed as well)
on:
workflow_call:
jobs:
e2e-embed:
timeout-minutes: 20
name: E2E Embed React (${{ matrix.shard }}/${{ strategy.job-total }})
runs-on: buildjet-4vcpu-ubuntu-2204
services:
postgres:
@@ -14,6 +15,11 @@ jobs:
POSTGRES_DB: calendso
ports:
- 5432:5432
strategy:
fail-fast: false
matrix:
## There aren't many tests for embed-react. So, just start with 2 shards. Increase if needed.
shard: [1, 2]
steps:
- uses: actions/checkout@v3
@@ -21,18 +27,41 @@ jobs:
- run: echo 'NODE_OPTIONS="--max_old_space_size=4096"' >> $GITHUB_ENV
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/yarn-playwright-install
- uses: ./.github/actions/env-read-file
- uses: ./.github/actions/cache-db
- uses: ./.github/actions/cache-build
- name: Run Tests
run: |
yarn test-e2e:embed-react
yarn e2e:embed-react --shard=${{ matrix.shard }}/${{ strategy.job-total }}
yarn workspace @calcom/embed-react packaged:tests
env:
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
GOOGLE_LOGIN_ENABLED: ${{ vars.CI_GOOGLE_LOGIN_ENABLED }}
NEXTAUTH_SECRET: ${{ secrets.CI_NEXTAUTH_SECRET }}
NEXTAUTH_URL: ${{ secrets.CI_NEXTAUTH_URL }}
NEXT_PUBLIC_IS_E2E: ${{ vars.CI_NEXT_PUBLIC_IS_E2E }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
NEXT_PUBLIC_WEBAPP_URL: ${{ vars.CI_NEXT_PUBLIC_WEBAPP_URL }}
NEXT_PUBLIC_WEBSITE_URL: ${{ vars.CI_NEXT_PUBLIC_WEBSITE_URL }}
PAYMENT_FEE_FIXED: ${{ vars.CI_PAYMENT_FEE_FIXED }}
PAYMENT_FEE_PERCENTAGE: ${{ vars.CI_PAYMENT_FEE_PERCENTAGE }}
SAML_ADMINS: ${{ secrets.CI_SAML_ADMINS }}
SAML_DATABASE_URL: ${{ secrets.CI_SAML_DATABASE_URL }}
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
SENDGRID_API_KEY: ${{ secrets.CI_SENDGRID_API_KEY }}
SENDGRID_EMAIL: ${{ secrets.CI_SENDGRID_EMAIL }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
- name: Upload Test Results
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name: test-results
name: embed-react-results-${{ matrix.shard }}_${{ strategy.job-total }}
path: test-results
+39 -4
View File
@@ -1,10 +1,11 @@
name: E2E Embed tests and booking flow(for non-embed as well)
name: E2E Embed Core tests and booking flow(for non-embed as well)
on:
workflow_call:
jobs:
e2e-embed:
timeout-minutes: 20
name: E2E Embed Core (${{ matrix.shard }}/${{ strategy.job-total }})
runs-on: buildjet-4vcpu-ubuntu-2204
services:
postgres:
@@ -14,6 +15,16 @@ jobs:
POSTGRES_DB: calendso
ports:
- 5432:5432
mailhog:
image: mailhog/mailhog:v1.0.1
ports:
- 8025:8025
- 1025:1025
strategy:
fail-fast: false
matrix:
## There aren't many tests for embed-core. So, just start with 2 shards. Increase if needed.
shard: [1, 2]
steps:
- uses: actions/checkout@v3
@@ -21,16 +32,40 @@ jobs:
- run: echo 'NODE_OPTIONS="--max_old_space_size=4096"' >> $GITHUB_ENV
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/yarn-playwright-install
- uses: ./.github/actions/env-read-file
- uses: ./.github/actions/cache-db
- uses: ./.github/actions/cache-build
- name: Run Tests
run: yarn test-e2e:embed
run: yarn e2e:embed --shard=${{ matrix.shard }}/${{ strategy.job-total }}
env:
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
E2E_TEST_MAILHOG_ENABLED: ${{ vars.E2E_TEST_MAILHOG_ENABLED }}
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
GOOGLE_LOGIN_ENABLED: ${{ vars.CI_GOOGLE_LOGIN_ENABLED }}
NEXTAUTH_SECRET: ${{ secrets.CI_NEXTAUTH_SECRET }}
NEXTAUTH_URL: ${{ secrets.CI_NEXTAUTH_URL }}
NEXT_PUBLIC_IS_E2E: ${{ vars.CI_NEXT_PUBLIC_IS_E2E }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
NEXT_PUBLIC_WEBAPP_URL: ${{ vars.CI_NEXT_PUBLIC_WEBAPP_URL }}
NEXT_PUBLIC_WEBSITE_URL: ${{ vars.CI_NEXT_PUBLIC_WEBSITE_URL }}
PAYMENT_FEE_FIXED: ${{ vars.CI_PAYMENT_FEE_FIXED }}
PAYMENT_FEE_PERCENTAGE: ${{ vars.CI_PAYMENT_FEE_PERCENTAGE }}
SAML_ADMINS: ${{ secrets.CI_SAML_ADMINS }}
SAML_DATABASE_URL: ${{ secrets.CI_SAML_DATABASE_URL }}
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
SENDGRID_API_KEY: ${{ secrets.CI_SENDGRID_API_KEY }}
SENDGRID_EMAIL: ${{ secrets.CI_SENDGRID_EMAIL }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
- name: Upload Test Results
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name: test-results
name: embed-core-results-${{ matrix.shard }}_${{ strategy.job-total }}
path: test-results
+36 -1
View File
@@ -1,6 +1,8 @@
name: E2E test
on:
workflow_call:
jobs:
e2e:
timeout-minutes: 20
@@ -14,6 +16,11 @@ jobs:
POSTGRES_DB: calendso
ports:
- 5432:5432
mailhog:
image: mailhog/mailhog:v1.0.1
ports:
- 8025:8025
- 1025:1025
strategy:
fail-fast: false
matrix:
@@ -24,13 +31,41 @@ jobs:
- run: echo 'NODE_OPTIONS="--max_old_space_size=4096"' >> $GITHUB_ENV
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/yarn-playwright-install
- uses: ./.github/actions/env-read-file
- uses: ./.github/actions/cache-db
- uses: ./.github/actions/cache-build
- name: Run Tests
run: yarn e2e --shard=${{ matrix.shard }}/${{ strategy.job-total }}
env:
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
E2E_TEST_MAILHOG_ENABLED: ${{ vars.E2E_TEST_MAILHOG_ENABLED }}
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
EMAIL_SERVER_HOST: ${{ secrets.CI_EMAIL_SERVER_HOST }}
EMAIL_SERVER_PORT: ${{ secrets.CI_EMAIL_SERVER_PORT }}
EMAIL_SERVER_USER: ${{ secrets.CI_EMAIL_SERVER_USER }}
EMAIL_SERVER_PASSWORD: ${{ secrets.CI_EMAIL_SERVER_PASSWORD}}
GOOGLE_LOGIN_ENABLED: ${{ vars.CI_GOOGLE_LOGIN_ENABLED }}
NEXTAUTH_SECRET: ${{ secrets.CI_NEXTAUTH_SECRET }}
NEXTAUTH_URL: ${{ secrets.CI_NEXTAUTH_URL }}
NEXT_PUBLIC_IS_E2E: ${{ vars.CI_NEXT_PUBLIC_IS_E2E }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
NEXT_PUBLIC_WEBAPP_URL: ${{ vars.CI_NEXT_PUBLIC_WEBAPP_URL }}
NEXT_PUBLIC_WEBSITE_URL: ${{ vars.CI_NEXT_PUBLIC_WEBSITE_URL }}
PAYMENT_FEE_FIXED: ${{ vars.CI_PAYMENT_FEE_FIXED }}
PAYMENT_FEE_PERCENTAGE: ${{ vars.CI_PAYMENT_FEE_PERCENTAGE }}
SAML_ADMINS: ${{ secrets.CI_SAML_ADMINS }}
SAML_DATABASE_URL: ${{ secrets.CI_SAML_DATABASE_URL }}
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
SENDGRID_API_KEY: ${{ secrets.CI_SENDGRID_API_KEY }}
SENDGRID_EMAIL: ${{ secrets.CI_SENDGRID_EMAIL }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
- name: Upload Test Results
if: ${{ always() }}
uses: actions/upload-artifact@v2
-55
View File
@@ -1,55 +0,0 @@
name: Create .env file
on:
workflow_call:
env:
ENV_FILE_NAME: gh.env-${{ github.sha }}
INPUT_ENV_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
INPUT_ENV_NEXT_PUBLIC_WEBAPP_URL: http://localhost:3000
INPUT_ENV_NEXT_PUBLIC_WEBSITE_URL: http://localhost:3000
INPUT_ENV_NEXTAUTH_SECRET: secret
INPUT_ENV_GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
INPUT_ENV_GOOGLE_LOGIN_ENABLED: true
# INPUT_ENV_CRON_API_KEY: xxx
# INPUT_ENV_CRON_ENABLE_APP_SYNC: true|false
INPUT_ENV_CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
INPUT_ENV_NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
INPUT_ENV_STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
INPUT_ENV_STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
INPUT_ENV_STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
INPUT_ENV_PAYMENT_FEE_PERCENTAGE: 0.005
INPUT_ENV_PAYMENT_FEE_FIXED: 10
INPUT_ENV_SAML_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
INPUT_ENV_SAML_ADMINS: pro@example.com
INPUT_ENV_NEXTAUTH_URL: http://localhost:3000
INPUT_ENV_NEXT_PUBLIC_IS_E2E: 1
# INPUT_ENV_EMAIL_FROM: e2e@cal.com
# INPUT_ENV_EMAIL_SERVER_HOST: ${{ secrets.CI_EMAIL_SERVER_HOST }}
# INPUT_ENV_EMAIL_SERVER_PORT: ${{ secrets.CI_EMAIL_SERVER_PORT }}
# INPUT_ENV_EMAIL_SERVER_USER: ${{ secrets.CI_EMAIL_SERVER_USER }}
# INPUT_ENV_EMAIL_SERVER_PASSWORD: ${{ secrets.CI_EMAIL_SERVER_PASSWORD }}
# INPUT_ENV_MS_GRAPH_CLIENT_ID: xxx
# INPUT_ENV_MS_GRAPH_CLIENT_SECRET: xxx
# INPUT_ENV_ZOOM_CLIENT_ID: xxx
# INPUT_ENV_ZOOM_CLIENT_SECRET: xxx
INPUT_ENV_SENDGRID_API_KEY: ${{ secrets.CI_SENDGRID_API_KEY }}
INPUT_ENV_SENDGRID_EMAIL: ${{ secrets.CI_SENDGRID_EMAIL }}
INPUT_ENV_TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
INPUT_ENV_TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
jobs:
create_env_file:
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
- uses: ozaytsev86/create-env-file@v1
with:
file-name: ${{ github.workspace }}/${{ env.ENV_FILE_NAME }}
- uses: buildjet/cache@v3
id: env-cache
with:
path: ${{ env.ENV_FILE_NAME }}
key: env-cache-${{ hashFiles(env.ENV_FILE_NAME) }}
+1 -11
View File
@@ -44,11 +44,6 @@ jobs:
- 'packages/embeds/**'
- 'playwright.config.ts'
env:
name: Create env file
uses: ./.github/workflows/env-create-file.yml
secrets: inherit
type-check:
name: Type check
uses: ./.github/workflows/check-types.yml
@@ -66,13 +61,11 @@ jobs:
build:
name: Production build
needs: env
uses: ./.github/workflows/production-build.yml
secrets: inherit
build-without-database:
name: Production build (without database)
needs: env
uses: ./.github/workflows/production-build-without-database.yml
secrets: inherit
@@ -84,21 +77,18 @@ jobs:
e2e-app-store:
name: E2E App Store tests
if: ${{ needs.changes.outputs.app-store == 'true' }}
needs: [changes, lint, build]
uses: ./.github/workflows/e2e-app-store.yml
secrets: inherit
e2e-embed:
name: E2E embeds tests
if: ${{ needs.changes.outputs.embed == 'true' }}
needs: [changes, lint, build]
uses: ./.github/workflows/e2e-embed.yml
secrets: inherit
e2e-embed-react:
name: E2E React embeds tests
if: ${{ needs.changes.outputs.embed-react == 'true' }}
needs: [changes, lint, build]
uses: ./.github/workflows/e2e-embed-react.yml
secrets: inherit
@@ -109,7 +99,7 @@ jobs:
secrets: inherit
required:
needs: [lint, type-check, test, build, e2e]
needs: [lint, type-check, test, build, e2e, e2e-embed, e2e-embed-react, e2e-app-store]
if: always()
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
@@ -3,6 +3,32 @@ name: Production Build (without database)
on:
workflow_call:
env:
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
GOOGLE_LOGIN_ENABLED: ${{ vars.CI_GOOGLE_LOGIN_ENABLED }}
NEXTAUTH_SECRET: ${{ secrets.CI_NEXTAUTH_SECRET }}
NEXTAUTH_URL: ${{ secrets.CI_NEXTAUTH_URL }}
NEXT_PUBLIC_IS_E2E: ${{ vars.CI_NEXT_PUBLIC_IS_E2E }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
NEXT_PUBLIC_WEBAPP_URL: ${{ vars.CI_NEXT_PUBLIC_WEBAPP_URL }}
NEXT_PUBLIC_WEBSITE_URL: ${{ vars.CI_NEXT_PUBLIC_WEBSITE_URL }}
PAYMENT_FEE_FIXED: ${{ vars.CI_PAYMENT_FEE_FIXED }}
PAYMENT_FEE_PERCENTAGE: ${{ vars.CI_PAYMENT_FEE_PERCENTAGE }}
SAML_ADMINS: ${{ secrets.CI_SAML_ADMINS }}
SAML_DATABASE_URL: ${{ secrets.CI_SAML_DATABASE_URL }}
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
SENDGRID_API_KEY: ${{ secrets.CI_SENDGRID_API_KEY }}
SENDGRID_EMAIL: ${{ secrets.CI_SENDGRID_EMAIL }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
jobs:
build:
name: Build
@@ -12,5 +38,4 @@ jobs:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/env-read-file
- uses: ./.github/actions/cache-build
+26 -1
View File
@@ -3,6 +3,32 @@ name: Production Build
on:
workflow_call:
env:
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
GOOGLE_LOGIN_ENABLED: ${{ vars.CI_GOOGLE_LOGIN_ENABLED }}
NEXTAUTH_SECRET: ${{ secrets.CI_NEXTAUTH_SECRET }}
NEXTAUTH_URL: ${{ secrets.CI_NEXTAUTH_URL }}
NEXT_PUBLIC_IS_E2E: ${{ vars.CI_NEXT_PUBLIC_IS_E2E }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
NEXT_PUBLIC_WEBAPP_URL: ${{ vars.CI_NEXT_PUBLIC_WEBAPP_URL }}
NEXT_PUBLIC_WEBSITE_URL: ${{ vars.CI_NEXT_PUBLIC_WEBSITE_URL }}
PAYMENT_FEE_FIXED: ${{ vars.CI_PAYMENT_FEE_FIXED }}
PAYMENT_FEE_PERCENTAGE: ${{ vars.CI_PAYMENT_FEE_PERCENTAGE }}
SAML_ADMINS: ${{ secrets.CI_SAML_ADMINS }}
SAML_DATABASE_URL: ${{ secrets.CI_SAML_DATABASE_URL }}
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
SENDGRID_API_KEY: ${{ secrets.CI_SENDGRID_API_KEY }}
SENDGRID_EMAIL: ${{ secrets.CI_SENDGRID_EMAIL }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
jobs:
build:
name: Build
@@ -20,6 +46,5 @@ jobs:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/env-read-file
- uses: ./.github/actions/cache-db
- uses: ./.github/actions/cache-build
+47
View File
@@ -0,0 +1,47 @@
name: "Release Docker"
on: # yamllint disable-line rule:truthy
release:
types:
- created
# in case manual trigger is needed
workflow_dispatch:
inputs:
RELEASE_TAG:
description: "v{Major}.{Minor}.{Patch}"
jobs:
release:
name: "Remote Release"
runs-on: "ubuntu-latest"
steps:
- name: checkout
uses: actions/checkout@v3
- name: "Determine tag"
run: 'echo "RELEASE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV'
- name: "Run remote release workflow"
uses: "actions/github-script@v6"
with:
# Requires a personal access token with Actions Read and write permissions on calcom/docker.
github-token: "${{ secrets.DOCKER_REPO_ACCESS_TOKEN }}"
script: |
try {
const response = await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: 'docker',
workflow_id: 'create-release.yaml',
ref: 'main',
inputs: {
"RELEASE_TAG": process.env.RELEASE_TAG
},
});
console.log(response);
} catch (error) {
console.error(error);
core.setFailed(error.message);
}
+4 -4
View File
@@ -29,11 +29,11 @@ jobs:
header: pr-title-lint-error
message: |
Hey there and thank you for opening this pull request! 👋🏼
We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted.
Details:
```
${{ steps.lint_pr_title.outputs.error_message }}
```
@@ -41,7 +41,7 @@ jobs:
# Delete a previous comment when the issue has been resolved
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
uses: marocchino/sticky-pull-request-comment@v2
with:
with:
header: pr-title-lint-error
message: |
Thank you for following the naming conventions! 🙏
Thank you for following the naming conventions! 🙏
+1
View File
@@ -1,6 +1,7 @@
name: Submodule Sync
on:
schedule:
# Runs "At minute 15 past every 4th hour." (see https://crontab.guru)
- cron: "15 */4 * * *"
workflow_dispatch: ~
jobs:
+18 -19
View File
@@ -1,28 +1,27 @@
name: "Welcome new contributors"
on:
issues:
types: opened
pull_request:
types: opened
issues:
types: opened
pull_request:
types: opened
permissions:
pull-requests: write
issues: write
jobs:
welcome-message:
name: Welcoming New Users
runs-on: ubuntu-latest
timeout-minutes: 10
needs: semantics
if: github.event.action == 'opened'
steps:
- uses: actions/first-interaction@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-message: |-
Thank you for making your first Pull Request and taking the time to improve Cal.com ! ❤️🎉
Feel free to join the conversation at [discord](https://go.cal.com/discord)
issue-message: |
Thank you for opening your first issue, one of our team members will review it as soon as it possible. ❤️🎉
welcome-message:
name: Welcoming New Users
runs-on: ubuntu-latest
timeout-minutes: 10
if: github.event.action == 'opened'
steps:
- uses: actions/first-interaction@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-message: |-
Thank you for making your first Pull Request and taking the time to improve Cal.com ! ❤️🎉
Feel free to join the conversation at [discord](https://go.cal.com/discord)
issue-message: |
Thank you for opening your first issue, one of our team members will review it as soon as it possible. ❤️🎉
+1 -1
View File
@@ -40,4 +40,4 @@ vscode:
- bradlc.vscode-tailwindcss
- ban.spellright
- stripe.vscode-stripe
- Prisma.prisma
- Prisma.prisma
+18 -8
View File
@@ -51,9 +51,9 @@
# Scheduling infrastructure for absolutely everyone
The open source Calendly alternative. You are in charge
of your own data, workflow and appearance.
of your own data, workflow, and appearance.
Calendly and other scheduling tools are awesome. It made our lives massively easier. We're using it for business meetings, seminars, yoga classes and even calls with our families. However, most tools are very limited in terms of control and customizations.
Calendly and other scheduling tools are awesome. It made our lives massively easier. We're using it for business meetings, seminars, yoga classes, and even calls with our families. However, most tools are very limited in terms of control and customization.
That's where Cal.com comes in. Self-hosted or hosted by us. White-label by design. API-driven and ready to be deployed on your own domain. Full control of your events and data.
@@ -90,7 +90,6 @@ That's where Cal.com comes in. Self-hosted or hosted by us. White-label by desig
- [Prisma.io](https://prisma.io/?ref=cal.com)
- [Daily.co](https://go.cal.com/daily)
## Contact us
Meet our sales team for any commercial inquiries.
@@ -99,7 +98,7 @@ Meet our sales team for any commercial inquiries.
## Stay Up-to-Date
Cal.com officially launched as v.1.0 on 15th of September 2021 and we've come a long way so far. Watch **releases** of this repository to be notified of future updates:
Cal.com officially launched as v.1.0 on the 15th of September 2021 and we've come a long way so far. Watch **releases** of this repository to be notified of future updates:
![cal-star-github](https://user-images.githubusercontent.com/8019099/154853944-a9e3c999-3da3-4048-b149-b4f73893c6fb.gif)
@@ -129,7 +128,7 @@ Here is what you need to be able to run Cal.com.
git clone https://github.com/calcom/cal.com.git
```
> If you are on windows, run the following command on `gitbash` with admin privileges: <br> > `git clone -c core.symlinks=true https://github.com/calcom/cal.com.git` <br>
> If you are on Windows, run the following command on `gitbash` with admin privileges: <br> > `git clone -c core.symlinks=true https://github.com/calcom/cal.com.git` <br>
> See [docs](https://cal.com/docs/how-to-guides/how-to-troubleshoot-symbolic-link-issues-on-windows#enable-symbolic-links) for more details.
1. Go to the project folder
@@ -222,6 +221,7 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env
```
1. Run [mailhog](https://github.com/mailhog/MailHog) to view emails sent during development
> **_NOTE:_** Required when `E2E_TEST_MAILHOG_ENABLED` is "1"
```sh
docker pull mailhog/mailhog
@@ -255,7 +255,7 @@ Be sure to set the environment variable `NEXTAUTH_URL` to the correct value. If
# In a terminal just run:
yarn test-e2e
# To open last HTML report run:
# To open the last HTML report run:
yarn playwright show-report test-results/reports/playwright-html-report
```
@@ -462,6 +462,18 @@ following
4. Now paste the API key to your `.env` file into the `DAILY_API_KEY` field in your `.env` file.
5. If you have the [Daily Scale Plan](https://daily.co/pricing) set the `DAILY_SCALE_PLAN` variable to `true` in order to use features like video recording.
### Obtaining Basecamp Client ID and Secret
1. Visit the [37 Signals Integrations Dashboard](launchpad.37signals.com/integrations) and sign in.
2. Register a new application by clicking the Register one now link.
3. Fill in your company details.
4. Select Basecamp 4 as the product to integrate with.
5. Set the Redirect URL for OAuth `<Cal.com URL>/api/integrations/basecamp3/callback` replacing Cal.com URL with the URI at which your application runs.
6. Click on done and copy the Client ID and secret into the `BASECAMP3_CLIENT_ID` and `BASECAMP3_CLIENT_SECRET` fields.
7. Set the `BASECAMP3_CLIENT_SECRET` env variable to `{your_domain} ({support_email})`.
For example, `Cal.com (support@cal.com)`.
### Obtaining HubSpot Client ID and Secret
1. Open [HubSpot Developer](https://developer.hubspot.com/) and sign into your account, or create a new one.
@@ -546,6 +558,4 @@ Special thanks to these amazing projects which help power Cal.com:
- [Tailwind CSS](https://tailwindcss.com/)
- [Prisma](https://prisma.io/)
<a href="https://jitsu.com/?utm_source=cal.com-gihub"><img height="40px" src="https://jitsu.com/img/powered-by-jitsu.png?gh=true" alt="Jitsu.com"></a>
Cal.com is an [open startup](https://cal.com/open) and [Jitsu](https://github.com/jitsucom/jitsu) (an open-source Segment alternative) helps us to track most of the usage metrics.
+1 -1
View File
@@ -7,7 +7,7 @@
<a href="https://cal.com/docs/enterprise-features/api#api-server-specifications">Read the API docs</a>
</div>
# Commercial Cal.com Public API
# Commercial Cal.com Public API
Welcome to the Public API ("/apps/api") of the Cal.com.
+7 -1
View File
@@ -1,6 +1,8 @@
import * as Sentry from "@sentry/nextjs";
import type { NextMiddleware } from "next-api-middleware";
import { redactError } from "@calcom/lib/redactError";
export const captureErrors: NextMiddleware = async (_req, res, next) => {
try {
// Catch any errors that are thrown in remaining
@@ -8,7 +10,11 @@ export const captureErrors: NextMiddleware = async (_req, res, next) => {
await next();
} catch (error) {
Sentry.captureException(error);
console.log(error);
const redactedError = redactError(error);
if (redactedError instanceof Error) {
res.status(400).json({ message: redactedError.message, error: redactedError });
return;
}
res.status(400).json({ message: "Something went wrong", error });
}
};
+2 -1
View File
@@ -2,6 +2,7 @@ import type { NextMiddleware } from "next-api-middleware";
import { hashAPIKey } from "@calcom/features/ee/api-keys/lib/apiKeys";
import checkLicense from "@calcom/features/ee/common/server/checkLicense";
import { IS_PRODUCTION } from "@calcom/lib/constants";
import { isAdminGuard } from "~/lib/utils/isAdmin";
@@ -17,7 +18,7 @@ export const dateNotInPast = function (date: Date) {
export const verifyApiKey: NextMiddleware = async (req, res, next) => {
const { prisma, isCustomPrisma, isAdmin } = req;
const hasValidLicense = await checkLicense(prisma);
if (!hasValidLicense && process.env.NODE_ENV !== "development")
if (!hasValidLicense && IS_PRODUCTION)
return res.status(401).json({ error: "Invalid or missing CALCOM_LICENSE_KEY environment variable" });
// If the user is an admin and using a license key (from customPrisma), skip the apiKey check.
if (isCustomPrisma && isAdmin) {
+10 -3
View File
@@ -28,10 +28,17 @@ const withMiddleware = label(
customPrismaClient,
extendRequest,
pagination: withPagination,
sentry: captureErrors,
captureErrors,
},
// The order here, determines the order of execution, put customPrismaClient before verifyApiKey always.
["extendRequest", "sentry", "customPrismaClient", "verifyApiKey", "addRequestId"] // <-- Provide a list of middleware to call automatically
// The order here, determines the order of execution
[
"extendRequest",
"captureErrors",
// - Put customPrismaClient before verifyApiKey always.
"customPrismaClient",
"verifyApiKey",
"addRequestId",
] // <-- Provide a list of middleware to call automatically
);
export { withMiddleware };
+10 -1
View File
@@ -33,12 +33,14 @@ export const schemaEventTypeBaseBodyParams = EventType.pick({
position: true,
eventName: true,
timeZone: true,
schedulingType: true,
// START Limit future bookings
periodType: true,
periodStartDate: true,
schedulingType: true,
periodEndDate: true,
periodDays: true,
periodCountCalendarDays: true,
// END Limit future bookings
requiresConfirmation: true,
disableGuests: true,
hideCalendarNotes: true,
@@ -51,6 +53,8 @@ export const schemaEventTypeBaseBodyParams = EventType.pick({
slotInterval: true,
successRedirectUrl: true,
locations: true,
bookingLimits: true,
durationLimits: true,
})
.merge(z.object({ hosts: z.array(hostSchema).optional().default([]) }))
.partial()
@@ -67,6 +71,7 @@ const schemaEventTypeCreateParams = z
seatsPerTimeSlot: z.number().optional(),
seatsShowAttendees: z.boolean().optional(),
bookingFields: eventTypeBookingFields.optional(),
scheduleId: z.number().optional(),
})
.strict();
@@ -85,6 +90,7 @@ const schemaEventTypeEditParams = z
seatsPerTimeSlot: z.number().optional(),
seatsShowAttendees: z.boolean().optional(),
bookingFields: eventTypeBookingFields.optional(),
scheduleId: z.number().optional(),
})
.strict();
@@ -98,6 +104,7 @@ export const schemaEventTypeReadPublic = EventType.pick({
position: true,
userId: true,
teamId: true,
scheduleId: true,
eventName: true,
timeZone: true,
periodType: true,
@@ -123,6 +130,8 @@ export const schemaEventTypeReadPublic = EventType.pick({
seatsPerTimeSlot: true,
seatsShowAttendees: true,
bookingFields: true,
bookingLimits: true,
durationLimits: true,
}).merge(
z.object({
locations: z
@@ -0,0 +1,20 @@
import { withValidation } from "next-validations";
import { z } from "zod";
import { baseApiParams } from "./baseApiParams";
// Extracted out as utility function so can be reused
// at different endpoints that require this validation.
export const schemaQueryUserEmail = baseApiParams.extend({
email: z.string().email(),
});
export const schemaQuerySingleOrMultipleUserEmails = z.object({
email: z.union([z.string().email(), z.array(z.string().email())]),
});
export const withValidQueryUserEmail = withValidation({
schema: schemaQueryUserEmail,
type: "Zod",
mode: "query",
});
+1 -1
View File
@@ -29,7 +29,7 @@ enum locales {
RO = "ro",
NL = "nl",
PT_BR = "pt-BR",
ES_419 = "es-419",
// ES_419 = "es-419", // Disabled until Crowdin reaches at least 80% completion
KO = "ko",
JA = "ja",
PL = "pl",
-3
View File
@@ -2,7 +2,6 @@ import type { NextApiRequest } from "next";
import { z } from "zod";
import { getUserAvailability } from "@calcom/core/getUserAvailability";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server";
import { availabilityUserSelect } from "@calcom/prisma";
@@ -120,10 +119,8 @@ const availabilitySchema = z
async function handler(req: NextApiRequest) {
const { prisma, isAdmin, userId: reqUserId } = req;
const { username, userId, eventTypeId, dateTo, dateFrom, teamId } = availabilitySchema.parse(req.query);
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(req.headers.host ?? "");
if (!teamId)
return getUserAvailability({
orgSlug: isValidOrgDomain ? currentOrgDomain || undefined : undefined,
username,
dateFrom,
dateTo,
+8 -1
View File
@@ -57,7 +57,14 @@ import { defaultResponder } from "@calcom/lib/server";
* format: email
* description: 'Attendee email address'
* location:
* type: string
* type: object
* properties:
* optionValue:
* type: string
* description: 'Option value for the location'
* value:
* type: string
* description: 'The meeting URL, Phone number or Address'
* description: 'Meeting location'
* metadata:
* type: object
+39 -3
View File
@@ -1,9 +1,11 @@
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server";
import { schemaEventTypeReadPublic } from "~/lib/validations/event-type";
import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt";
import { checkPermissions as canAccessTeamEventOrThrow } from "~/pages/api/teams/[teamId]/_auth-middleware";
import getCalLink from "../_utils/getCalLink";
@@ -42,7 +44,8 @@ import getCalLink from "../_utils/getCalLink";
export async function getHandler(req: NextApiRequest) {
const { prisma, query } = req;
const { id } = schemaQueryIdParseInt.parse(query);
const event_type = await prisma.eventType.findUnique({
const eventType = await prisma.eventType.findUnique({
where: { id },
include: {
customInputs: true,
@@ -51,10 +54,43 @@ export async function getHandler(req: NextApiRequest) {
owner: { select: { username: true, id: true } },
},
});
await checkPermissions(req, eventType);
const link = event_type ? getCalLink(event_type) : null;
const link = eventType ? getCalLink(eventType) : null;
// user.defaultScheduleId doesn't work the same for team events.
if (!eventType?.scheduleId && eventType?.userId && !eventType?.teamId) {
const user = await prisma.user.findUniqueOrThrow({
where: {
id: eventType.userId,
},
select: {
defaultScheduleId: true,
},
});
eventType.scheduleId = user.defaultScheduleId;
}
return { event_type: schemaEventTypeReadPublic.parse({ ...event_type, link }) };
// TODO: eventType when not found should be a 404
// but API consumers may depend on the {} behaviour.
return { event_type: schemaEventTypeReadPublic.parse({ ...eventType, link }) };
}
type BaseEventTypeCheckPermissions = {
userId: number | null;
teamId: number | null;
};
async function checkPermissions<T extends BaseEventTypeCheckPermissions>(
req: NextApiRequest,
eventType: (T & Partial<Omit<T, keyof BaseEventTypeCheckPermissions>>) | null
) {
if (req.isAdmin) return true;
if (eventType?.teamId) {
req.query.teamId = String(eventType.teamId);
await canAccessTeamEventOrThrow(req, "MEMBER");
}
if (eventType?.userId === req.userId) return true; // is owner.
throw new HttpError({ statusCode: 403, message: "Forbidden" });
}
export default defaultResponder(getHandler);
+12 -2
View File
@@ -1,4 +1,4 @@
import type { Prisma } from "@prisma/client";
import { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import type { z } from "zod";
@@ -52,6 +52,9 @@ import checkTeamEventEditPermission from "../_utils/checkTeamEventEditPermission
* slug:
* type: string
* description: Unique slug for the event type
* scheduleId:
* type: number
* description: The ID of the schedule for this event type
* hosts:
* type: array
* items:
@@ -199,10 +202,17 @@ import checkTeamEventEditPermission from "../_utils/checkTeamEventEditPermission
export async function patchHandler(req: NextApiRequest) {
const { prisma, query, body } = req;
const { id } = schemaQueryIdParseInt.parse(query);
const { hosts = [], ...parsedBody } = schemaEventTypeEditBodyParams.parse(body);
const {
hosts = [],
bookingLimits,
durationLimits,
...parsedBody
} = schemaEventTypeEditBodyParams.parse(body);
const data: Prisma.EventTypeUpdateArgs["data"] = {
...parsedBody,
bookingLimits: bookingLimits === null ? Prisma.DbNull : bookingLimits,
durationLimits: durationLimits === null ? Prisma.DbNull : durationLimits,
};
if (hosts) {
+72 -18
View File
@@ -1,8 +1,8 @@
import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server";
import type { PrismaClient } from "@calcom/prisma/client";
import { schemaEventTypeReadPublic } from "~/lib/validations/event-type";
import { schemaQuerySingleOrMultipleUserIds } from "~/lib/validations/shared/queryUserId";
@@ -35,20 +35,12 @@ import getCalLink from "./_utils/getCalLink";
* description: No event types were found
*/
async function getHandler(req: NextApiRequest) {
const { userId, isAdmin, prisma } = req;
const args: Prisma.EventTypeFindManyArgs = {
where: { userId },
};
/** Only admins can query other users */
if (!isAdmin && req.query.userId) throw new HttpError({ statusCode: 401, message: "ADMIN required" });
if (isAdmin && req.query.userId) {
const query = schemaQuerySingleOrMultipleUserIds.parse(req.query);
const userIds = Array.isArray(query.userId) ? query.userId : [query.userId || userId];
args.where = { userId: { in: userIds } };
}
const { userId, prisma } = req;
const userIds = req.query.userId ? extractUserIdsFromQuery(req) : [userId];
const data = await prisma.eventType.findMany({
...args,
where: {
userId: { in: userIds },
},
include: {
customInputs: true,
team: { select: { slug: true } },
@@ -56,12 +48,74 @@ async function getHandler(req: NextApiRequest) {
owner: { select: { username: true, id: true } },
},
});
// this really should return [], but backwards compatibility..
if (data.length === 0) new HttpError({ statusCode: 404, message: "No event types were found" });
return {
event_types: data.map((eventType) => {
const link = getCalLink(eventType);
return schemaEventTypeReadPublic.parse({ ...eventType, link });
}),
event_types: (await defaultScheduleId<(typeof data)[number]>({ eventTypes: data, prisma, userIds })).map(
(eventType) => {
const link = getCalLink(eventType);
return schemaEventTypeReadPublic.parse({ ...eventType, link });
}
),
};
}
// TODO: Extract & reuse.
function extractUserIdsFromQuery({ isAdmin, query }: NextApiRequest) {
/** Guard: Only admins can query other users */
if (!isAdmin) {
throw new HttpError({ statusCode: 401, message: "ADMIN required" });
}
const { userId: userIdOrUserIds } = schemaQuerySingleOrMultipleUserIds.parse(query);
return Array.isArray(userIdOrUserIds) ? userIdOrUserIds : [userIdOrUserIds];
}
type DefaultScheduleIdEventTypeBase = {
scheduleId: number | null;
userId: number | null;
};
// If an eventType is given w/o a scheduleId
// Then we associate the default user schedule id to the eventType
async function defaultScheduleId<T extends DefaultScheduleIdEventTypeBase>({
prisma,
eventTypes,
userIds,
}: {
prisma: PrismaClient;
eventTypes: (T & Partial<Omit<T, keyof DefaultScheduleIdEventTypeBase>>)[];
userIds: number[];
}) {
// there is no event types without a scheduleId, skip the user query
if (eventTypes.every((eventType) => eventType.scheduleId)) return eventTypes;
const users = await prisma.user.findMany({
where: {
id: {
in: userIds,
},
},
select: {
id: true,
defaultScheduleId: true,
},
});
if (!users.length) {
return eventTypes;
}
const defaultScheduleIds = users.reduce((result, user) => {
result[user.id] = user.defaultScheduleId;
return result;
}, {} as { [x: number]: number | null });
return eventTypes.map((eventType) => {
// realistically never happens, userId should't be null on personal event types.
if (!eventType.userId) return eventType;
return {
...eventType,
scheduleId: eventType.scheduleId || defaultScheduleIds[eventType.userId],
};
});
}
export default defaultResponder(getHandler);
+13 -2
View File
@@ -1,4 +1,4 @@
import type { Prisma } from "@prisma/client";
import { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
@@ -60,6 +60,9 @@ import ensureOnlyMembersAsHosts from "./_utils/ensureOnlyMembersAsHosts";
* hidden:
* type: boolean
* description: If the event type should be hidden from your public booking page
* scheduleId:
* type: number
* description: The ID of the schedule for this event type
* position:
* type: integer
* description: The position of the event type on the public booking page
@@ -178,6 +181,7 @@ import ensureOnlyMembersAsHosts from "./_utils/ensureOnlyMembersAsHosts";
* position: 0
* eventName: null
* timeZone: null
* scheduleId: 5
* periodType: UNLIMITED
* periodStartDate: 2023-02-15T08:46:16.000Z
* periodEndDate: 2023-0-15T08:46:16.000Z
@@ -255,12 +259,19 @@ import ensureOnlyMembersAsHosts from "./_utils/ensureOnlyMembersAsHosts";
async function postHandler(req: NextApiRequest) {
const { userId, isAdmin, prisma, body } = req;
const { hosts = [], ...parsedBody } = schemaEventTypeCreateBodyParams.parse(body || {});
const {
hosts = [],
bookingLimits,
durationLimits,
...parsedBody
} = schemaEventTypeCreateBodyParams.parse(body || {});
let data: Prisma.EventTypeCreateArgs["data"] = {
...parsedBody,
userId,
users: { connect: { id: userId } },
bookingLimits: bookingLimits === null ? Prisma.DbNull : bookingLimits,
durationLimits: durationLimits === null ? Prisma.DbNull : durationLimits,
};
await checkPermissions(req);
@@ -59,7 +59,7 @@ async function getHandler(req: NextApiRequest) {
};
const data = await prisma.eventType.findMany(args);
return { event_types: data.map((attendee) => schemaEventTypeReadPublic.parse(attendee)) };
return { event_types: data.map((eventType) => schemaEventTypeReadPublic.parse(eventType)) };
}
export default defaultResponder(getHandler);
+21 -1
View File
@@ -4,6 +4,7 @@ import type { NextApiRequest } from "next";
import { defaultResponder } from "@calcom/lib/server";
import { withMiddleware } from "~/lib/helpers/withMiddleware";
import { schemaQuerySingleOrMultipleUserEmails } from "~/lib/validations/shared/queryUserEmail";
import { schemaUsersReadPublic } from "~/lib/validations/user";
/**
@@ -19,6 +20,17 @@ import { schemaUsersReadPublic } from "~/lib/validations/user";
* schema:
* type: string
* description: Your API key
* - in: query
* name: email
* required: false
* schema:
* type: array
* items:
* type: string
* format: email
* style: form
* explode: true
* description: The email address or an array of email addresses to filter by
* tags:
* - users
* responses:
@@ -39,8 +51,16 @@ export async function getHandler(req: NextApiRequest) {
const where: Prisma.UserWhereInput = {};
// If user is not ADMIN, return only his data.
if (!isAdmin) where.id = userId;
if (req.query.email) {
const validationResult = schemaQuerySingleOrMultipleUserEmails.parse(req.query);
where.email = {
in: Array.isArray(validationResult.email) ? validationResult.email : [validationResult.email],
};
}
const [total, data] = await prisma.$transaction([
prisma.user.count(),
prisma.user.count({ where }),
prisma.user.findMany({ where, take, skip }),
]);
const users = schemaUsersReadPublic.parse(data);
+3 -3
View File
@@ -1,5 +1,5 @@
# Set the version of docker compose to use
version: '3.9'
version: "3.9"
# The containers that compose the project
services:
@@ -8,8 +8,8 @@ services:
restart: always
container_name: integration-tests-prisma
ports:
- '5433:5432'
- "5433:5432"
environment:
POSTGRES_USER: prisma
POSTGRES_PASSWORD: prisma
POSTGRES_DB: tests
POSTGRES_DB: tests
+21 -1
View File
@@ -1,9 +1,29 @@
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
const ns = ["common"];
const supportedLngs = ["en", "fr"];
const resources = ns.reduce((acc, n) => {
supportedLngs.forEach((lng) => {
if (!acc[lng]) acc[lng] = {};
acc[lng] = {
...acc[lng],
[n]: require(`../../web/public/static/locales/${lng}/${n}.json`),
};
});
return acc;
}, {});
i18n.use(initReactI18next).init({
resources: [],
debug: true,
fallbackLng: "en",
defaultNS: "common",
ns,
interpolation: {
escapeValue: false,
},
react: { useSuspense: true },
resources,
});
export default i18n;
+1
View File
@@ -15,6 +15,7 @@ module.exports = {
"storybook-addon-rtl-direction",
"storybook-react-i18next",
"storybook-addon-next",
"storybook-addon-next-router",
/*{
name: "storybook-addon-next",
options: {
+4 -4
View File
@@ -1,9 +1,9 @@
<script>
window.global = window;
window.isEmbed = ()=> {
return location.search.includes("embed=")
}
window.isEmbed = () => {
return location.search.includes("embed=");
};
</script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@500&display=swap');
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@500&display=swap");
</style>
+16
View File
@@ -1,4 +1,5 @@
import { addDecorator } from "@storybook/react";
import { AppRouterContext } from "next/dist/shared/lib/app-router-context";
import { I18nextProvider } from "react-i18next";
import "../styles/globals.css";
@@ -13,6 +14,21 @@ export const parameters = {
date: /Date$/,
},
},
nextRouter: {
pathname: "/",
asPath: "/",
query: {},
push() {},
Provider: AppRouterContext.Provider,
},
globals: {
locale: "en",
locales: {
en: "English",
fr: "Français",
},
},
i18n,
};
addDecorator((storyFn) => (
+2 -1
View File
@@ -18,6 +18,7 @@ export function VariantsTable({
const columns = React.Children.toArray(children) as ReactElement<RowProps>[];
return (
<div
id="light-variant"
className={classNames(
isDark &&
"relative py-8 before:absolute before:left-0 before:top-0 before:block before:h-full before:w-screen before:bg-[#1C1C1C]"
@@ -43,7 +44,7 @@ export function VariantsTable({
</table>
</div>
{!isDark && (
<div data-mode="dark" className="dark">
<div id="dark-variant" data-mode="dark" className="dark">
<VariantsTable titles={titles} isDark columnMinWidth={columnMinWidth}>
{children}
</VariantsTable>
+16 -6
View File
@@ -1,11 +1,21 @@
import { Meta } from '@storybook/addon-docs';
import { Meta } from "@storybook/addon-docs";
<Meta title="Introduction" />
<div className="text-center flex flex-col items-center">
<h1 style={{marginBottom: '24px', marginTop: '36px'}}>Welcome to Cal.com UI</h1>
<p>This is the beginning of our storybook impovments to match Figma as close as possible. Like our Figma library, we will be adding more components as we go along.</p>
<p>Our <a href="https://www.figma.com/file/9MOufQNLtdkpnDucmNX10R/%E2%9D%96-Cal-DS" target="_blank">Figma</a> library is avalible for anyone to view and use. If you have any questions or concerns, please reach out to the design team.</p>
<div className="flex flex-col items-center text-center">
<h1 style={{ marginBottom: "24px", marginTop: "36px" }}>Welcome to Cal.com UI</h1>
<p>
This is the beginning of our storybook improvements to match Figma as close as possible. Like our Figma
library, we will be adding more components as we go along.
</p>
<p>
Our{" "}
<a href="https://www.figma.com/file/9MOufQNLtdkpnDucmNX10R/%E2%9D%96-Cal-DS" target="_blank">
Figma
</a>{" "}
library is avalible for anyone to view and use. If you have any questions or concerns, please reach out to
the design team.
</p>
</div>
<img src="/sb-cover.jpg" />
<img src="/sb-cover.jpg" />
+1
View File
@@ -23,6 +23,7 @@
"next": "^13.4.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"storybook-addon-next-router": "^4.0.2",
"storybook-addon-rtl-direction": "^0.0.19"
},
"devDependencies": {
+53 -55
View File
@@ -1,8 +1,8 @@
@import url("../../../packages/features/calendars/weeklyview/styles/styles.css");
.sbdocs {
font-family: 'Inter var' !important;
padding: 0!important;
font-family: "Inter var" !important;
padding: 0 !important;
}
#docs-root {
@@ -21,8 +21,8 @@
font-weight: 500;
border: none;
border-top: 2px solid rgba(0, 0, 0, 0.12);
padding-top: 12px!important;
padding-bottom: 12px!important;
padding-top: 12px !important;
padding-bottom: 12px !important;
margin: 82px 0 0 0;
}
@@ -37,7 +37,7 @@
}
/** Docs table **/
.custom-args-wrapper{
.custom-args-wrapper {
max-height: 400px;
overflow-y: scroll;
overflow-x: hidden;
@@ -45,7 +45,7 @@
}
.docblock-argstable-body {
box-shadow: none!important;
box-shadow: none !important;
font-size: 14px;
}
@@ -64,7 +64,7 @@
.docblock-argstable-body div p,
.docblock-argstable-body div span {
color: #8F8F8F !important;
color: #8f8f8f !important;
}
/** Custom components **/
@@ -83,8 +83,8 @@
}
.story-title h1 span {
color: #9CA3AF;
font-family: 'Inter var';
color: #9ca3af;
font-family: "Inter var";
font-weight: normal;
display: inline-block;
margin-left: 8px;
@@ -96,7 +96,7 @@
}
.examples {
background-color: #F9FAFB;
background-color: #f9fafb;
padding: 20px;
display: flex;
flex-direction: column;
@@ -111,14 +111,14 @@
}
.examples-title {
color: #8F8F8F;
color: #8f8f8f;
font-size: 14px;
margin-bottom: 30px;
}
.examples-item-title {
font-size: 12px;
color: #8F8F8F;
color: #8f8f8f;
margin-bottom: 12px;
display: block;
}
@@ -130,7 +130,7 @@
.examples-footnote p,
.examples-footnote ul,
.examples-footnote li {
color: #8F8F8F;
color: #8f8f8f;
font-size: 14px;
}
@@ -139,11 +139,11 @@
}
.examples-footnote li {
margin: 0!important;
margin: 0 !important;
}
.story-note {
background-color: #F9FAFB;
background-color: #f9fafb;
font-size: 14px;
padding: 20px;
margin-bottom: 12px;
@@ -161,12 +161,12 @@
box-shadow: none !important;
margin: 0 !important;
border: none !important;
border-radius: none!important;
border-radius: none !important;
}
.docs-story > div:first-child {
padding: 0!important;
margin: 0!important;
padding: 0 !important;
margin: 0 !important;
}
.docs-story .innerZoomElementWrapper > div {
@@ -174,7 +174,7 @@
}
.sb-main-padded {
padding: 0!important;
padding: 0 !important;
}
@media screen and (max-width: 1200px) {
@@ -198,74 +198,72 @@
:root {
/* background */
--cal-bg-emphasis: #E5E7EB;
--cal-bg-emphasis: #e5e7eb;
--cal-bg: white;
--cal-bg-subtle: #F3F4F6;
--cal-bg-muted: #F9FAFB;
--cal-bg-subtle: #f3f4f6;
--cal-bg-muted: #f9fafb;
--cal-bg-inverted: #111827;
/* background -> components*/
--cal-bg-info: #DEE9FC;
--cal-bg-success: #E2FBE8;
--cal-bg-attention: #FCEED8;
--cal-bg-error: #F9E3E2;
--cal-bg-info: #dee9fc;
--cal-bg-success: #e2fbe8;
--cal-bg-attention: #fceed8;
--cal-bg-error: #f9e3e2;
--cal-bg-dark-error: #752522;
/* Borders */
--cal-border-emphasis:#9CA3AF;
--cal-border: #D1D5DB;
--cal-border-subtle:#E5E7EB;
--cal-border-muted:#F3F4F6;
--cal-border-emphasis: #9ca3af;
--cal-border: #d1d5db;
--cal-border-subtle: #e5e7eb;
--cal-border-muted: #f3f4f6;
--cal-border-error: #aa2e26;
/* Content/Text */
--cal-text-emphasis: #111827;
--cal-text:#374151;
--cal-text-subtle:#6B7280;
--cal-text-muted:#9CA3AF;
--cal-text-inverted:white;
--cal-text: #374151;
--cal-text-subtle: #6b7280;
--cal-text-muted: #9ca3af;
--cal-text-inverted: white;
/* Content/Text -> components */
--cal-text-info:#253985;
--cal-text-success:#285231;
--cal-text-attention:#73321B;
--cal-text-error:#752522;
--cal-text-info: #253985;
--cal-text-success: #285231;
--cal-text-attention: #73321b;
--cal-text-error: #752522;
/* Brand shinanigans
-> These will be computed for the users theme at runtime.
*/
--cal-brand:#111827;
--cal-brand-emphasis:#101010;
--cal-brand: #111827;
--cal-brand-emphasis: #101010;
--cal-brand-text: white;
}
.dark {
/* background */
--cal-bg-emphasis: #2B2B2B;
--cal-bg-emphasis: #2b2b2b;
--cal-bg: #101010;
--cal-bg-subtle: #2B2B2B;
--cal-bg-muted: #1C1C1C;
--cal-bg-inverted: #F3F4F6;
--cal-bg-subtle: #2b2b2b;
--cal-bg-muted: #1c1c1c;
--cal-bg-inverted: #f3f4f6;
/* background -> components*/
--cal-bg-info: #DEE9FC;
--cal-bg-success: #E2FBE8;
--cal-bg-attention: #FCEED8;
--cal-bg-error: #F9E3E2;
--cal-bg-info: #dee9fc;
--cal-bg-success: #e2fbe8;
--cal-bg-attention: #fceed8;
--cal-bg-error: #f9e3e2;
--cal-bg-dark-error: #752522;
/* Borders */
--cal-border-emphasis: #575757;
--cal-border: #444444;
--cal-border-subtle: #2B2B2B;
--cal-border-muted: #1C1C1C;
--cal-border-subtle: #2b2b2b;
--cal-border-muted: #1c1c1c;
--cal-border-error: #aa2e26;
/* Content/Text */
--cal-text-emphasis: #F3F4F6;
--cal-text: #D6D6D6;
--cal-text-emphasis: #f3f4f6;
--cal-text: #d6d6d6;
--cal-text-subtle: #757575;
--cal-text-muted: #575757;
--cal-text-inverted: #101010;
@@ -273,7 +271,7 @@
/* Content/Text -> components */
--cal-text-info: #253985;
--cal-text-success: #285231;
--cal-text-attention: #73321B;
--cal-text-attention: #73321b;
--cal-text-error: #752522;
/* Brand shenanigans
+13 -12
View File
@@ -2,8 +2,8 @@ html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans,
Droid Sans, Helvetica Neue, sans-serif;
}
a {
@@ -21,7 +21,7 @@ a {
}
.swagger-ui .opblock .opblock-summary {
display: grid;
flex-direction: column;
flex-direction: column;
}
.opblock-summary-path {
flex-shrink: 0;
@@ -43,22 +43,22 @@ a {
font-size: 22px;
}
.swagger-ui .scheme-container {
padding: 14px 0;
padding: 14px 0;
}
.swagger-ui .info {
margin: 10px 0;
margin: 10px 0;
}
.swagger-ui .auth-wrapper {
margin: 10px 0;
}
.swagger-ui .authorization__btn {
display: none;
display: none;
}
.swagger-ui .opblock {
margin: 0 0 5px;
}
button.opblock-summary-control > svg {
display: none;
display: none;
}
.swagger-ui .filter .operation-filter-input {
border: 2px solid #d8dde7;
@@ -73,15 +73,16 @@ a {
.swagger-ui .info .title small {
top: 5px;
}
.swagger-ui a.nostyle, .swagger-ui a.nostyle:visited {
.swagger-ui a.nostyle,
.swagger-ui a.nostyle:visited {
width: 100%;
}
div.request-snippets > div.curl-command > div:nth-child(1) {
overscroll-behavior: contain;
overflow-x: scroll;
}
.swagger-ui .opblock-body pre.microlight {
font-size: 9px;
.swagger-ui .opblock-body pre.microlight {
font-size: 9px;
}
.swagger-ui table tbody tr td {
padding: 0px 0 0;
@@ -91,7 +92,7 @@ a {
font-size: 12px;
}
div.no-margin > div > div.responses-wrapper > div.responses-inner > div > div > table > tbody > tr {
display: flex;
display: flex;
width: 100vw;
flex-direction: column;
font-size: 60%;
@@ -99,4 +100,4 @@ a {
div.no-margin > div > div.responses-wrapper > div.responses-inner > div > div > table > thead > tr {
display: none;
}
}
}
+34
View File
@@ -1,5 +1,39 @@
# @calcom/web
## 3.1.3
### Patch Changes
- Updated dependencies
- @calcom/embed-react@1.3.0
## 3.0.10
### Patch Changes
- Updated dependencies
- @calcom/embed-snippet@1.1.2
- @calcom/embed-react@1.2.2
- @calcom/embed-core@1.3.2
## 3.0.9
### Patch Changes
- Updated dependencies
- @calcom/embed-snippet@1.1.1
- @calcom/embed-react@1.2.1
- @calcom/embed-core@1.3.1
## 3.0.8
### Patch Changes
- Updated dependencies
- @calcom/embed-core@1.3.0
- @calcom/embed-react@1.2.0
- @calcom/embed-snippet@1.1.0
## 2.9.4
### Patch Changes
+17 -6
View File
@@ -1,15 +1,24 @@
import { useRouter } from "next/router";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import type { ReactNode } from "react";
import { useEffect, useRef, useState } from "react";
import { z } from "zod";
import type { CredentialOwner } from "@calcom/app-store/types";
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery";
import { Badge, ListItemText, Avatar } from "@calcom/ui";
import { AlertCircle } from "@calcom/ui/components/icon";
type ShouldHighlight = { slug: string; shouldHighlight: true } | { shouldHighlight?: never; slug?: never };
type ShouldHighlight =
| {
slug: string;
shouldHighlight: true;
}
| {
shouldHighlight?: never;
slug?: never;
};
type AppListCardProps = {
logo?: string;
@@ -46,14 +55,16 @@ export default function AppListCard(props: AppListCardProps) {
const router = useRouter();
const [highlight, setHighlight] = useState(shouldHighlight && hl === slug);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const searchParams = useSearchParams();
const pathname = usePathname();
useEffect(() => {
if (shouldHighlight && highlight) {
const timer = setTimeout(() => {
setHighlight(false);
const url = new URL(window.location.href);
url.searchParams.delete("hl");
router.replace(url.pathname, undefined, { shallow: true });
const _searchParams = new URLSearchParams(searchParams);
_searchParams.delete("hl");
router.replace(`${pathname}?${_searchParams.toString()}`);
}, 3000);
timeoutRef.current = timer;
}
@@ -96,7 +107,7 @@ export default function AppListCard(props: AppListCardProps) {
className="mr-2"
alt={credentialOwner.name || "Nameless"}
size="xs"
imageSrc={credentialOwner.avatar}
imageSrc={getPlaceholderAvatar(credentialOwner.avatar, credentialOwner?.name as string)}
/>
{credentialOwner.name}
</div>
-26
View File
@@ -1,26 +0,0 @@
import { useSession } from "next-auth/react";
import React from "react";
import NavTabs from "./NavTabs";
const tabs = [
{
name: "app_store",
href: "/apps",
},
{
name: "installed_apps",
href: "/apps/installed",
},
];
export default function AppsShell({ children }: { children: React.ReactNode }) {
const { status } = useSession();
return (
<>
<div className="mb-12 block lg:hidden">{status === "authenticated" && <NavTabs tabs={tabs} />}</div>
<main className="pb-6">{children}</main>
</>
);
}
-31
View File
@@ -1,31 +0,0 @@
import React from "react";
import NavTabs from "./NavTabs";
const tabs = [
{
name: "upcoming",
href: "/bookings/upcoming",
},
{
name: "recurring",
href: "/bookings/recurring",
},
{
name: "past",
href: "/bookings/past",
},
{
name: "cancelled",
href: "/bookings/cancelled",
},
];
export default function BookingsShell({ children }: { children: React.ReactNode }) {
return (
<>
<NavTabs tabs={tabs} />
<main>{children}</main>
</>
);
}
File diff suppressed because it is too large Load Diff
+38 -16
View File
@@ -1,32 +1,55 @@
import { isEmpty } from "lodash";
import { lookup } from "bcp-47-match";
import { useSession } from "next-auth/react";
import { useTranslation } from "next-i18next";
import { useEffect } from "react";
import { CALCOM_VERSION } from "@calcom/lib/constants";
import { trpc } from "@calcom/trpc/react";
export function useViewerI18n() {
return trpc.viewer.public.i18n.useQuery(undefined, {
staleTime: Infinity,
/**
* i18n should never be clubbed with other queries, so that it's caching can be managed independently.
* We intend to not cache i18n query
**/
trpc: {
context: { skipBatch: true },
},
});
function useViewerI18n(locale: string) {
return trpc.viewer.public.i18n.useQuery(
{ locale, CalComVersion: CALCOM_VERSION },
{
/**
* i18n should never be clubbed with other queries, so that it's caching can be managed independently.
**/
trpc: {
context: { skipBatch: true },
},
}
);
}
function useClientLocale(locales: string[]) {
const session = useSession();
// If the user is logged in, use their locale
if (session.data?.user.locale) return session.data.user.locale;
// If the user is not logged in, use the browser locale
if (typeof window !== "undefined") {
// This is the only way I found to ensure the prefetched locale is used on first render
// FIXME: Find a better way to pick the best matching locale from the browser
return lookup(locales, window.navigator.language) || window.navigator.language;
}
// If the browser is not available, use English
return "en";
}
export function useClientViewerI18n(locales: string[]) {
const clientLocale = useClientLocale(locales);
return useViewerI18n(clientLocale);
}
/**
* Auto-switches locale client-side to the logged in user's preference
*/
const I18nLanguageHandler = (): null => {
const I18nLanguageHandler = (props: { locales: string[] }) => {
const { locales } = props;
const { i18n } = useTranslation("common");
const locale = useViewerI18n().data?.locale || i18n.language;
const locale = useClientViewerI18n(locales).data?.locale || i18n.language;
useEffect(() => {
// bail early when i18n = {}
if (isEmpty(i18n)) return;
if (Object.keys(i18n).length === 0) return;
// if locale is ready and the i18n.language does != locale - changeLanguage
if (locale && i18n.language !== locale) {
i18n.changeLanguage(locale);
@@ -35,7 +58,6 @@ const I18nLanguageHandler = (): null => {
document.dir = i18n.dir();
document.documentElement.setAttribute("lang", locale);
}, [locale, i18n]);
return null;
};
-99
View File
@@ -1,99 +0,0 @@
import { noop } from "lodash";
import type { LinkProps } from "next/link";
import Link from "next/link";
import { useRouter } from "next/router";
import type { FC, MouseEventHandler } from "react";
import { Fragment } from "react";
import { PermissionContainer } from "@calcom/features/auth/PermissionContainer";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import classNames from "@lib/classNames";
import type { SVGComponent } from "@lib/types/SVGComponent";
export interface NavTabProps {
tabs: {
name: string;
/** If you want to change the path as per current tab */
href?: string;
/** If you want to change query param tabName as per current tab */
tabName?: string;
icon?: SVGComponent;
adminRequired?: boolean;
className?: string;
}[];
linkProps?: Omit<LinkProps, "href">;
}
const NavTabs: FC<NavTabProps> = ({ tabs, linkProps, ...props }) => {
const router = useRouter();
const { t } = useLocale();
return (
<>
<nav
className="no-scrollbar -mb-px flex space-x-5 overflow-x-scroll rtl:space-x-reverse sm:rtl:space-x-reverse"
aria-label="Tabs"
{...props}>
{tabs.map((tab) => {
if ((tab.tabName && tab.href) || (!tab.tabName && !tab.href)) {
throw new Error("Use either tabName or href");
}
let href = "";
let isCurrent;
if (tab.href) {
href = tab.href;
isCurrent = router.asPath === tab.href;
} else if (tab.tabName) {
href = "";
isCurrent = router.query.tabName === tab.tabName;
}
const onClick: MouseEventHandler = tab.tabName
? (e) => {
e.preventDefault();
router.push({
query: {
...router.query,
tabName: tab.tabName,
},
});
}
: noop;
const Component = tab.adminRequired ? PermissionContainer : Fragment;
const className = tab.className || "";
return (
<Component key={tab.name}>
<Link key={tab.name} href={href} {...linkProps} legacyBehavior>
<a
onClick={onClick}
className={classNames(
isCurrent
? "text-emphasis border-gray-900"
: "hover:border-default hover:text-default text-subtle border-transparent",
"group inline-flex items-center border-b-2 px-1 py-4 text-sm font-medium",
className
)}
aria-current={isCurrent ? "page" : undefined}>
{tab.icon && (
<tab.icon
className={classNames(
isCurrent ? "text-emphasis" : "group-hover:text-subtle text-muted",
"-ml-0.5 hidden h-4 w-4 ltr:mr-2 rtl:ml-2 sm:inline-block"
)}
aria-hidden="true"
/>
)}
<span>{t(tab.name)}</span>
</a>
</Link>
</Component>
);
})}
</nav>
<hr className="border-subtle" />
</>
);
};
export default NavTabs;
+2 -2
View File
@@ -6,7 +6,7 @@ import Script from "next/script";
import "@calcom/embed-core/src/embed-iframe";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import { WEBAPP_URL, IS_CALCOM } from "@calcom/lib/constants";
import { IS_CALCOM, WEBAPP_URL } from "@calcom/lib/constants";
import { buildCanonical } from "@calcom/lib/next-seo.config";
import type { AppProps } from "@lib/app-providers";
@@ -72,7 +72,7 @@ function PageWrapper(props: AppProps) {
}
{...seoConfig.defaultNextSeo}
/>
<I18nLanguageHandler />
<I18nLanguageHandler locales={props.router.locales || []} />
<Script
nonce={nonce}
id="page-status"
-60
View File
@@ -1,60 +0,0 @@
import type { ComponentProps } from "react";
import React from "react";
import Shell from "@calcom/features/shell/Shell";
import { ErrorBoundary } from "@calcom/ui";
import { CreditCard, Key, Lock, Terminal, User, Users } from "@calcom/ui/components/icon";
import NavTabs from "./NavTabs";
const tabs = [
{
name: "profile",
href: "/settings/my-account/profile",
icon: User,
},
{
name: "teams",
href: "/settings/teams",
icon: Users,
},
{
name: "security",
href: "/settings/security",
icon: Key,
},
{
name: "developer",
href: "/settings/developer",
icon: Terminal,
},
{
name: "billing",
href: "/settings/billing",
icon: CreditCard,
},
{
name: "admin",
href: "/settings/admin",
icon: Lock,
adminRequired: true,
},
];
export default function SettingsShell({
children,
...rest
}: { children: React.ReactNode } & ComponentProps<typeof Shell>) {
return (
<Shell {...rest} hideHeadingOnMobile>
<div className="sm:mx-auto">
<NavTabs tabs={tabs} />
</div>
<main className="max-w-4xl">
<>
<ErrorBoundary>{children}</ErrorBoundary>
</>
</main>
</Shell>
);
}
+6 -469
View File
@@ -1,376 +1,17 @@
import Link from "next/link";
import type { IframeHTMLAttributes } from "react";
import React, { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { CAL_URL } from "@calcom/lib/constants";
import type { RouterOutputs } from "@calcom/trpc/react";
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
import { InstallAppButton, AppDependencyComponent } from "@calcom/app-store/components";
import DisconnectIntegration from "@calcom/features/apps/components/DisconnectIntegration";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import type { UserAdminTeams } from "@calcom/features/ee/teams/lib/getUserAdminTeams";
import Shell from "@calcom/features/shell/Shell";
import classNames from "@calcom/lib/classNames";
import { APP_NAME, COMPANY_NAME, SUPPORT_MAIL_ADDRESS } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import type { App as AppType } from "@calcom/types/App";
import type { ButtonProps } from "@calcom/ui";
import type { AppFrontendPayload as App } from "@calcom/types/App";
import { Button, showToast, SkeletonButton, SkeletonText, HeadSeo, Badge } from "@calcom/ui";
import {
Dropdown,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuPortal,
DropdownMenuLabel,
DropdownItem,
Avatar,
} from "@calcom/ui";
import { BookOpen, Check, ExternalLink, File, Flag, Mail, Plus, Shield } from "@calcom/ui/components/icon";
import { Spinner } from "@calcom/features/calendars/weeklyview/components/spinner/Spinner";
import { HeadSeo } from "@calcom/ui";
/* These app slugs all require Google Cal to be installed */
const Component = ({
name,
type,
logo,
slug,
variant,
body,
categories,
author,
price = 0,
commission,
isGlobal = false,
feeType,
docs,
website,
email,
tos,
privacy,
teamsPlanRequired,
descriptionItems,
isTemplate,
dependencies,
}: Parameters<typeof App>[0]) => {
const { t, i18n } = useLocale();
const hasDescriptionItems = descriptionItems && descriptionItems.length > 0;
const mutation = useAddAppMutation(null, {
onSuccess: (data) => {
if (data?.setupPending) return;
showToast(t("app_successfully_installed"), "success");
},
onError: (error) => {
if (error instanceof Error) showToast(error.message || t("app_could_not_be_installed"), "error");
},
});
const priceInDollar = Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
useGrouping: false,
}).format(price);
const [existingCredentials, setExistingCredentials] = useState<number[]>([]);
const [showDisconnectIntegration, setShowDisconnectIntegration] = useState(false);
const appDbQuery = trpc.viewer.appCredentialsByType.useQuery(
{ appType: type },
{
onSettled(data) {
const credentialsCount = data?.credentials.length || 0
setShowDisconnectIntegration(data?.userAdminTeams.length ? credentialsCount >= data?.userAdminTeams.length : credentialsCount > 0);
setExistingCredentials(data?.credentials.map(credential => credential.id) || []);
},
}
);
const dependencyData = trpc.viewer.appsRouter.queryForDependencies.useQuery(dependencies, {
enabled: !!dependencies,
});
const disableInstall =
dependencyData.data && dependencyData.data.some((dependency) => !dependency.installed);
// const disableInstall = requiresGCal && !gCalInstalled.data;
// variant not other allows, an app to be shown in calendar category without requiring an actual calendar connection e.g. vimcal
// Such apps, can only be installed once.
const allowedMultipleInstalls = categories.indexOf("calendar") > -1 && variant !== "other";
return (
<div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0">
{hasDescriptionItems && (
<div className="align-center bg-subtle -ml-4 -mr-4 mb-4 flex min-h-[450px] w-auto basis-3/5 snap-x snap-mandatory flex-row overflow-auto whitespace-nowrap p-4 md:-ml-8 md:-mr-8 md:mb-8 md:p-8 lg:mx-0 lg:mb-0 lg:max-w-2xl lg:flex-col lg:justify-center lg:rounded-md">
{descriptionItems ? (
descriptionItems.map((descriptionItem, index) =>
typeof descriptionItem === "object" ? (
<div
key={`iframe-${index}`}
className="mr-4 max-h-full min-h-[315px] min-w-[90%] max-w-full snap-center last:mb-0 lg:mb-4 lg:mr-0 [&_iframe]:h-full [&_iframe]:min-h-[315px] [&_iframe]:w-full">
<iframe allowFullScreen {...descriptionItem.iframe} />
</div>
) : (
<img
key={descriptionItem}
src={descriptionItem}
alt={`Screenshot of app ${name}`}
className="mr-4 h-auto max-h-80 max-w-[90%] snap-center rounded-md object-contain last:mb-0 md:max-h-min lg:mb-4 lg:mr-0 lg:max-w-full"
/>
)
)
) : (
<SkeletonText />
)}
</div>
)}
<div
className={classNames(
"sticky top-0 -mt-4 max-w-xl basis-2/5 pb-12 text-sm lg:pb-0",
hasDescriptionItems && "lg:ml-8"
)}>
<div className="mb-8 flex pt-4">
<header>
<div className="mb-4 flex items-center">
<img
className={classNames(logo.includes("-dark") && "dark:invert", "min-h-16 min-w-16 h-16 w-16")}
src={logo}
alt={name}
/>
<h1 className="font-cal text-emphasis ml-4 text-3xl">{name}</h1>
</div>
<h2 className="text-default text-sm font-medium">
<Link
href={`categories/${categories[0]}`}
className="bg-subtle text-emphasis rounded-md p-1 text-xs capitalize">
{categories[0]}
</Link>{" "}
•{" "}
<a target="_blank" rel="noreferrer" href={website}>
{t("published_by", { author })}
</a>
</h2>
{isTemplate && (
<Badge variant="red" className="mt-4">
Template - Available in Dev Environment only for testing
</Badge>
)}
</header>
</div>
{!appDbQuery.isLoading ? (
isGlobal ||
(existingCredentials.length > 0 && allowedMultipleInstalls ? (
<div className="flex space-x-3">
<Button StartIcon={Check} color="secondary" disabled>
{existingCredentials.length > 0
? t("active_install", { count: existingCredentials.length })
: t("default")}
</Button>
{!isGlobal && (
<InstallAppButton
type={type}
disableInstall={disableInstall}
teamsPlanRequired={teamsPlanRequired}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
...props,
onClick: () => {
mutation.mutate({ type, variant, slug });
},
loading: mutation.isLoading,
};
}
return (
<InstallAppButtonChild appCategories={categories} userAdminTeams={appDbQuery.data?.userAdminTeams} addAppMutationInput={{ type, variant, slug }} multiInstall {...props} />
);
}}
/>
)}
</div>
) : showDisconnectIntegration ? (
<DisconnectIntegration
buttonProps={{ color: "secondary" }}
label={t("disconnect")}
credentialId={existingCredentials[0]}
onSuccess={() => {
appDbQuery.refetch();
}}
/>
) : (
<InstallAppButton
type={type}
disableInstall={disableInstall}
teamsPlanRequired={teamsPlanRequired}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
...props,
onClick: () => {
mutation.mutate({ type, variant, slug });
},
loading: mutation.isLoading,
};
}
return (
<InstallAppButtonChild appCategories={categories} userAdminTeams={appDbQuery.data?.userAdminTeams} addAppMutationInput={{type, variant, slug}} credentials={appDbQuery.data?.credentials} {...props} />
);
}}
/>
))
) : (
<SkeletonButton className="h-10 w-24" />
)}
{dependencies &&
(!dependencyData.isLoading ? (
<div className="mt-6">
<AppDependencyComponent appName={name} dependencyData={dependencyData.data} />
</div>
) : (
<SkeletonButton className="mt-6 h-20 grow" />
))}
{price !== 0 && (
<span className="block text-right">
{feeType === "usage-based" ? commission + "% + " + priceInDollar + "/booking" : priceInDollar}
{feeType === "monthly" && "/" + t("month")}
</span>
)}
<div className="prose-sm prose prose-a:text-default prose-headings:text-emphasis prose-code:text-default prose-strong:text-default text-default mt-8">
{body}
</div>
<h4 className="text-emphasis mt-8 font-semibold ">{t("pricing")}</h4>
<span className="text-default">
{teamsPlanRequired ? (
t("teams_plan_required")
) : price === 0 ? (
t("free_to_use_apps")
) : (
<>
{Intl.NumberFormat(i18n.language, {
style: "currency",
currency: "USD",
useGrouping: false,
}).format(price)}
{feeType === "monthly" && "/" + t("month")}
</>
)}
</span>
<h4 className="text-emphasis mb-2 mt-8 font-semibold ">{t("contact")}</h4>
<ul className="prose-sm -ml-1 -mr-1 leading-5">
{docs && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-emphasis text-sm font-normal no-underline hover:underline"
href={docs}>
<BookOpen className="text-subtle -mt-1 mr-1 inline h-4 w-4" />
{t("documentation")}
</a>
</li>
)}
{website && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={website}>
<ExternalLink className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{website.replace("https://", "")}
</a>
</li>
)}
{email && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={"mailto:" + email}>
<Mail className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{email}
</a>
</li>
)}
{tos && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={tos}>
<File className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{t("terms_of_service")}
</a>
</li>
)}
{privacy && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={privacy}>
<Shield className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{t("privacy_policy")}
</a>
</li>
)}
</ul>
<hr className="border-subtle my-8 border" />
<span className="leading-1 text-subtle block text-xs">
{t("every_app_published", { appName: APP_NAME, companyName: COMPANY_NAME })}
</span>
<a className="mt-2 block text-xs text-red-500" href={`mailto:${SUPPORT_MAIL_ADDRESS}`}>
<Flag className="inline h-3 w-3" /> {t("report_app")}
</a>
</div>
</div>
);
};
import type { AppPageProps } from "./AppPage";
import { AppPage } from "./AppPage";
const ShellHeading = () => {
const { t } = useLocale();
return <span className="block py-2">{t("app_store")}</span>;
};
export default function App(props: {
name: string;
description: AppType["description"];
type: AppType["type"];
isGlobal?: AppType["isGlobal"];
logo: string;
slug: string;
variant: string;
body: React.ReactNode;
categories: string[];
author: string;
pro?: boolean;
price?: number;
commission?: number;
feeType?: AppType["feeType"];
docs?: string;
website?: string;
email: string; // required
tos?: string;
privacy?: string;
licenseRequired: AppType["licenseRequired"];
teamsPlanRequired: AppType["teamsPlanRequired"];
descriptionItems?: Array<string | { iframe: IframeHTMLAttributes<HTMLIFrameElement> }>;
isTemplate?: boolean;
disableInstall?: boolean;
dependencies?: string[];
}) {
export default function WrappedApp(props: AppPageProps) {
return (
<Shell smallHeading isPublic hideHeadingOnMobile heading={<ShellHeading />} backPath="/apps" withoutSeo>
<HeadSeo
@@ -380,115 +21,11 @@ export default function App(props: {
/>
{props.licenseRequired ? (
<LicenseRequired>
<Component {...props} />
<AppPage {...props} />
</LicenseRequired>
) : (
<Component {...props} />
<AppPage {...props} />
)}
</Shell>
);
}
const InstallAppButtonChild = ({
userAdminTeams,
addAppMutationInput,
appCategories,
multiInstall,
credentials,
...props
}: {
userAdminTeams?: UserAdminTeams;
addAppMutationInput: { type: App["type"]; variant: string; slug: string };
appCategories: string[];
multiInstall?: boolean;
credentials?: RouterOutputs["viewer"]["appCredentialsByType"]["credentials"];
} & ButtonProps) => {
const { t } = useLocale();
const router = useRouter();
const mutation = useAddAppMutation(null, {
onSuccess: (data) => {
if (data?.setupPending) return;
showToast(t("app_successfully_installed"), "success");
},
onError: (error) => {
if (error instanceof Error) showToast(error.message || t("app_could_not_be_installed"), "error");
},
});
if (!userAdminTeams?.length || appCategories.some((category) => category === "calendar")) {
return <Button
data-testid="install-app-button"
{...props}
// @TODO: Overriding color and size prevent us from
// having to duplicate InstallAppButton for now.
color="primary"
size="base">
{multiInstall ? t("install_another") : t("install_app")}
</Button>
}
return (
<Dropdown>
<DropdownMenuTrigger asChild>
<Button
data-testid="install-app-button"
{...props}
// @TODO: Overriding color and size prevent us from
// having to duplicate InstallAppButton for now.
color="primary"
size="base">
{multiInstall ? t("install_another") : t("install_app")}
</Button>
</DropdownMenuTrigger>
<DropdownMenuPortal>
<DropdownMenuContent
onInteractOutside={(event) => {
if (mutation.isLoading) event.preventDefault();
}}
>
{mutation.isLoading && (
<div className="z-1 fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<Spinner />
</div>
)}
<DropdownMenuLabel>{t("install_app_on")}</DropdownMenuLabel>
{userAdminTeams.map((team) => {
const isInstalled = credentials &&
credentials.some((credential) =>
credential?.teamId ? credential?.teamId === team.id : credential.userId === team.id
)
return (
<DropdownItem
type="button"
data-testid={team.isUser ? "install-app-button-personal" : "anything else"}
key={team.id}
disabled={
isInstalled
}
StartIcon={(props) => (
<Avatar
alt={team.logo || ""}
imageSrc={team.logo || `${CAL_URL}/${team.logo}/avatar.png`} // if no image, use default avatar
size="sm"
{...props}
/>
)}
onClick={() => {
mutation.mutate(
team.isUser ? addAppMutationInput : { ...addAppMutationInput, teamId: team.id }
);
}}>
<p>{team.name}{" "}
{isInstalled &&
`(${t("installed")})`}</p>
</DropdownItem>
)})}
</DropdownMenuContent>
</DropdownMenuPortal>
</Dropdown>
);
};
+367
View File
@@ -0,0 +1,367 @@
import Link from "next/link";
import type { IframeHTMLAttributes } from "react";
import React, { useState } from "react";
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
import { AppDependencyComponent, InstallAppButton } from "@calcom/app-store/components";
import DisconnectIntegration from "@calcom/features/apps/components/DisconnectIntegration";
import classNames from "@calcom/lib/classNames";
import { APP_NAME, COMPANY_NAME, SUPPORT_MAIL_ADDRESS } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import type { App as AppType } from "@calcom/types/App";
import { Badge, Button, showToast, SkeletonButton, SkeletonText } from "@calcom/ui";
import { BookOpen, Check, ExternalLink, File, Flag, Mail, Shield } from "@calcom/ui/components/icon";
import { InstallAppButtonChild } from "./InstallAppButtonChild";
export type AppPageProps = {
name: string;
description: AppType["description"];
type: AppType["type"];
isGlobal?: AppType["isGlobal"];
logo: string;
slug: string;
variant: string;
body: React.ReactNode;
categories: string[];
author: string;
pro?: boolean;
price?: number;
commission?: number;
feeType?: AppType["feeType"];
docs?: string;
website?: string;
email: string; // required
tos?: string;
privacy?: string;
licenseRequired: AppType["licenseRequired"];
teamsPlanRequired: AppType["teamsPlanRequired"];
descriptionItems?: Array<string | { iframe: IframeHTMLAttributes<HTMLIFrameElement> }>;
isTemplate?: boolean;
disableInstall?: boolean;
dependencies?: string[];
concurrentMeetings: AppType["concurrentMeetings"];
};
export const AppPage = ({
name,
type,
logo,
slug,
variant,
body,
categories,
author,
price = 0,
commission,
isGlobal = false,
feeType,
docs,
website,
email,
tos,
privacy,
teamsPlanRequired,
descriptionItems,
isTemplate,
dependencies,
concurrentMeetings,
}: AppPageProps) => {
const { t, i18n } = useLocale();
const hasDescriptionItems = descriptionItems && descriptionItems.length > 0;
const mutation = useAddAppMutation(null, {
onSuccess: (data) => {
if (data?.setupPending) return;
showToast(t("app_successfully_installed"), "success");
},
onError: (error) => {
if (error instanceof Error) showToast(error.message || t("app_could_not_be_installed"), "error");
},
});
const priceInDollar = Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
useGrouping: false,
}).format(price);
const [existingCredentials, setExistingCredentials] = useState<number[]>([]);
const [showDisconnectIntegration, setShowDisconnectIntegration] = useState(false);
const appDbQuery = trpc.viewer.appCredentialsByType.useQuery(
{ appType: type },
{
onSettled(data) {
const credentialsCount = data?.credentials.length || 0;
setShowDisconnectIntegration(
data?.userAdminTeams.length ? credentialsCount >= data?.userAdminTeams.length : credentialsCount > 0
);
setExistingCredentials(data?.credentials.map((credential) => credential.id) || []);
},
}
);
const dependencyData = trpc.viewer.appsRouter.queryForDependencies.useQuery(dependencies, {
enabled: !!dependencies,
});
const disableInstall =
dependencyData.data && dependencyData.data.some((dependency) => !dependency.installed);
// const disableInstall = requiresGCal && !gCalInstalled.data;
// variant not other allows, an app to be shown in calendar category without requiring an actual calendar connection e.g. vimcal
// Such apps, can only be installed once.
const allowedMultipleInstalls = categories.indexOf("calendar") > -1 && variant !== "other";
return (
<div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0">
{hasDescriptionItems && (
<div className="align-center bg-subtle -ml-4 -mr-4 mb-4 flex min-h-[450px] w-auto basis-3/5 snap-x snap-mandatory flex-row overflow-auto whitespace-nowrap p-4 md:-ml-8 md:-mr-8 md:mb-8 md:p-8 lg:mx-0 lg:mb-0 lg:max-w-2xl lg:flex-col lg:justify-center lg:rounded-md">
{descriptionItems ? (
descriptionItems.map((descriptionItem, index) =>
typeof descriptionItem === "object" ? (
<div
key={`iframe-${index}`}
className="mr-4 max-h-full min-h-[315px] min-w-[90%] max-w-full snap-center last:mb-0 lg:mb-4 lg:mr-0 [&_iframe]:h-full [&_iframe]:min-h-[315px] [&_iframe]:w-full">
<iframe allowFullScreen {...descriptionItem.iframe} />
</div>
) : (
<img
key={descriptionItem}
src={descriptionItem}
alt={`Screenshot of app ${name}`}
className="mr-4 h-auto max-h-80 max-w-[90%] snap-center rounded-md object-contain last:mb-0 md:max-h-min lg:mb-4 lg:mr-0 lg:max-w-full"
/>
)
)
) : (
<SkeletonText />
)}
</div>
)}
<div
className={classNames(
"sticky top-0 -mt-4 max-w-xl basis-2/5 pb-12 text-sm lg:pb-0",
hasDescriptionItems && "lg:ml-8"
)}>
<div className="mb-8 flex pt-4">
<header>
<div className="mb-4 flex items-center">
<img
className={classNames(logo.includes("-dark") && "dark:invert", "min-h-16 min-w-16 h-16 w-16")}
src={logo}
alt={name}
/>
<h1 className="font-cal text-emphasis ml-4 text-3xl">{name}</h1>
</div>
<h2 className="text-default text-sm font-medium">
<Link
href={`categories/${categories[0]}`}
className="bg-subtle text-emphasis rounded-md p-1 text-xs capitalize">
{categories[0]}
</Link>{" "}
•{" "}
<a target="_blank" rel="noreferrer" href={website}>
{t("published_by", { author })}
</a>
</h2>
{isTemplate && (
<Badge variant="red" className="mt-4">
Template - Available in Dev Environment only for testing
</Badge>
)}
</header>
</div>
{!appDbQuery.isLoading ? (
isGlobal ||
(existingCredentials.length > 0 && allowedMultipleInstalls ? (
<div className="flex space-x-3">
<Button StartIcon={Check} color="secondary" disabled>
{existingCredentials.length > 0
? t("active_install", { count: existingCredentials.length })
: t("default")}
</Button>
{!isGlobal && (
<InstallAppButton
type={type}
disableInstall={disableInstall}
teamsPlanRequired={teamsPlanRequired}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
...props,
onClick: () => {
mutation.mutate({ type, variant, slug });
},
loading: mutation.isLoading,
};
}
return (
<InstallAppButtonChild
appCategories={categories}
userAdminTeams={appDbQuery.data?.userAdminTeams}
addAppMutationInput={{ type, variant, slug }}
multiInstall
concurrentMeetings={concurrentMeetings}
{...props}
/>
);
}}
/>
)}
</div>
) : showDisconnectIntegration ? (
<DisconnectIntegration
buttonProps={{ color: "secondary" }}
label={t("disconnect")}
credentialId={existingCredentials[0]}
onSuccess={() => {
appDbQuery.refetch();
}}
/>
) : (
<InstallAppButton
type={type}
disableInstall={disableInstall}
teamsPlanRequired={teamsPlanRequired}
render={({ useDefaultComponent, ...props }) => {
if (useDefaultComponent) {
props = {
...props,
onClick: () => {
mutation.mutate({ type, variant, slug });
},
loading: mutation.isLoading,
};
}
return (
<InstallAppButtonChild
appCategories={categories}
userAdminTeams={appDbQuery.data?.userAdminTeams}
addAppMutationInput={{ type, variant, slug }}
credentials={appDbQuery.data?.credentials}
concurrentMeetings={concurrentMeetings}
{...props}
/>
);
}}
/>
))
) : (
<SkeletonButton className="h-10 w-24" />
)}
{dependencies &&
(!dependencyData.isLoading ? (
<div className="mt-6">
<AppDependencyComponent appName={name} dependencyData={dependencyData.data} />
</div>
) : (
<SkeletonButton className="mt-6 h-20 grow" />
))}
{price !== 0 && (
<span className="block text-right">
{feeType === "usage-based" ? commission + "% + " + priceInDollar + "/booking" : priceInDollar}
{feeType === "monthly" && "/" + t("month")}
</span>
)}
<div className="prose-sm prose prose-a:text-default prose-headings:text-emphasis prose-code:text-default prose-strong:text-default text-default mt-8">
{body}
</div>
<h4 className="text-emphasis mt-8 font-semibold ">{t("pricing")}</h4>
<span className="text-default">
{teamsPlanRequired ? (
t("teams_plan_required")
) : price === 0 ? (
t("free_to_use_apps")
) : (
<>
{Intl.NumberFormat(i18n.language, {
style: "currency",
currency: "USD",
useGrouping: false,
}).format(price)}
{feeType === "monthly" && "/" + t("month")}
</>
)}
</span>
<h4 className="text-emphasis mb-2 mt-8 font-semibold ">{t("contact")}</h4>
<ul className="prose-sm -ml-1 -mr-1 leading-5">
{docs && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-emphasis text-sm font-normal no-underline hover:underline"
href={docs}>
<BookOpen className="text-subtle -mt-1 mr-1 inline h-4 w-4" />
{t("documentation")}
</a>
</li>
)}
{website && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={website}>
<ExternalLink className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{website.replace("https://", "")}
</a>
</li>
)}
{email && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={"mailto:" + email}>
<Mail className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{email}
</a>
</li>
)}
{tos && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={tos}>
<File className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{t("terms_of_service")}
</a>
</li>
)}
{privacy && (
<li>
<a
target="_blank"
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={privacy}>
<Shield className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{t("privacy_policy")}
</a>
</li>
)}
</ul>
<hr className="border-subtle my-8 border" />
<span className="leading-1 text-subtle block text-xs">
{t("every_app_published", { appName: APP_NAME, companyName: COMPANY_NAME })}
</span>
<a className="mt-2 block text-xs text-red-500" href={`mailto:${SUPPORT_MAIL_ADDRESS}`}>
<Flag className="inline h-3 w-3" /> {t("report_app")}
</a>
</div>
</div>
);
};
@@ -0,0 +1,124 @@
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
import { doesAppSupportTeamInstall } from "@calcom/app-store/utils";
import { Spinner } from "@calcom/features/calendars/weeklyview/components/spinner/Spinner";
import type { UserAdminTeams } from "@calcom/features/ee/teams/lib/getUserAdminTeams";
import { CAL_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterOutputs } from "@calcom/trpc/react";
import type { AppFrontendPayload } from "@calcom/types/App";
import type { ButtonProps } from "@calcom/ui";
import {
Avatar,
Button,
Dropdown,
DropdownItem,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuTrigger,
showToast,
} from "@calcom/ui";
export const InstallAppButtonChild = ({
userAdminTeams,
addAppMutationInput,
appCategories,
multiInstall,
credentials,
concurrentMeetings,
...props
}: {
userAdminTeams?: UserAdminTeams;
addAppMutationInput: { type: AppFrontendPayload["type"]; variant: string; slug: string };
appCategories: string[];
multiInstall?: boolean;
credentials?: RouterOutputs["viewer"]["appCredentialsByType"]["credentials"];
concurrentMeetings?: boolean;
} & ButtonProps) => {
const { t } = useLocale();
const mutation = useAddAppMutation(null, {
onSuccess: (data) => {
if (data?.setupPending) return;
showToast(t("app_successfully_installed"), "success");
},
onError: (error) => {
if (error instanceof Error) showToast(error.message || t("app_could_not_be_installed"), "error");
},
});
if (!userAdminTeams?.length || !doesAppSupportTeamInstall(appCategories, concurrentMeetings)) {
return (
<Button
data-testid="install-app-button"
{...props}
// @TODO: Overriding color and size prevent us from
// having to duplicate InstallAppButton for now.
color="primary"
size="base">
{multiInstall ? t("install_another") : t("install_app")}
</Button>
);
}
return (
<Dropdown>
<DropdownMenuTrigger asChild>
<Button
data-testid="install-app-button"
{...props}
// @TODO: Overriding color and size prevent us from
// having to duplicate InstallAppButton for now.
color="primary"
size="base">
{multiInstall ? t("install_another") : t("install_app")}
</Button>
</DropdownMenuTrigger>
<DropdownMenuPortal>
<DropdownMenuContent
onInteractOutside={(event) => {
if (mutation.isLoading) event.preventDefault();
}}>
{mutation.isLoading && (
<div className="z-1 fixed inset-0 flex items-center justify-center">
<Spinner />
</div>
)}
<DropdownMenuLabel>{t("install_app_on")}</DropdownMenuLabel>
{userAdminTeams.map((team) => {
const isInstalled =
credentials &&
credentials.some((credential) =>
credential?.teamId ? credential?.teamId === team.id : credential.userId === team.id
);
return (
<DropdownItem
type="button"
data-testid={team.isUser ? "install-app-button-personal" : "anything else"}
key={team.id}
disabled={isInstalled}
StartIcon={(props) => (
<Avatar
alt={team.logo || ""}
imageSrc={team.logo || `${CAL_URL}/${team.logo}/avatar.png`} // if no image, use default avatar
size="sm"
{...props}
/>
)}
onClick={() => {
mutation.mutate(
team.isUser ? addAppMutationInput : { ...addAppMutationInput, teamId: team.id }
);
}}>
<p>
{t(team.name)} {isInstalled && `(${t("installed")})`}
</p>
</DropdownItem>
);
})}
</DropdownMenuContent>
</DropdownMenuPortal>
</Dropdown>
);
};
@@ -1,9 +1,9 @@
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import { useRouter } from "next/navigation";
import type { ComponentProps } from "react";
import React from "react";
import Shell from "@calcom/features/shell/Shell";
import { ShellMain } from "@calcom/features/shell/Shell";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { EmptyScreen } from "@calcom/ui";
import { AlertCircle } from "@calcom/ui/components/icon";
@@ -12,7 +12,7 @@ type AppsLayoutProps = {
children: React.ReactNode;
actions?: (className?: string) => JSX.Element;
emptyStore?: boolean;
} & Omit<ComponentProps<typeof Shell>, "actions">;
} & Omit<ComponentProps<typeof ShellMain>, "actions">;
export default function AppsLayout({ children, actions, emptyStore, ...rest }: AppsLayoutProps) {
const { t } = useLocale();
@@ -22,7 +22,7 @@ export default function AppsLayout({ children, actions, emptyStore, ...rest }: A
if (session.status === "loading") return <></>;
return (
<Shell {...rest} actions={actions?.("block")} hideHeadingOnMobile>
<ShellMain {...rest} actions={actions?.("block")} hideHeadingOnMobile>
<div className="flex flex-col xl:flex-row">
<main className="w-full">
{emptyStore ? (
@@ -38,7 +38,6 @@ export default function AppsLayout({ children, actions, emptyStore, ...rest }: A
)}
</main>
</div>
</Shell>
</ShellMain>
);
}
export const getLayout = (page: React.ReactElement) => <AppsLayout>{page}</AppsLayout>;
@@ -1,5 +1,5 @@
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import { usePathname, useRouter } from "next/navigation";
import type { ComponentProps } from "react";
import React, { useEffect } from "react";
@@ -10,9 +10,9 @@ import { ErrorBoundary } from "@calcom/ui";
export default function AdminLayout({
children,
...rest
}: { children: React.ReactNode } & ComponentProps<typeof Shell>) {
const pathname = usePathname();
const session = useSession();
const router = useRouter();
@@ -23,7 +23,7 @@ export default function AdminLayout({
}
}, [session, router]);
const isAppsPage = router.asPath.startsWith("/settings/admin/apps");
const isAppsPage = pathname?.startsWith("/settings/admin/apps");
return (
<SettingsLayout {...rest}>
<div className="divide-subtle mx-auto flex max-w-4xl flex-row divide-y">
@@ -1,228 +0,0 @@
import { useAutoAnimate } from "@formkit/auto-animate/react";
import Link from "next/link";
import { useRouter } from "next/router";
import type { FC } from "react";
import { useEffect, useState, useCallback } from "react";
import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import useMediaQuery from "@calcom/lib/hooks/useMediaQuery";
import { TimeFormat } from "@calcom/lib/timeFormat";
import { nameOfDay } from "@calcom/lib/weekday";
import { trpc } from "@calcom/trpc/react";
import type { Slot } from "@calcom/trpc/server/routers/viewer/slots/types";
import { SkeletonContainer, SkeletonText, ToggleGroup } from "@calcom/ui";
import classNames from "@lib/classNames";
import { timeZone } from "@lib/clock";
type AvailableTimesProps = {
timeFormat: TimeFormat;
onTimeFormatChange: (is24Hour: boolean) => void;
eventTypeId: number;
recurringCount: number | undefined;
eventTypeSlug: string;
date?: Dayjs;
seatsPerTimeSlot?: number | null;
bookingAttendees?: number | null;
slots?: Slot[];
isLoading: boolean;
duration: number;
};
const AvailableTimes: FC<AvailableTimesProps> = ({
slots = [],
isLoading,
date,
eventTypeId,
eventTypeSlug,
recurringCount,
timeFormat,
onTimeFormatChange,
seatsPerTimeSlot,
bookingAttendees,
duration,
}) => {
const reserveSlotMutation = trpc.viewer.public.slots.reserveSlot.useMutation();
const [slotPickerRef] = useAutoAnimate<HTMLDivElement>();
const { t, i18n } = useLocale();
const router = useRouter();
const { rescheduleUid } = router.query;
const [brand, setBrand] = useState("#292929");
useEffect(() => {
setBrand(getComputedStyle(document.documentElement).getPropertyValue("--brand-color").trim());
}, []);
const isMobile = useMediaQuery("(max-width: 768px)");
const ref = useCallback(
(node: HTMLDivElement) => {
if (isMobile) {
node?.scrollIntoView({ behavior: "smooth" });
}
},
[isMobile]
);
const reserveSlot = (slot: Slot) => {
// Prevent double clicking
if (reserveSlotMutation.isLoading || reserveSlotMutation.isSuccess) {
return;
}
reserveSlotMutation.mutate({
slotUtcStartDate: slot.time,
eventTypeId,
slotUtcEndDate: dayjs(slot.time).utc().add(duration, "minutes").format(),
bookingUid: slot.bookingUid,
});
};
return (
<div ref={slotPickerRef}>
{!!date ? (
<div className="mt-8 flex h-full w-full flex-col rounded-md px-4 text-center sm:mt-0 sm:p-4 md:-mb-5 md:min-w-[200px] md:p-4 lg:min-w-[300px]">
<div className="mb-4 flex items-center text-left text-base">
<div className="mr-4">
<span className="text-emphasis font-semibold">
{nameOfDay(i18n.language, Number(date.format("d")), "short")}
</span>
<span className="text-subtle">
, {date.toDate().toLocaleString(i18n.language, { month: "short" })} {date.format(" D ")}
</span>
</div>
<div className="ml-auto">
<ToggleGroup
onValueChange={(timeFormat) => onTimeFormatChange(timeFormat === "24")}
defaultValue={timeFormat === TimeFormat.TWELVE_HOUR ? "12" : "24"}
options={[
{ value: "12", label: t("12_hour_short") },
{ value: "24", label: t("24_hour_short") },
]}
/>
</div>
</div>
<div
ref={ref}
className="scroll-bar scrollbar-track-w-20 relative -mb-4 flex-grow overflow-y-auto sm:block md:h-[364px]">
{slots.length > 0 &&
slots.map((slot) => {
type BookingURL = {
pathname: string;
query: Record<string, string | number | string[] | undefined | TimeFormat>;
};
const bookingUrl: BookingURL = {
pathname: router.pathname.endsWith("/embed") ? "../book" : "book",
query: {
...router.query,
date: dayjs.utc(slot.time).tz(timeZone()).format(),
type: eventTypeId,
slug: eventTypeSlug,
timeFormat,
/** Treat as recurring only when a count exist and it's not a rescheduling workflow */
count: recurringCount && !rescheduleUid ? recurringCount : undefined,
},
};
if (rescheduleUid) {
bookingUrl.query.rescheduleUid = rescheduleUid as string;
}
// If event already has an attendee add booking id
if (slot.bookingUid) {
bookingUrl.query.bookingUid = slot.bookingUid;
}
let slotFull, notEnoughSeats;
if (slot.attendees && seatsPerTimeSlot) slotFull = slot.attendees >= seatsPerTimeSlot;
if (slot.attendees && bookingAttendees && seatsPerTimeSlot) {
notEnoughSeats = slot.attendees + bookingAttendees > seatsPerTimeSlot;
}
const isHalfFull =
slot.attendees && seatsPerTimeSlot && slot.attendees / seatsPerTimeSlot >= 0.5;
const isNearlyFull =
slot.attendees && seatsPerTimeSlot && slot.attendees / seatsPerTimeSlot >= 0.83;
const colorClass = isNearlyFull
? "text-rose-600"
: isHalfFull
? "text-yellow-500"
: "text-emerald-400";
return (
<div data-slot-owner={(slot.userIds || []).join(",")} key={`${dayjs(slot.time).format()}`}>
{/* ^ data-slot-owner is helpful in debugging and used to identify the owners of the slot. Owners are the users which have the timeslot in their schedule. It doesn't consider if a user has that timeslot booked */}
{/* Current there is no way to disable Next.js Links */}
{seatsPerTimeSlot && slot.attendees && (slotFull || notEnoughSeats) ? (
<div
className={classNames(
"text-default bg-default border-subtle mb-2 block rounded-sm border py-2 font-medium opacity-25",
brand === "#fff" || brand === "#ffffff" ? "" : ""
)}
data-testid="time"
data-disabled="true">
{dayjs(slot.time).tz(timeZone()).format(timeFormat)}
{notEnoughSeats ? (
<p className="text-sm">{t("not_enough_seats")}</p>
) : slots ? (
<p className="text-sm">{t("booking_full")}</p>
) : null}
</div>
) : (
<Link
href={bookingUrl}
prefetch={false}
className={classNames(
" bg-default dark:bg-muted border-default hover:bg-subtle hover:border-brand-default text-emphasis mb-2 block rounded-md border py-2 text-sm font-medium",
brand === "#fff" || brand === "#ffffff" ? "" : ""
)}
onClick={() => reserveSlot(slot)}
data-testid="time"
data-disabled="false">
{dayjs(slot.time).tz(timeZone()).format(timeFormat)}
{!!seatsPerTimeSlot && (
<p className={`${colorClass} text-sm`}>
{slot.attendees ? seatsPerTimeSlot - slot.attendees : seatsPerTimeSlot} /{" "}
{seatsPerTimeSlot}{" "}
{t("seats_available", {
count: slot.attendees ? seatsPerTimeSlot - slot.attendees : seatsPerTimeSlot,
})}
</p>
)}
</Link>
)}
</div>
);
})}
{!isLoading && !slots.length && (
<div className="-mt-4 flex h-full w-full flex-col content-center items-center justify-center">
<h1 className="text-emphasis my-6 text-xl">{t("all_booked_today")}</h1>
</div>
)}
{isLoading && !slots.length && (
<>
<SkeletonContainer className="mb-2">
<SkeletonText className="h-5 w-full" />
</SkeletonContainer>
<SkeletonContainer className="mb-2">
<SkeletonText className="h-5 w-full" />
</SkeletonContainer>
<SkeletonContainer className="mb-2">
<SkeletonText className="h-5 w-full" />
</SkeletonContainer>
</>
)}
</div>
</div>
) : null}
</div>
);
};
AvailableTimes.displayName = "AvailableTimes";
export default AvailableTimes;
@@ -1,38 +0,0 @@
import { i18n } from "next-i18next";
import type { TFunction } from "next-i18next";
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
import { CreditCard } from "@calcom/ui/components/icon";
const BookingDescriptionPayment = (props: {
eventType: Parameters<typeof getPaymentAppData>[0];
t: TFunction;
i18n: typeof i18n;
}) => {
const paymentAppData = getPaymentAppData(props.eventType);
if (!paymentAppData || paymentAppData.price <= 0) return null;
const params = {
amount: paymentAppData.price / 100.0,
formatParams: { amount: { currency: paymentAppData.currency } },
};
return (
<p className="text-bookinglight -ml-2 px-2 text-sm ">
<CreditCard className="-mt-1 ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
{paymentAppData.paymentOption === "HOLD" ? (
<>{props.t("no_show_fee_amount", params)}</>
) : (
<>
{/* If undefined this will default to the browser locale */}
{new Intl.NumberFormat(i18n?.language, {
style: "currency",
currency: paymentAppData.currency,
}).format(paymentAppData.price / 100)}
</>
)}
</p>
);
};
export default BookingDescriptionPayment;
+16 -12
View File
@@ -1,4 +1,4 @@
import { useRouter } from "next/router";
import { useRouter } from "next/navigation";
import { useState } from "react";
import type { EventLocationType } from "@calcom/app-store/locations";
@@ -158,7 +158,7 @@ function BookingListItem(booking: BookingItemProps) {
id: "cancel",
label: isTabRecurring && isRecurring ? t("cancel_all_remaining") : t("cancel"),
/* When cancelling we need to let the UI and the API know if the intention is to
cancel all remaining bookings or just that booking instance. */
cancel all remaining bookings or just that booking instance. */
href: `/booking/${booking.uid}?cancel=true${
isTabRecurring && isRecurring ? "&allRemainingBookings=true" : ""
}${booking.seatsReferences.length ? `&seatReferenceUid=${getSeatReferenceUid()}` : ""}
@@ -239,13 +239,18 @@ function BookingListItem(booking: BookingItemProps) {
},
});
const saveLocation = (newLocationType: EventLocationType["type"], details: { [key: string]: string }) => {
const saveLocation = (
newLocationType: EventLocationType["type"],
details: {
[key: string]: string;
}
) => {
let newLocation = newLocationType as string;
const eventLocationType = getEventLocationType(newLocationType);
if (eventLocationType?.organizerInputType) {
newLocation = details[Object.keys(details)[0]];
}
setLocationMutation.mutate({ bookingId: booking.id, newLocation });
setLocationMutation.mutate({ bookingId: booking.id, newLocation, details });
};
// Getting accepted recurring dates to show
@@ -255,13 +260,11 @@ function BookingListItem(booking: BookingItemProps) {
.sort((date1: Date, date2: Date) => date1.getTime() - date2.getTime());
const onClickTableData = () => {
router.push({
pathname: `/booking/${booking.uid}`,
query: {
allRemainingBookings: isTabRecurring,
email: booking.attendees[0] ? booking.attendees[0].email : undefined,
},
const urlSearchParams = new URLSearchParams({
allRemainingBookings: isTabRecurring.toString(),
});
if (booking.attendees[0]) urlSearchParams.set("email", booking.attendees[0].email);
router.push(`/booking/${booking.uid}?${urlSearchParams.toString()}`);
};
const title = booking.title;
@@ -283,6 +286,7 @@ function BookingListItem(booking: BookingItemProps) {
saveLocation={saveLocation}
isOpenDialog={isOpenSetLocationDialog}
setShowLocationModal={setIsOpenLocationDialog}
teamId={booking.eventType?.team?.id}
/>
{booking.paid && booking.payment[0] && (
<ChargeCardDialog
@@ -332,7 +336,7 @@ function BookingListItem(booking: BookingItemProps) {
</DialogContent>
</Dialog>
<tr className="hover:bg-muted group flex flex-col sm:flex-row">
<tr data-testid="booking-item" className="hover:bg-muted group flex flex-col sm:flex-row">
<td
className="hidden align-top ltr:pl-6 rtl:pr-6 sm:table-cell sm:min-w-[12rem]"
onClick={onClickTableData}>
@@ -364,7 +368,7 @@ function BookingListItem(booking: BookingItemProps) {
{t("error_collecting_card")}
</Badge>
) : booking.paid ? (
<Badge className="ltr:mr-2 rtl:ml-2" variant="green">
<Badge className="ltr:mr-2 rtl:ml-2" variant="green" data-testid="paid_badge">
{booking.payment[0].paymentOption === "HOLD" ? t("card_held") : t("paid")}
</Badge>
) : null}
@@ -1,4 +1,4 @@
import { useRouter } from "next/router";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useCallback, useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -26,6 +26,9 @@ type Props = {
};
export default function CancelBooking(props: Props) {
const pathname = usePathname();
const searchParams = useSearchParams();
const asPath = `${pathname}?${searchParams.toString()}`;
const [cancellationReason, setCancellationReason] = useState<string>("");
const { t } = useLocale();
const router = useRouter();
@@ -97,7 +100,7 @@ export default function CancelBooking(props: Props) {
});
if (res.status >= 200 && res.status < 300) {
await router.replace(router.asPath);
router.replace(asPath);
} else {
setLoading(false);
setError(
-198
View File
@@ -1,198 +0,0 @@
import type { EventType } from "@prisma/client";
import dynamic from "next/dynamic";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import type { z } from "zod";
import type { Dayjs } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import DatePicker from "@calcom/features/calendars/DatePicker";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { TimeFormat } from "@calcom/lib/timeFormat";
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc/react";
import useRouterQuery from "@lib/hooks/useRouterQuery";
const AvailableTimes = dynamic(() => import("@components/booking/AvailableTimes"));
const getRefetchInterval = (refetchCount: number): number => {
const intervals = [3000, 3000, 5000, 10000, 20000, 30000] as const;
return intervals[refetchCount] || intervals[intervals.length - 1];
};
const useSlots = ({
eventTypeId,
eventTypeSlug,
startTime,
endTime,
usernameList,
timeZone,
duration,
enabled = true,
}: {
eventTypeId: number;
eventTypeSlug: string;
startTime?: Dayjs;
endTime?: Dayjs;
usernameList: string[];
timeZone?: string;
duration?: string;
enabled?: boolean;
}) => {
const [refetchCount, setRefetchCount] = useState(0);
const refetchInterval = getRefetchInterval(refetchCount);
const { data, isLoading, isPaused, fetchStatus } = trpc.viewer.public.slots.getSchedule.useQuery(
{
eventTypeId,
eventTypeSlug,
usernameList,
startTime: startTime?.toISOString() || "",
endTime: endTime?.toISOString() || "",
timeZone,
duration,
},
{
enabled: !!startTime && !!endTime && enabled,
refetchInterval,
trpc: { context: { skipBatch: true } },
}
);
useEffect(() => {
if (!!data && fetchStatus === "idle") {
setRefetchCount(refetchCount + 1);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fetchStatus, data]);
// The very first time isPaused is set if auto-fetch is disabled, so isPaused should also be considered a loading state.
return { slots: data?.slots || {}, isLoading: isLoading || isPaused };
};
export const SlotPicker = ({
eventType,
timeFormat,
onTimeFormatChange,
timeZone,
recurringEventCount,
users,
seatsPerTimeSlot,
bookingAttendees,
weekStart = 0,
}: {
eventType: Pick<
EventType & { metadata: z.infer<typeof EventTypeMetaDataSchema> },
"id" | "schedulingType" | "slug" | "length" | "metadata"
>;
timeFormat: TimeFormat;
onTimeFormatChange: (is24Hour: boolean) => void;
timeZone?: string;
seatsPerTimeSlot?: number;
bookingAttendees?: number;
recurringEventCount?: number;
users: string[];
weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
}) => {
const [selectedDate, setSelectedDate] = useState<Dayjs>();
const [browsingDate, setBrowsingDate] = useState<Dayjs>();
let { duration = eventType.length.toString() } = useRouterQuery("duration");
const { date, setQuery: setDate } = useRouterQuery("date");
const { month, setQuery: setMonth } = useRouterQuery("month");
const router = useRouter();
if (!eventType.metadata?.multipleDuration) {
duration = eventType.length.toString();
}
useEffect(() => {
if (!router.isReady) return;
// Etc/GMT is not actually a timeZone, so handle this select option explicitly to prevent a hard crash.
if (timeZone === "Etc/GMT") {
setBrowsingDate(dayjs.utc(month).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0));
if (date) {
setSelectedDate(dayjs.utc(date));
}
} else {
// Set the start of the month without shifting time like startOf() may do.
setBrowsingDate(
dayjs.tz(month, timeZone).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0)
);
if (date) {
// It's important to set the date immediately to the timeZone, dayjs(date) will convert to browsertime.
setSelectedDate(dayjs.tz(date, timeZone));
}
}
}, [router.isReady, month, date, duration, timeZone]);
const { i18n, isLocaleReady } = useLocale();
const { slots: monthSlots, isLoading } = useSlots({
eventTypeId: eventType.id,
eventTypeSlug: eventType.slug,
usernameList: users,
startTime: dayjs().startOf("day"),
endTime: browsingDate?.endOf("month"),
timeZone,
duration,
});
const { slots: selectedDateSlots, isLoading: _isLoadingSelectedDateSlots } = useSlots({
eventTypeId: eventType.id,
eventTypeSlug: eventType.slug,
usernameList: users,
startTime: selectedDate?.startOf("day"),
endTime: selectedDate?.endOf("day"),
timeZone,
duration,
/** Prevent refetching is we already have this data from month slots */
enabled: !!selectedDate,
});
/** Hide skeleton if we have the slot loaded in the month query */
const isLoadingSelectedDateSlots = (() => {
if (!selectedDate) return _isLoadingSelectedDateSlots;
if (!!selectedDateSlots[selectedDate.format("YYYY-MM-DD")]) return false;
if (!!monthSlots[selectedDate.format("YYYY-MM-DD")]) return false;
return false;
})();
return (
<>
<DatePicker
isLoading={isLoading}
className={classNames(
"mt-8 px-4 pb-4 sm:mt-0 md:min-w-[300px] md:px-4 lg:min-w-[455px]",
selectedDate ? " border-subtle sm:border-r sm:p-4 sm:pr-6" : "sm:p-4"
)}
includedDates={Object.keys(monthSlots).filter((k) => monthSlots[k].length > 0)}
locale={isLocaleReady ? i18n.language : "en"}
selected={selectedDate}
onChange={(newDate) => {
setDate(newDate.format("YYYY-MM-DD"));
}}
onMonthChange={(newMonth) => {
setMonth(newMonth.format("YYYY-MM"));
}}
browsingDate={browsingDate}
weekStart={weekStart}
/>
<AvailableTimes
isLoading={isLoadingSelectedDateSlots}
slots={
selectedDate &&
(selectedDateSlots[selectedDate.format("YYYY-MM-DD")] ||
monthSlots[selectedDate.format("YYYY-MM-DD")])
}
date={selectedDate}
timeFormat={timeFormat}
onTimeFormatChange={onTimeFormatChange}
eventTypeId={eventType.id}
eventTypeSlug={eventType.slug}
seatsPerTimeSlot={seatsPerTimeSlot}
bookingAttendees={bookingAttendees}
recurringCount={recurringEventCount}
duration={parseInt(duration)}
/>
</>
);
};
@@ -1,41 +0,0 @@
import type { FC } from "react";
import { useEffect, useState } from "react";
import type { ITimezoneOption } from "@calcom/ui";
import { TimezoneSelect } from "@calcom/ui";
import { timeZone } from "../../lib/clock";
type Props = {
onSelectTimeZone: (selectedTimeZone: string) => void;
};
const TimeOptions: FC<Props> = ({ onSelectTimeZone }) => {
const [selectedTimeZone, setSelectedTimeZone] = useState("");
useEffect(() => {
setSelectedTimeZone(timeZone());
}, []);
useEffect(() => {
if (selectedTimeZone && timeZone() && selectedTimeZone !== timeZone()) {
onSelectTimeZone(timeZone(selectedTimeZone));
}
}, [selectedTimeZone, onSelectTimeZone]);
return !!selectedTimeZone ? (
<TimezoneSelect
id="timeZone"
classNames={{
singleValue: () => "text-default",
dropdownIndicator: () => "text-default",
menu: () => "!w-64 max-w-[90vw] shadow-dropdown bg-default border-subtle border rounded-md mt-1",
}}
variant="minimal"
value={selectedTimeZone}
onChange={(tz: ITimezoneOption) => setSelectedTimeZone(tz.value)}
/>
) : null;
};
export default TimeOptions;
@@ -1,26 +0,0 @@
import { Globe } from "@calcom/ui/components/icon";
import { timeZone as localStorageTimeZone } from "@lib/clock";
import TimeOptions from "@components/booking/TimeOptions";
export function TimezoneDropdown({
onChangeTimeZone,
}: {
onChangeTimeZone: (newTimeZone: string) => void;
timeZone?: string;
}) {
const handleSelectTimeZone = (newTimeZone: string) => {
onChangeTimeZone(newTimeZone);
localStorageTimeZone(newTimeZone);
};
return (
<>
<div className="dark:focus-within:bg-darkgray-200 dark:hover:bg-darkgray-200 !mt-3 flex w-full max-w-[20rem] items-center rounded-[4px] px-1 text-sm font-medium focus-within:bg-gray-200 hover:bg-gray-100 lg:max-w-[12rem] [&_p]:focus-within:text-gray-900 dark:[&_p]:focus-within:text-white [&_svg]:focus-within:text-gray-900 dark:[&_svg]:focus-within:text-white">
<Globe className="dark:text-darkgray-600 flex h-4 w-4 text-gray-600 ltr:mr-2 rtl:ml-2" />
<TimeOptions onSelectTimeZone={handleSelectTimeZone} />
</div>
</>
);
}
@@ -1,44 +0,0 @@
import { CAL_URL } from "@calcom/lib/constants";
import type { AvatarGroupProps } from "@calcom/ui";
import { AvatarGroup } from "@calcom/ui";
export const UserAvatars = ({
profile,
users,
...props
}: {
profile: { image: string | null; name?: string | null; username?: string | null };
showMembers: boolean;
users: { username: string | null; name?: string | null }[];
} & Pick<AvatarGroupProps, "size" | "truncateAfter">) => {
const showMembers = !users.find((user) => user.name === profile.name) && props.showMembers;
return (
<AvatarGroup
items={
[
{
image: profile.image,
alt: profile.name,
title: profile.name,
href: profile.username ? `${CAL_URL}/${profile.username}` : undefined,
},
...(showMembers
? users.map((user) => ({
title: user.name,
image: `${CAL_URL}/${user.username}/avatar.png`,
alt: user.name || undefined,
href: user.username ? `${CAL_URL}/${user.username}` : undefined,
}))
: []),
].filter((item) => !!item.image) as {
image: string;
alt?: string;
title?: string;
href?: string;
}[]
}
size="sm"
truncateAfter={props.truncateAfter}
/>
);
};
@@ -104,6 +104,7 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
phone: z.string().optional().nullable(),
locationAddress: z.string().optional(),
credentialId: z.number().optional(),
teamName: z.string().optional(),
locationLink: z
.string()
.optional()
@@ -298,8 +299,19 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
}
if (values.credentialId) {
details = { ...details, credentialId: values.credentialId };
details = {
...details,
credentialId: values.credentialId,
};
}
if (values.teamName) {
details = {
...details,
teamName: values.teamName,
};
}
saveLocation(newLocation, details);
setShowLocationModal(false);
setSelectedLocation?.(undefined);
@@ -344,6 +356,11 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
onChange={(val) => {
if (val) {
locationFormMethods.setValue("locationType", val.value);
if (val.credential) {
locationFormMethods.setValue("credentialId", val.credential.id);
locationFormMethods.setValue("teamName", val.credential.team?.name);
}
locationFormMethods.unregister([
"locationLink",
"locationAddress",
@@ -5,7 +5,7 @@ import { useEffect, useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import short from "short-uuid";
import { v5 as uuidv5 } from "uuid";
import { z } from "zod";
import type { z } from "zod";
import type { EventNameObjectType } from "@calcom/core/event";
import { getEventName } from "@calcom/core/event";
@@ -17,15 +17,25 @@ import {
allowDisablingHostConfirmationEmails,
} from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails";
import { FormBuilder } from "@calcom/features/form-builder/FormBuilder";
import { EditableSchema } from "@calcom/features/form-builder/FormBuilderFieldsSchema"
import type { EditableSchema } from "@calcom/features/form-builder/schema";
import { BookerLayoutSelector } from "@calcom/features/settings/BookerLayoutSelector";
import { classNames } from "@calcom/lib";
import { APP_NAME, CAL_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { Prisma } from "@calcom/prisma/client";
import { trpc } from "@calcom/trpc/react";
import { Button, Checkbox, Label, SettingsToggle, showToast, TextField, Tooltip, Alert } from "@calcom/ui";
import { Edit, Copy } from "@calcom/ui/components/icon";
import {
Alert,
Button,
CheckboxField,
Label,
SettingsToggle,
showToast,
TextField,
Tooltip,
} from "@calcom/ui";
import { Copy, Edit } from "@calcom/ui/components/icon";
import { IS_VISUAL_REGRESSION_TESTING } from "@calcom/web/constants";
import RequiresConfirmationController from "./RequiresConfirmationController";
@@ -87,7 +97,9 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
return {
...field,
hidden: !enabled,
editable: (!enabled ? "system-but-hidden" : "system-but-optional") as z.infer<typeof EditableSchema>
editable: (!enabled ? "system-but-hidden" : "system-but-optional") as z.infer<
typeof EditableSchema
>,
};
}
return field;
@@ -193,6 +205,21 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
onRequiresConfirmation={setRequiresConfirmation}
/>
<hr className="border-subtle" />
<Controller
name="requiresBookerEmailVerification"
control={formMethods.control}
defaultValue={eventType.requiresBookerEmailVerification}
render={({ field: { value, onChange } }) => (
<SettingsToggle
title={t("requires_booker_email_verification")}
{...shouldLockDisableProps("requiresBookerEmailVerification")}
description={t("description_requires_booker_email_verification")}
checked={value}
onCheckedChange={(e) => onChange(e)}
/>
)}
/>
<hr className="border-subtle" />
<Controller
name="hideCalendarNotes"
control={formMethods.control}
@@ -260,36 +287,38 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
}}>
{/* Textfield has some margin by default we remove that so we can keep consitant aligment */}
<div className="lg:-ml-2">
<TextField
disabled
name="hashedLink"
label={t("private_link_label")}
data-testid="generated-hash-url"
labelSrOnly
type="text"
hint={t("private_link_hint")}
defaultValue={placeholderHashedLink}
addOnSuffix={
<Tooltip content={eventType.hashedLink ? t("copy_to_clipboard") : t("enabled_after_update")}>
<Button
color="minimal"
size="sm"
type="button"
className="hover:stroke-3 hover:text-emphasis min-w-fit !py-0 px-0 hover:bg-transparent"
aria-label="copy link"
onClick={() => {
navigator.clipboard.writeText(placeholderHashedLink);
if (eventType.hashedLink) {
showToast(t("private_link_copied"), "success");
} else {
showToast(t("enabled_after_update_description"), "warning");
}
}}>
<Copy className="h-4 w-4" />
</Button>
</Tooltip>
}
/>
{!IS_VISUAL_REGRESSION_TESTING && (
<TextField
disabled
name="hashedLink"
label={t("private_link_label")}
data-testid="generated-hash-url"
labelSrOnly
type="text"
hint={t("private_link_hint")}
defaultValue={placeholderHashedLink}
addOnSuffix={
<Tooltip content={eventType.hashedLink ? t("copy_to_clipboard") : t("enabled_after_update")}>
<Button
color="minimal"
size="sm"
type="button"
className="hover:stroke-3 hover:text-emphasis min-w-fit !py-0 px-0 hover:bg-transparent"
aria-label="copy link"
onClick={() => {
navigator.clipboard.writeText(placeholderHashedLink);
if (eventType.hashedLink) {
showToast(t("private_link_copied"), "success");
} else {
showToast(t("enabled_after_update_description"), "warning");
}
}}>
<Copy className="h-4 w-4" />
</Button>
</Tooltip>
}
/>
)}
</div>
</SettingsToggle>
<hr className="border-subtle" />
@@ -312,6 +341,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
toggleGuests(false);
formMethods.setValue("requiresConfirmation", false);
setRequiresConfirmation(false);
formMethods.setValue("metadata.multipleDuration", undefined);
formMethods.setValue("seatsPerTimeSlot", 2);
} else {
formMethods.setValue("seatsPerTimeSlot", null);
@@ -340,7 +370,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
}}
/>
<div className="mt-2">
<Checkbox
<CheckboxField
description={t("show_attendees")}
disabled={seatsLocked.disabled}
onChange={(e) => formMethods.setValue("seatsShowAttendees", e.target.checked)}
+16 -2
View File
@@ -1,3 +1,5 @@
import { Trans } from "next-i18next";
import Link from "next/link";
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
import { useFormContext } from "react-hook-form";
@@ -158,9 +160,21 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
{!shouldLockDisableProps("apps").disabled && (
<div className="bg-muted rounded-md p-8">
{!isLoading && notInstalledApps?.length ? (
<h2 className="text-emphasis text-lg font-semibold">{t("available_apps")}</h2>
<>
<h2 className="text-emphasis mb-2 text-xl font-semibold leading-5 tracking-[0.01em]">
{t("available_apps_lower_case")}
</h2>
<p className="text-default mb-6 text-sm font-normal">
<Trans i18nKey="available_apps_desc">
You have no apps installed. View popular apps below and explore more in our &nbsp;
<Link className="cursor-pointer underline" href="/apps">
App Store
</Link>
</Trans>
</p>
</>
) : null}
<div className="before:border-0">
<div className="bg-default border-subtle divide-subtle divide-y rounded-md border before:border-0">
{notInstalledApps?.map((app) => (
<EventTypeAppCard
getAppData={getAppDataGetter(app.slug as EventTypeAppsList)}
@@ -1,5 +1,5 @@
import type { EventTypeSetup, FormValues } from "pages/event-types/[type]";
import { useState } from "react";
import { useState, memo } from "react";
import { Controller, useFormContext } from "react-hook-form";
import type { OptionProps, SingleValueProps } from "react-select";
import { components } from "react-select";
@@ -71,86 +71,91 @@ const format = (date: Date, hour12: boolean) =>
hourCycle: hour12 ? "h12" : "h24",
}).format(new Date(dayjs.utc(date).format("YYYY-MM-DDTHH:mm:ss")));
const EventTypeScheduleDetails = ({
isManagedEventType,
selectedScheduleValue,
}: {
isManagedEventType: boolean;
selectedScheduleValue: AvailabilityOption | undefined;
}) => {
const { data: loggedInUser } = useMeQuery();
const timeFormat = loggedInUser?.timeFormat;
const { t, i18n } = useLocale();
const { watch } = useFormContext<FormValues>();
const EventTypeScheduleDetails = memo(
({
isManagedEventType,
selectedScheduleValue,
}: {
isManagedEventType: boolean;
selectedScheduleValue: AvailabilityOption | undefined;
}) => {
const { data: loggedInUser } = useMeQuery();
const timeFormat = loggedInUser?.timeFormat;
const { t, i18n } = useLocale();
const { watch } = useFormContext<FormValues>();
const scheduleId = watch("schedule");
const { isLoading, data: schedule } = trpc.viewer.availability.schedule.get.useQuery(
{
scheduleId: scheduleId || loggedInUser?.defaultScheduleId || selectedScheduleValue?.value || undefined,
isManagedEventType,
},
{ enabled: !!scheduleId || !!loggedInUser?.defaultScheduleId || !!selectedScheduleValue }
);
const scheduleId = watch("schedule");
const { isLoading, data: schedule } = trpc.viewer.availability.schedule.get.useQuery(
{
scheduleId:
scheduleId || loggedInUser?.defaultScheduleId || selectedScheduleValue?.value || undefined,
isManagedEventType,
},
{ enabled: !!scheduleId || !!loggedInUser?.defaultScheduleId || !!selectedScheduleValue }
);
const filterDays = (dayNum: number) =>
schedule?.schedule.filter((item) => item.days.includes((dayNum + 1) % 7)) || [];
const filterDays = (dayNum: number) =>
schedule?.schedule.filter((item) => item.days.includes((dayNum + 1) % 7)) || [];
return (
<div className="border-default space-y-4 rounded border px-6 pb-4">
<ol className="table border-collapse text-sm">
{weekdayNames(i18n.language, 1, "long").map((day, index) => {
const isAvailable = !!filterDays(index).length;
return (
<li key={day} className="my-6 flex border-transparent last:mb-2">
<span
className={classNames(
"w-20 font-medium sm:w-32 ",
!isAvailable ? "text-subtle line-through" : "text-default"
)}>
{day}
</span>
{isLoading ? (
<SkeletonText className="block h-5 w-60" />
) : isAvailable ? (
<div className="space-y-3 text-right">
{filterDays(index).map((dayRange, i) => (
<div key={i} className="text-default flex items-center leading-4">
<span className="w-16 sm:w-28 sm:text-left">
{format(dayRange.startTime, timeFormat === 12)}
</span>
<span className="ms-4">-</span>
<div className="ml-6 sm:w-28">{format(dayRange.endTime, timeFormat === 12)}</div>
</div>
))}
</div>
) : (
<span className="text-subtle ml-6 sm:ml-0">{t("unavailable")}</span>
)}
</li>
);
})}
</ol>
<hr className="border-subtle" />
<div className="flex flex-col justify-center gap-2 sm:flex-row sm:justify-between">
<span className="text-default flex items-center justify-center text-sm sm:justify-start">
<Globe className="h-3.5 w-3.5 ltr:mr-2 rtl:ml-2" />
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
</span>
{!!schedule?.id && !schedule.isManaged && (
<Button
href={`/availability/${schedule.id}`}
disabled={isLoading}
color="minimal"
EndIcon={ExternalLink}
target="_blank"
rel="noopener noreferrer">
{t("edit_availability")}
</Button>
)}
return (
<div className="border-default space-y-4 rounded border px-6 pb-4">
<ol className="table border-collapse text-sm">
{weekdayNames(i18n.language, 1, "long").map((day, index) => {
const isAvailable = !!filterDays(index).length;
return (
<li key={day} className="my-6 flex border-transparent last:mb-2">
<span
className={classNames(
"w-20 font-medium sm:w-32 ",
!isAvailable ? "text-subtle line-through" : "text-default"
)}>
{day}
</span>
{isLoading ? (
<SkeletonText className="block h-5 w-60" />
) : isAvailable ? (
<div className="space-y-3 text-right">
{filterDays(index).map((dayRange, i) => (
<div key={i} className="text-default flex items-center leading-4">
<span className="w-16 sm:w-28 sm:text-left">
{format(dayRange.startTime, timeFormat === 12)}
</span>
<span className="ms-4">-</span>
<div className="ml-6 sm:w-28">{format(dayRange.endTime, timeFormat === 12)}</div>
</div>
))}
</div>
) : (
<span className="text-subtle ml-6 sm:ml-0">{t("unavailable")}</span>
)}
</li>
);
})}
</ol>
<hr className="border-subtle" />
<div className="flex flex-col justify-center gap-2 sm:flex-row sm:justify-between">
<span className="text-default flex items-center justify-center text-sm sm:justify-start">
<Globe className="h-3.5 w-3.5 ltr:mr-2 rtl:ml-2" />
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
</span>
{!!schedule?.id && !schedule.isManaged && !schedule.readOnly && (
<Button
href={`/availability/${schedule.id}`}
disabled={isLoading}
color="minimal"
EndIcon={ExternalLink}
target="_blank"
rel="noopener noreferrer">
{t("edit_availability")}
</Button>
)}
</div>
</div>
</div>
);
};
);
}
);
EventTypeScheduleDetails.displayName = "EventTypeScheduleDetails";
const EventTypeSchedule = ({ eventType }: { eventType: EventTypeSetup }) => {
const { t } = useLocale();
@@ -197,6 +202,15 @@ const EventTypeSchedule = ({ eventType }: { eventType: EventTypeSetup }) => {
isManaged: false,
});
}
// We push the selected schedule from the event type if it's not part of the list response. This happens if the user is an admin but not the schedule owner.
else if (eventType.schedule && !schedules.find((schedule) => schedule.id === eventType.schedule)) {
options.push({
value: eventType.schedule,
label: eventType.scheduleName ?? t("default_schedule_name"),
isDefault: false,
isManaged: false,
});
}
setOptions(options);
@@ -13,6 +13,7 @@ import type { DurationType } from "@calcom/lib/convertToNewDurationType";
import convertToNewDurationType from "@calcom/lib/convertToNewDurationType";
import findDurationType from "@calcom/lib/findDurationType";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { ascendingLimitKeys, intervalLimitKeyToUnit } from "@calcom/lib/intervalLimit";
import type { PeriodType } from "@calcom/prisma/enums";
import type { IntervalLimit } from "@calcom/types/Calendar";
import { Button, DateRangePicker, InputField, Label, Select, SettingsToggle, TextField } from "@calcom/ui";
@@ -462,7 +463,6 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
type="number"
{...offsetStartLockedProps}
label={t("offset_start")}
defaultValue={eventType.offsetStart}
{...formMethods.register("offsetStart")}
addOnSuffix={<>{t("minutes")}</>}
hint={t("offset_start_description", {
@@ -477,11 +477,9 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
type IntervalLimitsKey = keyof IntervalLimit;
const intervalOrderKeys = ["PER_DAY", "PER_WEEK", "PER_MONTH", "PER_YEAR"] as const;
const INTERVAL_LIMIT_OPTIONS = intervalOrderKeys.map((key) => ({
const INTERVAL_LIMIT_OPTIONS = ascendingLimitKeys.map((key) => ({
value: key as keyof IntervalLimit,
label: `Per ${key.split("_")[1].toLocaleLowerCase()}`,
label: `Per ${intervalLimitKeyToUnit(key)}`,
}));
type IntervalLimitItemProps = {
@@ -590,8 +588,8 @@ const IntervalLimitsManager = <K extends "durationLimits" | "bookingLimits">({
Object.entries(currentIntervalLimits)
.sort(([limitKeyA], [limitKeyB]) => {
return (
intervalOrderKeys.indexOf(limitKeyA as IntervalLimitsKey) -
intervalOrderKeys.indexOf(limitKeyB as IntervalLimitsKey)
ascendingLimitKeys.indexOf(limitKeyA as IntervalLimitsKey) -
ascendingLimitKeys.indexOf(limitKeyB as IntervalLimitsKey)
);
})
.map(([key, value]) => {
@@ -12,6 +12,7 @@ import { z } from "zod";
import type { EventLocationType } from "@calcom/app-store/locations";
import { getEventLocationType, MeetLocationType, LocationType } from "@calcom/app-store/locations";
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
import cx from "@calcom/lib/classNames";
import { CAL_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -116,6 +117,8 @@ export const EventSetupTab = (
const [editingLocationType, setEditingLocationType] = useState<string>("");
const [selectedLocation, setSelectedLocation] = useState<LocationOption | undefined>(undefined);
const [multipleDuration, setMultipleDuration] = useState(eventType.metadata?.multipleDuration);
const orgBranding = useOrgBranding();
const seatsEnabled = formMethods.watch("seatsPerTimeSlotEnabled");
const locationOptions = props.locationOptions.map((locationOption) => {
const options = locationOption.options.filter((option) => {
@@ -129,10 +132,12 @@ export const EventSetupTab = (
};
});
const multipleDurationOptions = [5, 10, 15, 20, 25, 30, 45, 50, 60, 75, 80, 90, 120, 180].map((mins) => ({
value: mins,
label: t("multiple_duration_mins", { count: mins }),
}));
const multipleDurationOptions = [5, 10, 15, 20, 25, 30, 45, 50, 60, 75, 80, 90, 120, 150, 180].map(
(mins) => ({
value: mins,
label: t("multiple_duration_mins", { count: mins }),
})
);
const [selectedMultipleDuration, setSelectedMultipleDuration] = useState<
MultiValue<{
@@ -306,7 +311,9 @@ export const EventSetupTab = (
)}
alt={`${eventLocationType.label} logo`}
/>
<span className="ms-1 line-clamp-1 text-sm">{eventLabel}</span>
<span className="ms-1 line-clamp-1 text-sm">{`${eventLabel} ${
location.teamName ? `(${location.teamName})` : ""
}`}</span>
</div>
<div className="flex">
<button
@@ -381,6 +388,9 @@ export const EventSetupTab = (
const lengthLockedProps = shouldLockDisableProps("length");
const descriptionLockedProps = shouldLockDisableProps("description");
const urlPrefix = orgBranding
? orgBranding?.fullDomain.replace(/^(https?:|)\/\//, "")
: `${CAL_URL?.replace(/^(https?:|)\/\//, "")}`;
return (
<div>
@@ -409,10 +419,10 @@ export const EventSetupTab = (
defaultValue={eventType.slug}
addOnLeading={
<>
{CAL_URL?.replace(/^(https?:|)\/\//, "")}/
{urlPrefix}/
{!isManagedEventType
? team
? "team/" + team.slug
? (orgBranding ? "" : "team/") + team.slug
: eventType.users[0].username
: t("username_placeholder")}
/
@@ -499,6 +509,8 @@ export const EventSetupTab = (
<SettingsToggle
title={t("allow_booker_to_select_duration")}
checked={multipleDuration !== undefined}
disabled={seatsEnabled}
tooltip={seatsEnabled ? t("seat_options_doesnt_multiple_durations") : undefined}
onCheckedChange={() => {
if (multipleDuration !== undefined) {
setMultipleDuration(undefined);
+31 -7
View File
@@ -1,3 +1,5 @@
import { Trans } from "next-i18next";
import Link from "next/link";
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
import { useEffect, useRef } from "react";
import type { ComponentProps } from "react";
@@ -19,16 +21,17 @@ interface IUserToValue {
email: string;
}
const mapUserToValue = ({ id, name, username, email }: IUserToValue) => ({
const mapUserToValue = ({ id, name, username, email }: IUserToValue, pendingString: string) => ({
value: `${id || ""}`,
label: `${name || ""}`,
label: `${name || email || ""}${!username ? ` (${pendingString})` : ""}`,
avatar: `${WEBAPP_URL}/${username}/avatar.png`,
email,
});
export const mapMemberToChildrenOption = (
member: EventTypeSetupProps["teamMembers"][number],
slug: string
slug: string,
pendingString: string
) => {
return {
slug,
@@ -43,7 +46,7 @@ export const mapMemberToChildrenOption = (
eventTypeSlugs: member.eventTypes ?? [],
},
value: `${member.id ?? ""}`,
label: member.name ?? "",
label: `${member.name || member.email || ""}${!member.username ? ` (${pendingString})` : ""}`,
};
};
@@ -98,6 +101,7 @@ const CheckedHostField = ({
isFixed,
value,
onChange,
helperText,
...rest
}: {
labelText: string;
@@ -106,6 +110,7 @@ const CheckedHostField = ({
value: { isFixed: boolean; userId: number }[];
onChange?: (options: { isFixed: boolean; userId: number }[]) => void;
options?: Options<CheckedSelectOption>;
helperText?: React.ReactNode | string;
} & Omit<Partial<ComponentProps<typeof CheckedTeamSelect>>, "onChange" | "value">) => {
return (
<div className="bg-muted flex flex-col space-y-5 p-4">
@@ -135,11 +140,24 @@ const CheckedHostField = ({
placeholder={placeholder}
{...rest}
/>
{helperText && <p className="text-subtle text-sm">{helperText}</p>}
</div>
</div>
);
};
const FixedHostHelper = (
<Trans i18nKey="fixed_host_helper">
Add anyone who needs to attend the event.
<Link
className="underline underline-offset-2"
target="_blank"
href="https://cal.com/docs/enterprise-features/teams/round-robin-scheduling#fixed-hosts">
Learn more
</Link>
</Trans>
);
const RoundRobinHosts = ({
teamMembers,
value,
@@ -166,6 +184,7 @@ const RoundRobinHosts = ({
value={value}
placeholder={t("add_fixed_hosts")}
labelText={t("fixed_hosts")}
helperText={FixedHostHelper}
/>
<CheckedHostField
options={teamMembers.sort(sortByLabel)}
@@ -174,6 +193,7 @@ const RoundRobinHosts = ({
isFixed={false}
placeholder={t("add_attendees")}
labelText={t("round_robin_hosts")}
helperText={t("round_robin_helper")}
/>
</>
);
@@ -291,9 +311,13 @@ export const EventTeamTab = ({
// description: t("round_robin_description"),
},
];
const teamMembersOptions = teamMembers.map(mapUserToValue);
const childrenEventTypeOptions = teamMembers.map((member) => {
return mapMemberToChildrenOption(member, eventType.slug);
const pendingMembers = (member: (typeof teamMembers)[number]) =>
!!eventType.team?.parentId || !!member.username;
const teamMembersOptions = teamMembers
.filter(pendingMembers)
.map((member) => mapUserToValue(member, t("pending")));
const childrenEventTypeOptions = teamMembers.filter(pendingMembers).map((member) => {
return mapMemberToChildrenOption(member, eventType.slug, t("pending"));
});
const isManagedEventType = eventType.schedulingType === SchedulingType.MANAGED;
return (
@@ -1,13 +1,14 @@
import { Webhook as TbWebhook } from "lucide-react";
import type { TFunction } from "next-i18next";
import { Trans } from "next-i18next";
import { useRouter } from "next/router";
import { useRouter } from "next/navigation";
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
import { useMemo, useState, Suspense } from "react";
import type { UseFormReturn } from "react-hook-form";
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks";
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
import { EventTypeEmbedButton, EventTypeEmbedDialog } from "@calcom/features/embed/EventTypeEmbed";
import Shell from "@calcom/features/shell/Shell";
import { classNames } from "@calcom/lib";
import { CAL_URL } from "@calcom/lib/constants";
@@ -51,7 +52,6 @@ import {
Loader,
} from "@calcom/ui/components/icon";
import { EmbedButton, EmbedDialog } from "@components/Embed";
import type { AvailabilityOption } from "@components/eventtype/EventAvailabilityTab";
type Props = {
@@ -148,7 +148,7 @@ function EventTypeSingleLayout({
onSuccess: async () => {
await utils.viewer.eventTypes.invalidate();
showToast(t("event_type_deleted_successfully"), "success");
await router.push("/event-types");
router.push("/event-types");
setDeleteDialogOpen(false);
},
onError: (err) => {
@@ -209,10 +209,8 @@ function EventTypeSingleLayout({
}`,
});
}
if (isManagedEventType || isChildrenManagedEventType) {
// Removing apps and workflows for manageg event types by admins v1
navigation.splice(0, 1);
} else {
const showWebhooks = !(isManagedEventType || isChildrenManagedEventType);
if (showWebhooks) {
navigation.push({
name: "webhooks",
href: `/event-types/${eventType.id}?tabName=webhooks`,
@@ -234,7 +232,7 @@ function EventTypeSingleLayout({
formMethods,
]);
const orgBranding = useOrgBrandingValues();
const orgBranding = useOrgBranding();
const isOrgEvent = orgBranding?.fullDomain;
const permalink = `${orgBranding?.fullDomain ?? CAL_URL}/${
team ? `${!isOrgEvent ? "team/" : ""}${team.slug}` : eventType.users[0].username
@@ -312,12 +310,13 @@ function EventTypeSingleLayout({
showToast("Link copied!", "success");
}}
/>
<EmbedButton
<EventTypeEmbedButton
embedUrl={encodeURIComponent(embedLink)}
StartIcon={Code}
color="secondary"
variant="icon"
tooltip={t("embed")}
eventId={eventType.id}
/>
</>
)}
@@ -407,13 +406,13 @@ function EventTypeSingleLayout({
className="primary-navigation"
tabs={EventTypeTabs}
sticky
linkProps={{ shallow: true }}
linkShallow
itemClassname="items-start"
iconClassName="md:mt-px"
/>
</div>
<div className="p-2 md:mx-0 md:p-0 xl:hidden">
<HorizontalTabs tabs={EventTypeTabs} linkProps={{ shallow: true }} />
<HorizontalTabs tabs={EventTypeTabs} linkShallow />
</div>
<div className="w-full ltr:mr-2 rtl:ml-2">
<div
@@ -451,7 +450,7 @@ function EventTypeSingleLayout({
</p>
</ConfirmationDialogContent>
</Dialog>
<EmbedDialog />
<EventTypeEmbedDialog />
</Shell>
);
}
@@ -1,4 +1,3 @@
import { useRouter } from "next/router";
import { useForm } from "react-hook-form";
import { Schedule } from "@calcom/features/schedules";
@@ -21,12 +20,11 @@ const SetupAvailability = (props: ISetupAvailabilityProps) => {
const { t } = useLocale();
const { nextStep } = props;
const router = useRouter();
const scheduleId = defaultScheduleId === null ? undefined : defaultScheduleId;
const queryAvailability = trpc.viewer.availability.schedule.get.useQuery(
{ scheduleId: defaultScheduleId! },
{ scheduleId: defaultScheduleId ?? undefined },
{
enabled: router.isReady && !!defaultScheduleId,
enabled: !!scheduleId,
}
);
@@ -1,4 +1,4 @@
import { useRouter } from "next/router";
import { useRouter } from "next/navigation";
import type { FormEvent } from "react";
import { useRef, useState } from "react";
import { useForm } from "react-hook-form";
@@ -15,6 +15,7 @@ import { UsernameAvailabilityField } from "@components/ui/UsernameAvailability";
interface IUserSettingsProps {
nextStep: () => void;
hideUsername?: boolean;
}
const UserSettings = (props: IUserSettingsProps) => {
@@ -66,8 +67,8 @@ const UserSettings = (props: IUserSettingsProps) => {
return (
<form onSubmit={onSubmit}>
<div className="space-y-6">
{/* Username textfield */}
<UsernameAvailabilityField />
{/* Username textfield: when not coming from signup */}
{!props.hideUsername && <UsernameAvailabilityField />}
{/* Full name textfield */}
<div className="w-full">
@@ -12,9 +12,6 @@ const DisableUserImpersonation = ({ disableImpersonation }: { disableImpersonati
showToast(t("your_user_profile_updated_successfully"), "success");
await utils.viewer.me.invalidate();
},
async onSettled() {
await utils.viewer.public.i18n.invalidate();
},
});
return (
@@ -12,7 +12,7 @@ import TwoFactorAuthAPI from "./TwoFactorAuthAPI";
interface DisableTwoFactorAuthModalProps {
open: boolean;
onOpenChange: () => void;
disablePassword?: boolean;
/** Called when the user closes the modal without disabling two-factor auth */
onCancel: () => void;
/** Called when the user disables two-factor auth */
@@ -27,6 +27,7 @@ interface DisableTwoFactorValues {
const DisableTwoFactorAuthModal = ({
onDisable,
onCancel,
disablePassword,
open,
onOpenChange,
}: DisableTwoFactorAuthModalProps) => {
@@ -75,23 +76,30 @@ const DisableTwoFactorAuthModal = ({
<DialogContent title={t("disable_2fa")} description={t("disable_2fa_recommendation")} type="creation">
<Form form={form} handleSubmit={handleDisable}>
<div className="mb-8">
<PasswordField
labelProps={{
className: "block text-sm font-medium text-default",
}}
{...form.register("password")}
className="border-default mt-1 block w-full rounded-md border px-3 py-2 text-sm focus:border-black focus:outline-none focus:ring-black"
/>
{!disablePassword && (
<PasswordField
labelProps={{
className: "block text-sm font-medium text-default",
}}
{...form.register("password")}
className="border-default mt-1 block w-full rounded-md border px-3 py-2 text-sm focus:border-black focus:outline-none focus:ring-black"
/>
)}
<TwoFactor center={false} />
{errorMessage && <p className="mt-1 text-sm text-red-700">{errorMessage}</p>}
</div>
<DialogFooter showDivider className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<DialogFooter showDivider className="relative mt-5">
<Button color="secondary" onClick={onCancel}>
{t("cancel")}
</Button>
<Button type="submit" className="me-2 ms-2" data-testid="disable-2fa" disabled={isDisabling}>
<Button
type="submit"
className="me-2 ms-2"
data-testid="disable-2fa"
loading={isDisabling}
disabled={isDisabling}>
{t("disable")}
</Button>
</DialogFooter>
@@ -185,7 +185,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
)}
</div>
</WithStep>
<DialogFooter className="mt-8 sm:flex sm:flex-row-reverse" showDivider>
<DialogFooter className="mt-8" showDivider>
<Button color="secondary" onClick={onCancel}>
{t("cancel")}
</Button>
@@ -194,6 +194,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
type="submit"
className="me-2 ms-2"
onClick={handleSetup}
loading={isSubmitting}
disabled={password.length === 0 || isSubmitting}>
{t("continue")}
</Button>
@@ -208,7 +209,12 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
</Button>
</WithStep>
<WithStep step={SetupStep.EnterTotpCode} current={step}>
<Button type="submit" className="me-2 ms-2" data-testid="enable-2fa" disabled={isSubmitting}>
<Button
type="submit"
className="me-2 ms-2"
data-testid="enable-2fa"
loading={isSubmitting}
disabled={isSubmitting}>
{t("enable")}
</Button>
</WithStep>
@@ -1,4 +1,5 @@
import { zodResolver } from "@hookform/resolvers/zod";
// eslint-disable-next-line no-restricted-imports
import { noop } from "lodash";
import { useCallback, useState } from "react";
import { Controller, FormProvider, useForm, useFormState } from "react-hook-form";
+1 -1
View File
@@ -1,4 +1,4 @@
import { useRouter } from "next/router";
import { useRouter } from "next/navigation";
import type { Dispatch, SetStateAction } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
+4 -5
View File
@@ -1,25 +1,24 @@
import Link from "next/link";
import { useRouter } from "next/router";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
import { md } from "@calcom/lib/markdownIt";
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import type { TeamWithMembers } from "@calcom/lib/server/queries/teams";
import { Avatar } from "@calcom/ui";
type TeamType = NonNullable<TeamWithMembers>;
type TeamType = Omit<NonNullable<TeamWithMembers>, "inviteToken">;
type MembersType = TeamType["members"];
type MemberType = MembersType[number] & { safeBio: string | null };
type TeamTypeWithSafeHtml = Omit<TeamType, "members" | "inviteToken"> & { members: MemberType[] };
const Member = ({ member, teamName }: { member: MemberType; teamName: string | null }) => {
const routerQuery = useRouterQuery();
const { t } = useLocale();
const router = useRouter();
const isBioEmpty = !member.bio || !member.bio.replace("<p><br></p>", "").length;
// We don't want to forward orgSlug and user which are route params to the next route
const { slug: _slug, orgSlug: _orgSlug, user: _user, ...queryParamsToForward } = router.query;
const { slug: _slug, orgSlug: _orgSlug, user: _user, ...queryParamsToForward } = routerQuery;
return (
<Link key={member.id} href={{ pathname: `/${member.username}`, query: queryParamsToForward }}>
@@ -1,7 +1,8 @@
import classNames from "classnames";
// eslint-disable-next-line no-restricted-imports
import { debounce, noop } from "lodash";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import type { RefCallback } from "react";
import { useEffect, useMemo, useState } from "react";
@@ -14,7 +15,7 @@ import type { TRPCClientErrorLike } from "@calcom/trpc/client";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import { Button, Dialog, DialogClose, DialogContent, Input, Label } from "@calcom/ui";
import { Button, Dialog, DialogClose, DialogContent, DialogFooter, Input, Label } from "@calcom/ui";
import { Check, Edit2, ExternalLink, Star as StarSolid } from "@calcom/ui/components/icon";
export enum UsernameChangeStatusEnum {
@@ -46,8 +47,11 @@ const obtainNewUsernameChangeCondition = ({
};
const PremiumTextfield = (props: ICustomUsernameProps) => {
const searchParams = useSearchParams();
const pathname = usePathname();
const router = useRouter();
const { t } = useLocale();
const { data: session, update } = useSession();
const { update } = useSession();
const {
currentUsername,
setCurrentUsername = noop,
@@ -61,8 +65,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
const [user] = trpc.viewer.me.useSuspenseQuery();
const [usernameIsAvailable, setUsernameIsAvailable] = useState(false);
const [markAsError, setMarkAsError] = useState(false);
const router = useRouter();
const { paymentStatus: recentAttemptPaymentStatus } = router.query;
const recentAttemptPaymentStatus = searchParams?.get("recentAttemptPaymentStatus");
const [openDialogSaveUsername, setOpenDialogSaveUsername] = useState(false);
const { data: stripeCustomer } = trpc.viewer.stripeCustomer.useQuery();
const isCurrentUsernamePremium =
@@ -92,19 +95,15 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
debouncedApiCall(inputUsernameValue);
}, [debouncedApiCall, inputUsernameValue]);
const utils = trpc.useContext();
const updateUsername = trpc.viewer.updateProfile.useMutation({
onSuccess: async () => {
onSuccessMutation && (await onSuccessMutation());
await update();
await update({ username: inputUsernameValue });
setOpenDialogSaveUsername(false);
},
onError: (error) => {
onErrorMutation && onErrorMutation(error);
},
async onSettled() {
await utils.viewer.public.i18n.invalidate();
},
});
// when current username isn't set - Go to stripe to check what username he wanted to buy and was it a premium and was it paid for
@@ -120,7 +119,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
const paymentLink = `/api/integrations/stripepayment/subscription?intentUsername=${
inputUsernameValue || usernameFromStripe
}&action=${usernameChangeCondition}&callbackUrl=${WEBAPP_URL}${router.asPath}`;
}&action=${usernameChangeCondition}&callbackUrl=${WEBAPP_URL}${pathname}`;
const ActionButtons = () => {
if (paymentRequired) {
@@ -223,7 +222,11 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
onChange={(event) => {
event.preventDefault();
// Reset payment status
delete router.query.paymentStatus;
const _searchParams = new URLSearchParams(searchParams);
_searchParams.delete("paymentStatus");
if (searchParams.toString() !== _searchParams.toString()) {
router.replace(`${pathname}?${_searchParams.toString()}`);
}
setInputUsernameValue(event.target.value);
}}
data-testid="username-input"
@@ -284,7 +287,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
</div>
</div>
<div className="mt-4 flex flex-row-reverse gap-x-2">
<DialogFooter className="mt-4">
{/* redirect to checkout */}
{usernameChangeCondition === UsernameChangeStatusEnum.UPGRADE && (
<Button
@@ -312,7 +315,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
<DialogClose color="secondary" onClick={() => setOpenDialogSaveUsername(false)}>
{t("cancel")}
</DialogClose>
</div>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
@@ -1,4 +1,5 @@
import classNames from "classnames";
// eslint-disable-next-line no-restricted-imports
import { debounce, noop } from "lodash";
import { useSession } from "next-auth/react";
import type { RefCallback } from "react";
@@ -9,7 +10,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
import { trpc } from "@calcom/trpc/react";
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import { Button, Dialog, DialogClose, DialogContent, TextField } from "@calcom/ui";
import { Button, Dialog, DialogClose, DialogContent, TextField, DialogFooter } from "@calcom/ui";
import { Check, Edit2 } from "@calcom/ui/components/icon";
interface ICustomUsernameProps {
@@ -24,7 +25,8 @@ interface ICustomUsernameProps {
const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.ComponentProps<typeof TextField>>) => {
const { t } = useLocale();
const { data: session, update } = useSession();
const { update } = useSession();
const {
currentUsername,
setCurrentUsername = noop,
@@ -64,21 +66,16 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.Component
}
}, [inputUsernameValue, debouncedApiCall, currentUsername]);
const utils = trpc.useContext();
const updateUsernameMutation = trpc.viewer.updateProfile.useMutation({
onSuccess: async () => {
onSuccessMutation && (await onSuccessMutation());
setOpenDialogSaveUsername(false);
setCurrentUsername(inputUsernameValue);
await update();
await update({ username: inputUsernameValue });
},
onError: (error) => {
onErrorMutation && onErrorMutation(error);
},
async onSettled() {
await utils.viewer.public.i18n.invalidate();
},
});
const ActionButtons = () => {
@@ -175,7 +172,8 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.Component
</div>
</div>
</div>
<div className="ml-4 mt-4 flex flex-row-reverse gap-x-2">
<DialogFooter className="mt-4">
<Button
type="button"
loading={updateUsernameMutation.isLoading}
@@ -187,7 +185,7 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.Component
<DialogClose color="secondary" onClick={() => setOpenDialogSaveUsername(false)}>
{t("cancel")}
</DialogClose>
</div>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
@@ -1,46 +1,37 @@
import { useRouter } from "next/router";
import dynamic from "next/dynamic";
import { useSearchParams } from "next/navigation";
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { getOrgFullDomain } from "@calcom/features/ee/organizations/lib/orgDomains";
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
import { CAL_URL, IS_SELF_HOSTED } from "@calcom/lib/constants";
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import useRouterQuery from "@lib/hooks/useRouterQuery";
import { PremiumTextfield } from "./PremiumTextfield";
import { UsernameTextfield } from "./UsernameTextfield";
export const UsernameAvailability = IS_SELF_HOSTED ? UsernameTextfield : PremiumTextfield;
interface UsernameAvailabilityFieldProps {
onSuccessMutation?: () => void;
onErrorMutation?: (error: TRPCClientErrorLike<AppRouter>) => void;
}
function useUserNamePrefix(organization: RouterOutputs["viewer"]["me"]["organization"]): string {
return organization
? organization.slug
? getOrgFullDomain(organization.slug, { protocol: false })
: organization.metadata && organization.metadata.requestedSlug
? getOrgFullDomain(organization.metadata.requestedSlug, { protocol: false })
: process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")
: process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "");
}
export const getUsernameAvailabilityComponent = (isPremium: boolean) => {
if (isPremium)
return dynamic(() => import("./PremiumTextfield").then((m) => m.PremiumTextfield), { ssr: false });
return dynamic(() => import("./UsernameTextfield").then((m) => m.UsernameTextfield), { ssr: false });
};
export const UsernameAvailabilityField = ({
onSuccessMutation,
onErrorMutation,
}: UsernameAvailabilityFieldProps) => {
const router = useRouter();
const searchParams = useSearchParams();
const [user] = trpc.viewer.me.useSuspenseQuery();
const [currentUsernameState, setCurrentUsernameState] = useState(user.username || "");
const { username: usernameFromQuery, setQuery: setUsernameFromQuery } = useRouterQuery("username");
const { username: currentUsername, setQuery: setCurrentUsername } =
router.query["username"] && user.username === null
searchParams?.get("username") && user.username === null
? { username: usernameFromQuery, setQuery: setUsernameFromQuery }
: { username: currentUsernameState || "", setQuery: setCurrentUsernameState };
const formMethods = useForm({
@@ -49,7 +40,12 @@ export const UsernameAvailabilityField = ({
},
});
const usernamePrefix = useUserNamePrefix(user.organization);
const UsernameAvailability = getUsernameAvailabilityComponent(!IS_SELF_HOSTED && !user.organization?.id);
const orgBranding = useOrgBranding();
const usernamePrefix = orgBranding
? orgBranding?.fullDomain.replace(/^(https?:|)\/\//, "")
: `${CAL_URL?.replace(/^(https?:|)\/\//, "")}`;
return (
<Controller
@@ -65,7 +61,8 @@ export const UsernameAvailabilityField = ({
setInputUsernameValue={onChange}
onSuccessMutation={onSuccessMutation}
onErrorMutation={onErrorMutation}
addOnLeading={usernamePrefix}
disabled={!!user.organization?.id}
addOnLeading={`${usernamePrefix}/`}
/>
);
}}
@@ -2,6 +2,7 @@ import type { GroupBase, Props, SingleValue } from "react-select";
import { components } from "react-select";
import type { EventLocationType } from "@calcom/app-store/locations";
import type { CredentialDataWithTeamName } from "@calcom/app-store/utils";
import { classNames } from "@calcom/lib";
import cx from "@calcom/lib/classNames";
import { Select } from "@calcom/ui";
@@ -12,6 +13,7 @@ export type LocationOption = {
icon?: string;
disabled?: boolean;
address?: string;
credential?: CredentialDataWithTeamName;
};
export type SingleValueLocationOption = SingleValue<LocationOption>;
+2 -2
View File
@@ -3,7 +3,7 @@ import type { GroupBase, Props, InputProps, SingleValue, MultiValue } from "reac
import ReactSelect, { components } from "react-select";
import classNames from "@calcom/lib/classNames";
import useTheme from "@calcom/lib/hooks/useTheme";
import { useGetTheme } from "@calcom/lib/hooks/useTheme";
export type SelectProps<
Option,
@@ -30,7 +30,7 @@ function Select<
Group extends GroupBase<Option> = GroupBase<Option>
>({ className, ...props }: SelectProps<Option, IsMulti, Group>) {
const [mounted, setMounted] = useState<boolean>(false);
const { resolvedTheme, forcedTheme } = useTheme();
const { resolvedTheme, forcedTheme } = useGetTheme();
const hasDarkTheme = !forcedTheme && resolvedTheme === "dark";
const darkThemeColors = {
/** Dark Theme starts */
+1
View File
@@ -0,0 +1 @@
export const IS_VISUAL_REGRESSION_TESTING = Boolean(globalThis.window?.Meticulous?.isRunningAsTest);
+73 -35
View File
@@ -1,16 +1,15 @@
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { SessionProvider } from "next-auth/react";
import type { Session } from "next-auth";
import { SessionProvider, useSession } from "next-auth/react";
import { EventCollectionProvider } from "next-collect/client";
import type { SSRConfig } from "next-i18next";
import { appWithTranslation } from "next-i18next";
import { ThemeProvider } from "next-themes";
import type { AppProps as NextAppProps, AppProps as NextJsAppProps } from "next/app";
import type { NextRouter } from "next/router";
import { useRouter } from "next/router";
import type { ParsedUrlQuery } from "querystring";
import type { ComponentProps, PropsWithChildren, ReactNode } from "react";
import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider";
import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks";
import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic";
import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic";
import { FeatureProvider } from "@calcom/features/flags/context/provider";
@@ -18,25 +17,32 @@ import { useFlags } from "@calcom/features/flags/hooks";
import { trpc } from "@calcom/trpc/react";
import { MetaProvider } from "@calcom/ui";
import usePublicPage from "@lib/hooks/usePublicPage";
import useIsBookingPage from "@lib/hooks/useIsBookingPage";
import type { WithNonceProps } from "@lib/withNonce";
import { useViewerI18n } from "@components/I18nLanguageHandler";
import { useClientViewerI18n } from "@components/I18nLanguageHandler";
const I18nextAdapter = appWithTranslation<NextJsAppProps<SSRConfig> & { children: React.ReactNode }>(
({ children }) => <>{children}</>
);
const I18nextAdapter = appWithTranslation<
NextJsAppProps<SSRConfig> & {
children: React.ReactNode;
}
>(({ children }) => <>{children}</>);
// Workaround for https://github.com/vercel/next.js/issues/8592
export type AppProps = Omit<
NextAppProps<WithNonceProps & { themeBasis?: string } & Record<string, unknown>>,
NextAppProps<
WithNonceProps & {
themeBasis?: string;
session: Session;
} & Record<string, unknown>
>,
"Component"
> & {
Component: NextAppProps["Component"] & {
requiresLicense?: boolean;
isThemeSupported?: boolean;
isBookingPage?: boolean | ((arg: { router: NextRouter }) => boolean);
getLayout?: (page: React.ReactElement, router: NextRouter) => ReactNode;
isBookingPage?: boolean | ((arg: { router: NextAppProps["router"] }) => boolean);
getLayout?: (page: React.ReactElement, router: NextAppProps["router"]) => ReactNode;
PageWrapper?: (props: AppProps) => JSX.Element;
};
@@ -48,14 +54,23 @@ type AppPropsWithChildren = AppProps & {
children: ReactNode;
};
const CustomI18nextProvider = (props: AppPropsWithChildren) => {
const getEmbedNamespace = (query: ParsedUrlQuery) => {
// Mostly embed query param should be available on server. Use that there.
// Use the most reliable detection on client
return typeof window !== "undefined" ? window.getEmbedNamespace() : (query.embed as string) || null;
};
// We dont need to pass nonce to the i18n provider - this was causing x2-x3 re-renders on a hard refresh
type AppPropsWithoutNonce = Omit<AppPropsWithChildren, "pageProps"> & {
pageProps: Omit<AppPropsWithChildren["pageProps"], "nonce">;
};
const CustomI18nextProvider = (props: AppPropsWithoutNonce) => {
/**
* i18n should never be clubbed with other queries, so that it's caching can be managed independently.
* We intend to not cache i18n query
**/
const { i18n, locale } = useViewerI18n().data ?? {
locale: "en",
};
const clientViewerI18n = useClientViewerI18n(props.router.locales || []);
const { i18n, locale } = clientViewerI18n.data || {};
const passedProps = {
...props,
@@ -65,6 +80,7 @@ const CustomI18nextProvider = (props: AppPropsWithChildren) => {
},
router: locale ? { locale } : props.router,
} as unknown as ComponentProps<typeof I18nextAdapter>;
return <I18nextAdapter {...passedProps} />;
};
@@ -78,20 +94,19 @@ const enum ThemeSupport {
}
type CalcomThemeProps = PropsWithChildren<
Pick<AppProps["pageProps"], "nonce" | "themeBasis"> &
Pick<AppProps, "router"> &
Pick<AppProps["pageProps"], "nonce" | "themeBasis"> &
Pick<AppProps["Component"], "isBookingPage" | "isThemeSupported">
>;
const CalcomThemeProvider = (props: CalcomThemeProps) => {
const router = useRouter();
// 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 embedNamespace = getEmbedNamespace(props.router.query);
const isEmbedMode = typeof embedNamespace === "string";
return (
<ThemeProvider {...getThemeProviderProps({ props, isEmbedMode, embedNamespace, router })}>
<ThemeProvider {...getThemeProviderProps({ props, isEmbedMode, embedNamespace })}>
{/* Embed Mode can be detected reliably only on client side here as there can be static generated pages as well which can't determine if it's embed mode at backend */}
{/* color-scheme makes background:transparent not work in iframe which is required by embed. */}
{typeof window !== "undefined" && !isEmbedMode && (
@@ -137,16 +152,14 @@ function getThemeProviderProps({
props,
isEmbedMode,
embedNamespace,
router,
}: {
props: Omit<CalcomThemeProps, "children">;
isEmbedMode: boolean;
embedNamespace: string | null;
router: NextRouter;
}) {
const isBookingPage = (() => {
if (typeof props.isBookingPage === "function") {
return props.isBookingPage({ router: router });
return props.isBookingPage({ router: props.router });
}
return props.isBookingPage;
})();
@@ -158,10 +171,10 @@ function getThemeProviderProps({
? ThemeSupport.None
: ThemeSupport.App;
const isBookingPageThemSupportRequired = themeSupport === ThemeSupport.Booking;
const isBookingPageThemeSupportRequired = themeSupport === ThemeSupport.Booking;
const themeBasis = props.themeBasis;
if ((isBookingPageThemSupportRequired || isEmbedMode) && !themeBasis) {
if ((isBookingPageThemeSupportRequired || isEmbedMode) && !themeBasis) {
console.warn(
"`themeBasis` is required for booking page theme support. Not providing it will cause theme flicker."
);
@@ -184,7 +197,7 @@ function getThemeProviderProps({
`embed-theme-${embedNamespace}${appearanceIdSuffix}${embedExplicitlySetThemeSuffix}`
: themeSupport === ThemeSupport.App
? "app-theme"
: isBookingPageThemSupportRequired
: isBookingPageThemeSupportRequired
? `booking-theme${appearanceIdSuffix}`
: undefined;
@@ -207,27 +220,52 @@ function FeatureFlagsProvider({ children }: { children: React.ReactNode }) {
return <FeatureProvider value={flags}>{children}</FeatureProvider>;
}
function useOrgBrandingValues() {
const session = useSession();
const res = trpc.viewer.organizations.getBrand.useQuery(undefined, {
// Only fetch if we have a session to avoid flooding logs with errors
enabled: session.status === "authenticated",
});
if (res.status === "loading") {
return undefined;
}
if (res.status === "error") return null;
return res.data;
}
function OrgBrandProvider({ children }: { children: React.ReactNode }) {
const orgBrand = useOrgBrandingValues();
return <OrgBrandingProvider value={orgBrand}>{children}</OrgBrandingProvider>;
return <OrgBrandingProvider value={{ orgBrand }}>{children}</OrgBrandingProvider>;
}
const AppProviders = (props: AppPropsWithChildren) => {
const session = trpc.viewer.public.session.useQuery().data;
// No need to have intercom on public pages - Good for Page Performance
const isPublicPage = usePublicPage();
const isBookingPage = useIsBookingPage();
const { pageProps, ...rest } = props;
const { _nonce, ...restPageProps } = pageProps;
const propsWithoutNonce = {
pageProps: {
...restPageProps,
},
...rest,
};
const RemainingProviders = (
<EventCollectionProvider options={{ apiPath: "/api/collect-events" }}>
<SessionProvider session={session || undefined}>
<CustomI18nextProvider {...props}>
<SessionProvider session={pageProps.session ?? undefined}>
<CustomI18nextProvider {...propsWithoutNonce}>
<TooltipProvider>
{/* 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 */}
<CalcomThemeProvider
themeBasis={props.pageProps.themeBasis}
nonce={props.pageProps.nonce}
isThemeSupported={props.Component.isThemeSupported}
isBookingPage={props.Component.isBookingPage}>
isBookingPage={props.Component.isBookingPage || isBookingPage}
router={props.router}>
<FeatureFlagsProvider>
<OrgBrandProvider>
<MetaProvider>{props.children}</MetaProvider>
@@ -240,7 +278,7 @@ const AppProviders = (props: AppPropsWithChildren) => {
</EventCollectionProvider>
);
if (isPublicPage) {
if (isBookingPage) {
return RemainingProviders;
}
-53
View File
@@ -1,53 +0,0 @@
import parser from "accept-language-parser";
import type { IncomingMessage } from "http";
import type { GetServerSidePropsContext } from "next";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import type { Maybe } from "@calcom/trpc/server";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { i18n } = require("@calcom/config/next-i18next.config");
export function getLocaleFromHeaders(req: IncomingMessage): string {
let preferredLocale: string | null | undefined;
if (req.headers["accept-language"]) {
preferredLocale = parser.pick(i18n.locales, req.headers["accept-language"]) as Maybe<string>;
}
return preferredLocale ?? i18n.defaultLocale;
}
export const getOrSetUserLocaleFromHeaders = async (
req: GetServerSidePropsContext["req"],
res: GetServerSidePropsContext["res"]
): Promise<string> => {
const { default: prisma } = await import("@calcom/prisma");
const session = await getServerSession({ req, res });
const preferredLocale = getLocaleFromHeaders(req);
if (session?.user?.id) {
const user = await prisma.user.findUnique({
where: {
id: session.user.id,
},
select: {
locale: true,
},
});
if (user?.locale) {
return user.locale;
}
await prisma.user.update({
where: {
id: session.user.id,
},
data: {
locale: preferredLocale,
},
});
}
return preferredLocale;
};

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