diff --git a/apps/web/components/ImageUploader.tsx b/apps/web/components/ImageUploader.tsx deleted file mode 100644 index d4c133154c..0000000000 --- a/apps/web/components/ImageUploader.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import type { FormEvent } from "react"; -import { useCallback, useEffect, useState } from "react"; -import Cropper from "react-easy-crop"; - -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { Button, Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui"; - -import type { Area } from "@lib/cropImage"; -import { getCroppedImg } from "@lib/cropImage"; -import { useFileReader } from "@lib/hooks/useFileReader"; - -import Slider from "@components/Slider"; - -type ImageUploaderProps = { - id: string; - buttonMsg: string; - handleAvatarChange: (imageSrc: string) => void; - imageSrc?: string; - target: string; -}; - -interface FileEvent extends FormEvent { - target: EventTarget & T; -} - -// This is separate to prevent loading the component until file upload -function CropContainer({ - onCropComplete, - imageSrc, -}: { - imageSrc: string; - onCropComplete: (croppedAreaPixels: Area) => void; -}) { - const { t } = useLocale(); - const [crop, setCrop] = useState({ x: 0, y: 0 }); - const [zoom, setZoom] = useState(1); - - const handleZoomSliderChange = (value: number) => { - value < 1 ? setZoom(1) : setZoom(value); - }; - - return ( -
-
- onCropComplete(croppedAreaPixels)} - onZoomChange={setZoom} - /> -
- -
- ); -} - -/** @deprecated Use `packages/ui/v2/core/ImageUploader.tsx` */ -export default function ImageUploader({ - target, - id, - buttonMsg, - handleAvatarChange, - ...props -}: ImageUploaderProps) { - const { t } = useLocale(); - const [imageSrc, setImageSrc] = useState(null); - const [croppedAreaPixels, setCroppedAreaPixels] = useState(null); - - const [{ result }, setFile] = useFileReader({ - method: "readAsDataURL", - }); - - useEffect(() => { - if (props.imageSrc) setImageSrc(props.imageSrc); - }, [props.imageSrc]); - - const onInputFile = (e: FileEvent) => { - if (!e.target.files?.length) { - return; - } - setFile(e.target.files[0]); - }; - - const showCroppedImage = useCallback( - async (croppedAreaPixels: Area | null) => { - try { - if (!croppedAreaPixels) return; - const croppedImage = await getCroppedImg( - result as string /* result is always string when using readAsDataUrl */, - croppedAreaPixels - ); - setImageSrc(croppedImage); - handleAvatarChange(croppedImage); - } catch (e) { - console.error(e); - } - }, - [result, handleAvatarChange] - ); - - return ( - !opened && setFile(null) // unset file on close - }> - -
- -
-
- -
-
- -
-
-
-
- {!result && ( -
- {!imageSrc && ( -

- {t("no_target", { target })} -

- )} - {imageSrc && ( - // eslint-disable-next-line @next/next/no-img-element - {target} - )} -
- )} - {result && } - -
-
-
- showCroppedImage(croppedAreaPixels)}>{t("save")} - {t("cancel")} -
-
-
- ); -} diff --git a/apps/web/components/NavTabs.tsx b/apps/web/components/NavTabs.tsx index f92e0fda40..fe66bc3558 100644 --- a/apps/web/components/NavTabs.tsx +++ b/apps/web/components/NavTabs.tsx @@ -1,4 +1,3 @@ -import { AdminRequired } from "components/ui/AdminRequired"; import { noop } from "lodash"; import type { LinkProps } from "next/link"; import Link from "next/link"; @@ -6,6 +5,7 @@ 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"; @@ -60,7 +60,7 @@ const NavTabs: FC = ({ tabs, linkProps, ...props }) => { } : noop; - const Component = tab.adminRequired ? AdminRequired : Fragment; + const Component = tab.adminRequired ? PermissionContainer : Fragment; const className = tab.className || ""; return ( diff --git a/apps/web/components/Slider.tsx b/apps/web/components/Slider.tsx deleted file mode 100644 index cf8e4d54ee..0000000000 --- a/apps/web/components/Slider.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as SliderPrimitive from "@radix-ui/react-slider"; -import React from "react"; - -const Slider = ({ - value, - label, - changeHandler, - ...props -}: Omit & { - value: number; - label: string; - changeHandler: (value: number) => void; -}) => ( - changeHandler(value[0] ?? value)} - {...props}> - - - - - -); - -export default Slider; diff --git a/apps/web/components/apps/App.tsx b/apps/web/components/apps/App.tsx index 49c10a5830..269da61eea 100644 --- a/apps/web/components/apps/App.tsx +++ b/apps/web/components/apps/App.tsx @@ -6,7 +6,7 @@ import React, { useState } from "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/v2/LicenseRequired"; +import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired"; 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"; diff --git a/apps/web/components/ui/AdminRequired.tsx b/apps/web/components/ui/AdminRequired.tsx deleted file mode 100644 index 34458396b0..0000000000 --- a/apps/web/components/ui/AdminRequired.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { useSession } from "next-auth/react"; -import type { FC } from "react"; -import { Fragment } from "react"; - -type AdminRequiredProps = { - as?: keyof JSX.IntrinsicElements; - children?: React.ReactNode; -}; - -/** @deprecated use PermssionContainer instead. Will delete once V2 goes live */ -export const AdminRequired: FC = ({ children, as, ...rest }) => { - const session = useSession(); - - if (session.data?.user.role !== "ADMIN") return null; - const Component = as ?? Fragment; - return {children}; -}; diff --git a/apps/web/package.json b/apps/web/package.json index 0a073bab1d..a894a0fb73 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -115,8 +115,6 @@ "react-select": "^5.7.0", "react-timezone-select": "^1.4.0", "react-use-intercom": "1.5.1", - "react-virtualized-auto-sizer": "^1.0.6", - "react-window": "^1.8.7", "remark": "^14.0.2", "rrule": "^2.7.1", "sanitize-html": "^2.10.0", @@ -155,8 +153,6 @@ "@types/qrcode": "^1.4.3", "@types/react": "18.0.26", "@types/react-phone-number-input": "^3.0.14", - "@types/react-virtualized-auto-sizer": "^1.0.1", - "@types/react-window": "^1.8.5", "@types/sanitize-html": "^2.9.0", "@types/stripe": "^8.0.417", "@types/uuid": "8.3.1", diff --git a/apps/web/pages/settings/developer/api-keys.tsx b/apps/web/pages/settings/developer/api-keys.tsx index e4d1f89ad0..3cadde85c5 100644 --- a/apps/web/pages/settings/developer/api-keys.tsx +++ b/apps/web/pages/settings/developer/api-keys.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import type { TApiKeys } from "@calcom/ee/api-keys/components/ApiKeyListItem"; -import LicenseRequired from "@calcom/ee/common/components/v2/LicenseRequired"; +import LicenseRequired from "@calcom/ee/common/components/LicenseRequired"; import ApiKeyDialogForm from "@calcom/features/ee/api-keys/components/ApiKeyDialogForm"; import ApiKeyListItem from "@calcom/features/ee/api-keys/components/ApiKeyListItem"; import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; diff --git a/apps/web/pages/signup.tsx b/apps/web/pages/signup.tsx index 4fe818ddd6..a454648351 100644 --- a/apps/web/pages/signup.tsx +++ b/apps/web/pages/signup.tsx @@ -5,7 +5,7 @@ import type { CSSProperties } from "react"; import type { SubmitHandler } from "react-hook-form"; import { FormProvider, useForm } from "react-hook-form"; -import LicenseRequired from "@calcom/features/ee/common/components/v2/LicenseRequired"; +import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired"; import { isSAMLLoginEnabled } from "@calcom/features/ee/sso/lib/saml"; import { WEBAPP_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; diff --git a/packages/features/auth/PermissionContainer.tsx b/packages/features/auth/PermissionContainer.tsx index dbe4a8222d..445aa45948 100644 --- a/packages/features/auth/PermissionContainer.tsx +++ b/packages/features/auth/PermissionContainer.tsx @@ -19,7 +19,8 @@ export const PermissionContainer: FC = ({ const session = useSession(); // Admin can do everything - if (session.data?.user.role !== roleRequired || !UserPermissionRole.ADMIN) return null; + if (session.data?.user.role !== roleRequired && session.data?.user.role != UserPermissionRole.ADMIN) + return null; const Component = as ?? Fragment; return {children}; }; diff --git a/packages/features/ee/api-keys/components/ApiKeyDialogForm.tsx b/packages/features/ee/api-keys/components/ApiKeyDialogForm.tsx index ad501e3aa4..4616df9b3b 100644 --- a/packages/features/ee/api-keys/components/ApiKeyDialogForm.tsx +++ b/packages/features/ee/api-keys/components/ApiKeyDialogForm.tsx @@ -3,7 +3,7 @@ import { Controller, useForm } from "react-hook-form"; import dayjs from "@calcom/dayjs"; import type { TApiKeys } from "@calcom/ee/api-keys/components/ApiKeyListItem"; -import LicenseRequired from "@calcom/ee/common/components/v2/LicenseRequired"; +import LicenseRequired from "@calcom/ee/common/components/LicenseRequired"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc/react"; import { Button, DatePicker, DialogFooter, Form, showToast, Switch, TextField, Tooltip } from "@calcom/ui"; diff --git a/packages/features/ee/common/components/LicenseRequired.tsx b/packages/features/ee/common/components/LicenseRequired.tsx index e4e36a76cd..103330b9f2 100644 --- a/packages/features/ee/common/components/LicenseRequired.tsx +++ b/packages/features/ee/common/components/LicenseRequired.tsx @@ -1,14 +1,9 @@ -/** - * @deprecated file - * All new changes should be made to the V2 file in - * `/packages/features/ee/common/components/v2/LicenseRequired.tsx` - */ import DOMPurify from "dompurify"; import { useSession } from "next-auth/react"; import type { AriaRole, ComponentType } from "react"; import React, { Fragment } from "react"; -import { APP_NAME, WEBAPP_URL } from "@calcom/lib/constants"; +import { APP_NAME, CONSOLE_URL, SUPPORT_MAIL_ADDRESS, WEBAPP_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { EmptyScreen } from "@calcom/ui"; import { AlertTriangle } from "@calcom/ui/components/icon"; @@ -20,20 +15,15 @@ type LicenseRequiredProps = { children: React.ReactNode; }; -/** - * @deprecated file - * All new changes should be made to the V2 file in - * `/packages/features/ee/common/components/v2/LicenseRequired.tsx` - * This component will only render it's children if the installation has a valid - * license. - */ const LicenseRequired = ({ children, as = "", ...rest }: LicenseRequiredProps) => { - const { t } = useLocale(); const session = useSession(); + const { t } = useLocale(); const Component = as || Fragment; + const hasValidLicense = session.data ? session.data.hasValidLicense : null; + return ( - {session.data?.hasValidLicense ? ( + {hasValidLicense === null || hasValidLicense ? ( children ) : ( + consoleUrl: ` ${APP_NAME} `, - setupUrl: `/auth/setup`, - supportMail: ` - sales@cal.com - `, + setupUrl: `/auth/setup`, + supportMail: ` + ${SUPPORT_MAIL_ADDRESS}`, }) ), }} diff --git a/packages/features/ee/common/components/v2/LicenseRequired.tsx b/packages/features/ee/common/components/v2/LicenseRequired.tsx deleted file mode 100644 index 103330b9f2..0000000000 --- a/packages/features/ee/common/components/v2/LicenseRequired.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import DOMPurify from "dompurify"; -import { useSession } from "next-auth/react"; -import type { AriaRole, ComponentType } from "react"; -import React, { Fragment } from "react"; - -import { APP_NAME, CONSOLE_URL, SUPPORT_MAIL_ADDRESS, WEBAPP_URL } from "@calcom/lib/constants"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { EmptyScreen } from "@calcom/ui"; -import { AlertTriangle } from "@calcom/ui/components/icon"; - -type LicenseRequiredProps = { - as?: keyof JSX.IntrinsicElements | ""; - className?: string; - role?: AriaRole | undefined; - children: React.ReactNode; -}; - -const LicenseRequired = ({ children, as = "", ...rest }: LicenseRequiredProps) => { - const session = useSession(); - const { t } = useLocale(); - const Component = as || Fragment; - const hasValidLicense = session.data ? session.data.hasValidLicense : null; - - return ( - - {hasValidLicense === null || hasValidLicense ? ( - children - ) : ( - - ${APP_NAME} - `, - setupUrl: `/auth/setup`, - supportMail: ` - ${SUPPORT_MAIL_ADDRESS}`, - }) - ), - }} - /> - } - /> - )} - - ); -}; - -export const withLicenseRequired = - (Component: ComponentType) => - // eslint-disable-next-line react/display-name - (hocProps: T) => - ( - - - - ); - -export default LicenseRequired; diff --git a/packages/features/ee/sso/components/SSOConfiguration.tsx b/packages/features/ee/sso/components/SSOConfiguration.tsx index abf0530c35..a214967d54 100644 --- a/packages/features/ee/sso/components/SSOConfiguration.tsx +++ b/packages/features/ee/sso/components/SSOConfiguration.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import ConnectionInfo from "@calcom/ee/sso/components/ConnectionInfo"; -import LicenseRequired from "@calcom/features/ee/common/components/v2/LicenseRequired"; +import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired"; import OIDCConnection from "@calcom/features/ee/sso/components/OIDCConnection"; import SAMLConnection from "@calcom/features/ee/sso/components/SAMLConnection"; import { useLocale } from "@calcom/lib/hooks/useLocale"; diff --git a/packages/features/ee/teams/components/MemberListItem.tsx b/packages/features/ee/teams/components/MemberListItem.tsx index 4e234a4bdf..b94bba4353 100644 --- a/packages/features/ee/teams/components/MemberListItem.tsx +++ b/packages/features/ee/teams/components/MemberListItem.tsx @@ -29,8 +29,8 @@ import { import { ExternalLink, MoreHorizontal, Edit2, Lock, Trash } from "@calcom/ui/components/icon"; import MemberChangeRoleModal from "./MemberChangeRoleModal"; +import TeamAvailabilityModal from "./TeamAvailabilityModal"; import TeamPill, { TeamRole } from "./TeamPill"; -import TeamAvailabilityModal from "./v2/TeamAvailabilityModal"; interface Props { team: RouterOutputs["viewer"]["teams"]["get"]; diff --git a/packages/features/ee/teams/components/v2/SkeletonLoaderAvailabilityTimes.tsx b/packages/features/ee/teams/components/SkeletonLoaderAvailabilityTimes.tsx similarity index 100% rename from packages/features/ee/teams/components/v2/SkeletonLoaderAvailabilityTimes.tsx rename to packages/features/ee/teams/components/SkeletonLoaderAvailabilityTimes.tsx diff --git a/packages/features/ee/teams/components/TeamAvailabilityModal.tsx b/packages/features/ee/teams/components/TeamAvailabilityModal.tsx index bd680bd9d8..a4522a6ba1 100644 --- a/packages/features/ee/teams/components/TeamAvailabilityModal.tsx +++ b/packages/features/ee/teams/components/TeamAvailabilityModal.tsx @@ -1,13 +1,14 @@ import { useEffect, useState } from "react"; import dayjs from "@calcom/dayjs"; +import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired"; import { WEBAPP_URL } from "@calcom/lib/constants"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { RouterOutputs } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react"; import type { ITimezone } from "@calcom/ui"; -import { Avatar, DatePickerField as DatePicker, Select, TimezoneSelect } from "@calcom/ui"; +import { Avatar, DatePicker, Label, Select, TimezoneSelect } from "@calcom/ui"; -import LicenseRequired from "../../common/components/LicenseRequired"; import TeamAvailabilityTimes from "./TeamAvailabilityTimes"; interface Props { @@ -21,6 +22,9 @@ export default function TeamAvailabilityModal(props: Props) { const [selectedTimeZone, setSelectedTimeZone] = useState( localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess() ); + + const { t } = useLocale(); + const [frequency, setFrequency] = useState<15 | 30 | 60>(30); useEffect(() => { @@ -29,66 +33,71 @@ export default function TeamAvailabilityModal(props: Props) { return ( -
-
-
- -
- {props.member?.name} - {props.member?.email} + <> +
+
+
+ +
+ + {props.member?.name} + +
+
+
+
{t("availability")}
+ { + setSelectedDate(dayjs(newDate)); + }} + /> + + + setSelectedTimeZone(timezone.value)} + classNamePrefix="react-select" + /> +
+
+ + setFrequency(newFrequency?.value ?? 30)} - /> + +
+ {props.team && props.member && ( + + )}
- {props.team && props.member && ( - - )} -
+ ); } diff --git a/packages/features/ee/teams/components/TeamAvailabilityScreen.tsx b/packages/features/ee/teams/components/TeamAvailabilityScreen.tsx deleted file mode 100644 index e33fb661a1..0000000000 --- a/packages/features/ee/teams/components/TeamAvailabilityScreen.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import type { CSSProperties } from "react"; -import { useEffect, useState } from "react"; -import AutoSizer from "react-virtualized-auto-sizer"; -import { FixedSizeList as List } from "react-window"; - -import dayjs from "@calcom/dayjs"; -import { CAL_URL } from "@calcom/lib/constants"; -import type { RouterOutputs } from "@calcom/trpc/react"; -import { trpc } from "@calcom/trpc/react"; -import type { ITimezone } from "@calcom/ui"; -import { Avatar, DatePickerField as DatePicker, Select, TimezoneSelect } from "@calcom/ui"; - -import TeamAvailabilityTimes from "./TeamAvailabilityTimes"; - -interface Props { - team?: RouterOutputs["viewer"]["teams"]["get"]; -} - -export default function TeamAvailabilityScreen(props: Props) { - const utils = trpc.useContext(); - const [selectedDate, setSelectedDate] = useState(dayjs()); - const [selectedTimeZone, setSelectedTimeZone] = useState( - localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess() - ); - const [frequency, setFrequency] = useState<15 | 30 | 60>(30); - - useEffect(() => { - utils.viewer.teams.getMemberAvailability.invalidate(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedTimeZone, selectedDate]); - - const Item = ({ index, style }: { index: number; style: CSSProperties }) => { - const member = props.team?.members?.[index]; - if (!member) return <>; - - return ( -
- - -
- {member?.name} - {member?.email} -
-
- } - /> -
- ); - }; - - return ( -
-
-
- Date - { - setSelectedDate(dayjs(newDate)); - }} - /> -
-
- Timezone - setSelectedTimeZone(timezone.value)} - classNamePrefix="react-select" - className="react-select-container border-default w-full rounded-sm border text-sm" - /> -
-
- Slot Length - setFrequency(newFrequency?.value ?? 30)} - /> -
-
- -
- {props.team && props.member && ( - - )} -
-
- - - ); -} diff --git a/packages/features/ee/teams/components/v2/TeamAvailabilityScreen.tsx b/packages/features/ee/teams/components/v2/TeamAvailabilityScreen.tsx deleted file mode 100644 index e33fb661a1..0000000000 --- a/packages/features/ee/teams/components/v2/TeamAvailabilityScreen.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import type { CSSProperties } from "react"; -import { useEffect, useState } from "react"; -import AutoSizer from "react-virtualized-auto-sizer"; -import { FixedSizeList as List } from "react-window"; - -import dayjs from "@calcom/dayjs"; -import { CAL_URL } from "@calcom/lib/constants"; -import type { RouterOutputs } from "@calcom/trpc/react"; -import { trpc } from "@calcom/trpc/react"; -import type { ITimezone } from "@calcom/ui"; -import { Avatar, DatePickerField as DatePicker, Select, TimezoneSelect } from "@calcom/ui"; - -import TeamAvailabilityTimes from "./TeamAvailabilityTimes"; - -interface Props { - team?: RouterOutputs["viewer"]["teams"]["get"]; -} - -export default function TeamAvailabilityScreen(props: Props) { - const utils = trpc.useContext(); - const [selectedDate, setSelectedDate] = useState(dayjs()); - const [selectedTimeZone, setSelectedTimeZone] = useState( - localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess() - ); - const [frequency, setFrequency] = useState<15 | 30 | 60>(30); - - useEffect(() => { - utils.viewer.teams.getMemberAvailability.invalidate(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedTimeZone, selectedDate]); - - const Item = ({ index, style }: { index: number; style: CSSProperties }) => { - const member = props.team?.members?.[index]; - if (!member) return <>; - - return ( -
- - -
- {member?.name} - {member?.email} -
-
- } - /> -
- ); - }; - - return ( -
-
-
- Date - { - setSelectedDate(dayjs(newDate)); - }} - /> -
-
- Timezone - setSelectedTimeZone(timezone.value)} - classNamePrefix="react-select" - className="react-select-container border-default w-full rounded-sm border text-sm" - /> -
-
- Slot Length - - {children} - - ); -}; - -type MultiSelectionCheckboxesProps = { - options: { label: string; value: string }[]; - setSelected: Dispatch>; - selected: Option[]; - setValue: (s: Option[]) => unknown; -}; - -const MultiValue = ({ index, getValue }: { index: number; getValue: any }) => { - const { t } = useLocale(); - - return <>{!index &&
{t("nr_event_type", { count: getValue().length })}
}; -}; - -/** - * @deprecated file - * All new changes should be made to the V2 file in - * `/packages/ui/v2/core/form/MultiSelectCheckboxes.tsx` - */ -export default function MultiSelectCheckboxes({ - options, - isLoading, - selected, - setSelected, - setValue, -}: Omit & MultiSelectionCheckboxesProps) { - const additonalComponents = { MultiValue }; - - return ( -