diff --git a/apps/web/components/Embed.tsx b/apps/web/components/Embed.tsx index 1885747c..b48eea54 100644 --- a/apps/web/components/Embed.tsx +++ b/apps/web/components/Embed.tsx @@ -1,18 +1,18 @@ -import { CodeIcon, EyeIcon, SunIcon, ChevronRightIcon, ArrowLeftIcon } from "@heroicons/react/solid"; +import { ArrowLeftIcon, ChevronRightIcon, CodeIcon, EyeIcon, SunIcon } from "@heroicons/react/solid"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible"; import classNames from "classnames"; import { useRouter } from "next/router"; import { useRef, useState } from "react"; -import { components, ControlProps, SingleValue } from "react-select"; +import { components, ControlProps } from "react-select"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import showToast from "@calcom/lib/notification"; import { EventType } from "@calcom/prisma/client"; import { Button, Switch } from "@calcom/ui"; -import { Dialog, DialogContent, DialogClose } from "@calcom/ui/Dialog"; +import { Dialog, DialogClose, DialogContent } from "@calcom/ui/Dialog"; import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields"; -import { WEBAPP_URL, EMBED_LIB_URL } from "@lib/config/constants"; +import { EMBED_LIB_URL, WEBAPP_URL } from "@lib/config/constants"; import { trpc } from "@lib/trpc"; import NavTabs from "@components/NavTabs"; @@ -241,7 +241,10 @@ const EmbedNavBar = () => { return ; }; -const ThemeSelectControl = ({ children, ...props }: ControlProps) => { +const ThemeSelectControl = ({ + children, + ...props +}: ControlProps<{ value: string; label: string }, false>) => { return ( @@ -381,7 +384,7 @@ Cal("inline", { }); ${getEmbedUIInstructionString().trim()}`; } else if (embedType === "floating-popup") { - let floatingButtonArg = { + const floatingButtonArg = { calLink, ...previewState.floatingPopup, }; @@ -418,7 +421,7 @@ ${getEmbedUIInstructionString().trim()}`; }); }; - const previewInstruction = (instruction: { name: string; arg: any }) => { + const previewInstruction = (instruction: { name: string; arg: unknown }) => { iframeRef.current?.contentWindow?.postMessage( { mode: "cal:preview", @@ -544,7 +547,7 @@ ${getEmbedUIInstructionString().trim()}`; value={previewState.inline.width} onChange={(e) => { setPreviewState((previewState) => { - let width = e.target.value || "100%"; + const width = e.target.value || "100%"; return { ...previewState, @@ -759,11 +762,11 @@ ${getEmbedUIInstructionString().trim()}`; { - //@ts-ignore - How to support dynamic palette names? addToPalette({ - [palette.name]: color, + [palette.name as keyof typeof previewState["palette"]]: color, }); - }}> + }} + /> ))} diff --git a/apps/web/components/NavTabs.tsx b/apps/web/components/NavTabs.tsx index a08f79d4..4aefe925 100644 --- a/apps/web/components/NavTabs.tsx +++ b/apps/web/components/NavTabs.tsx @@ -1,4 +1,5 @@ import { AdminRequired } from "components/ui/AdminRequired"; +import noop from "lodash/noop"; import Link, { LinkProps } from "next/link"; import { useRouter } from "next/router"; import { FC, Fragment, MouseEventHandler } from "react"; @@ -28,11 +29,11 @@ const NavTabs: FC = ({ tabs, linkProps, ...props }) => { aria-label="Tabs" {...props}> {tabs.map((tab) => { - let href: string; - let isCurrent; 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; @@ -40,6 +41,7 @@ const NavTabs: FC = ({ tabs, linkProps, ...props }) => { href = ""; isCurrent = router.query.tabName === tab.tabName; } + const onClick: MouseEventHandler = tab.tabName ? (e) => { e.preventDefault(); @@ -50,12 +52,14 @@ const NavTabs: FC = ({ tabs, linkProps, ...props }) => { }, }); } - : () => {}; + : noop; const Component = tab.adminRequired ? AdminRequired : Fragment; + + if (!href) return null; return ( - + ({ +const Slider = ({ title = "", className = "", items, diff --git a/apps/web/components/availability/NewScheduleButton.tsx b/apps/web/components/availability/NewScheduleButton.tsx index 8dc84a9e..6960d2eb 100644 --- a/apps/web/components/availability/NewScheduleButton.tsx +++ b/apps/web/components/availability/NewScheduleButton.tsx @@ -6,7 +6,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import showToast from "@calcom/lib/notification"; import { Button } from "@calcom/ui"; import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui/Dialog"; -import { Form, TextField } from "@calcom/ui/form/fields"; +import { Form } from "@calcom/ui/form/fields"; import { HttpError } from "@lib/core/http/error"; import { trpc } from "@lib/trpc"; diff --git a/apps/web/components/availability/Schedule.tsx b/apps/web/components/availability/Schedule.tsx index 76bc4b1c..1369f480 100644 --- a/apps/web/components/availability/Schedule.tsx +++ b/apps/web/components/availability/Schedule.tsx @@ -6,7 +6,7 @@ import timezone from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { Controller, useFieldArray, useFormContext } from "react-hook-form"; -import { GroupBase, Props, SingleValue } from "react-select"; +import { GroupBase, Props } from "react-select"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import Button from "@calcom/ui/Button"; diff --git a/apps/web/components/availability/ScheduleListItem.tsx b/apps/web/components/availability/ScheduleListItem.tsx index f085c0de..74f75956 100644 --- a/apps/web/components/availability/ScheduleListItem.tsx +++ b/apps/web/components/availability/ScheduleListItem.tsx @@ -16,7 +16,7 @@ export function ScheduleListItem({ isDeleting = false, }: { schedule: inferQueryOutput<"viewer.availability.list">["schedules"][number]; - deleteFunction: Function; + deleteFunction: ({ scheduleId }: { scheduleId: number }) => void; isDeleting: boolean; }) { const { t, i18n } = useLocale(); diff --git a/apps/web/components/booking/DatePicker.tsx b/apps/web/components/booking/DatePicker.tsx index 6ff2334a..ff4848ff 100644 --- a/apps/web/components/booking/DatePicker.tsx +++ b/apps/web/components/booking/DatePicker.tsx @@ -51,12 +51,13 @@ function isOutOfBounds( > ) { const date = dayjs(time); + if (!periodDays) throw Error("periodDays is undefined"); switch (periodType) { case PeriodType.ROLLING: { const periodRollingEndDay = periodCountCalendarDays - ? dayjs().utcOffset(date.utcOffset()).add(periodDays!, "days").endOf("day") - : dayjs().utcOffset(date.utcOffset()).businessDaysAdd(periodDays!).endOf("day"); + ? dayjs().utcOffset(date.utcOffset()).add(periodDays, "days").endOf("day") + : dayjs().utcOffset(date.utcOffset()).businessDaysAdd(periodDays).endOf("day"); return date.endOf("day").isAfter(periodRollingEndDay); } @@ -94,7 +95,7 @@ function DatePicker({ const [isFirstMonth, setIsFirstMonth] = useState(false); const [daysFromState, setDays] = useState< | { - disabled: Boolean; + disabled: boolean; date: number; }[] | null @@ -191,7 +192,7 @@ function DatePicker({ name: "DatePicker", length: daysInMonth, callback: (i: number) => { - let day = i + 1; + const day = i + 1; days[daysInitialOffset + i] = { disabled: isDisabledMemoized(day, { browsingDate, diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 604a9ace..ab1ecbe8 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -25,7 +25,6 @@ import { useIsEmbed, useIsBackgroundTransparent, sdkActionManager, - useEmbedType, useEmbedNonStylesConfig, } from "@calcom/embed-core"; import classNames from "@calcom/lib/classNames"; @@ -68,7 +67,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage const availabilityDatePickerEmbedStyles = useEmbedStyles("availabilityDatePicker"); const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left"; const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed; - let isBackgroundTransparent = useIsBackgroundTransparent(); + const isBackgroundTransparent = useIsBackgroundTransparent(); useExposePlanGlobally(plan); useEffect(() => { if (eventType.metadata.smartContractAddress) { diff --git a/apps/web/components/booking/pages/BookingPage.tsx b/apps/web/components/booking/pages/BookingPage.tsx index f479d00e..0bff2931 100644 --- a/apps/web/components/booking/pages/BookingPage.tsx +++ b/apps/web/components/booking/pages/BookingPage.tsx @@ -52,6 +52,15 @@ import { BookPageProps } from "../../../pages/[user]/book"; import { HashLinkPageProps } from "../../../pages/d/[link]/book"; import { TeamBookingPageProps } from "../../../pages/team/[slug]/book"; +declare global { + // eslint-disable-next-line no-var + var web3: { + currentProvider: { + selectedAddress: string; + }; + }; +} + /** These are like 40kb that not every user needs */ const PhoneInput = dynamic( () => import("@components/ui/form/PhoneInput") @@ -107,7 +116,6 @@ const BookingPage = ({ const eventOwner = eventType.users[0]; if (!contracts[(eventType.metadata.smartContractAddress || null) as number]) - /* @ts-ignore */ router.replace(`/${eventOwner.username}`); } }, [contracts, eventType.metadata.smartContractAddress, eventType.users, router]); @@ -331,7 +339,6 @@ const BookingPage = ({ let web3Details: Record<"userWallet" | "userSignature", string> | undefined; if (eventTypeDetail.metadata.smartContractAddress) { web3Details = { - // @ts-ignore userWallet: window.web3.currentProvider.selectedAddress, userSignature: contracts[(eventTypeDetail.metadata.smartContractAddress || null) as number], }; @@ -359,8 +366,8 @@ const BookingPage = ({ ), metadata, customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({ - label: eventType.customInputs.find((input) => input.id === parseInt(inputId))!.label, - value: booking.customInputs![inputId], + label: eventType.customInputs.find((input) => input.id === parseInt(inputId))?.label || "", + value: booking.customInputs && inputId in booking.customInputs ? booking.customInputs[inputId] : "", })), hasHashedBookingLink, hashedLink, @@ -383,8 +390,8 @@ const BookingPage = ({ ), metadata, customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({ - label: eventType.customInputs.find((input) => input.id === parseInt(inputId))!.label, - value: booking.customInputs![inputId], + label: eventType.customInputs.find((input) => input.id === parseInt(inputId))?.label || "", + value: booking.customInputs && inputId in booking.customInputs ? booking.customInputs[inputId] : "", })), hasHashedBookingLink, hashedLink, diff --git a/apps/web/components/dialog/DeleteStripeDialogContent.tsx b/apps/web/components/dialog/DeleteStripeDialogContent.tsx index 18ab4e49..02409bda 100644 --- a/apps/web/components/dialog/DeleteStripeDialogContent.tsx +++ b/apps/web/components/dialog/DeleteStripeDialogContent.tsx @@ -1,15 +1,13 @@ import { ExclamationIcon } from "@heroicons/react/outline"; import { CheckIcon } from "@heroicons/react/solid"; import * as DialogPrimitive from "@radix-ui/react-dialog"; -import React, { PropsWithChildren, ReactNode } from "react"; +import React, { PropsWithChildren } from "react"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Button } from "@calcom/ui/Button"; import { DialogClose, DialogContent } from "@calcom/ui/Dialog"; -import { useLocale } from "@lib/hooks/useLocale"; - export type DeleteStripeDialogContentProps = { - confirmBtn?: ReactNode; cancelAllBookingsBtnText?: string; removeBtnText?: string; cancelBtnText?: string; @@ -24,7 +22,6 @@ export default function DeleteStripeDialogContent(props: PropsWithChildren; + formMethods: UseFormReturn; paymentEnabled: boolean; - onRecurringEventDefined: Function; + onRecurringEventDefined: (value: boolean) => void; }; export default function RecurringEventController({ diff --git a/apps/web/components/ui/ImpersonatingBanner.tsx b/apps/web/components/ui/ImpersonatingBanner.tsx index 41238970..72403498 100644 --- a/apps/web/components/ui/ImpersonatingBanner.tsx +++ b/apps/web/components/ui/ImpersonatingBanner.tsx @@ -4,9 +4,7 @@ import { Trans } from "next-i18next"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Alert } from "@calcom/ui/Alert"; -type Props = {}; - -function ImpersonatingBanner({}: Props) { +function ImpersonatingBanner() { const { t } = useLocale(); const { data } = useSession(); diff --git a/apps/web/components/ui/ModalContainer.tsx b/apps/web/components/ui/ModalContainer.tsx index 94f79dd4..63964207 100644 --- a/apps/web/components/ui/ModalContainer.tsx +++ b/apps/web/components/ui/ModalContainer.tsx @@ -1,7 +1,7 @@ import classNames from "classnames"; import React from "react"; -import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog"; +import { Dialog, DialogContent } from "@calcom/ui/Dialog"; interface Props extends React.PropsWithChildren { wide?: boolean; diff --git a/apps/web/lib/auth/next-auth-custom-adapter.ts b/apps/web/lib/auth/next-auth-custom-adapter.ts index 1fda3b95..64aded03 100644 --- a/apps/web/lib/auth/next-auth-custom-adapter.ts +++ b/apps/web/lib/auth/next-auth-custom-adapter.ts @@ -1,4 +1,5 @@ import { Account, IdentityProvider, Prisma, PrismaClient, User, VerificationToken } from "@prisma/client"; +import { PrismaClientKnownRequestError } from "@prisma/client/runtime"; import { identityProviderNameMap } from "@lib/auth"; @@ -45,6 +46,7 @@ export default function CalComAdapter(prismaClient: PrismaClient) { prismaClient.user.update({ where: { id }, data }), deleteUser: (id: User["id"]) => prismaClient.user.delete({ where: { id } }), async createVerificationToken(data: VerificationToken) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { id: _, ...verificationToken } = await prismaClient.verificationToken.create({ data, }); @@ -52,6 +54,7 @@ export default function CalComAdapter(prismaClient: PrismaClient) { }, async useVerificationToken(identifier_token: Prisma.VerificationTokenIdentifierTokenCompoundUniqueInput) { try { + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { id: _, ...verificationToken } = await prismaClient.verificationToken.delete({ where: { identifier_token }, }); @@ -59,8 +62,9 @@ export default function CalComAdapter(prismaClient: PrismaClient) { } catch (error) { // If token already used/deleted, just return null // https://www.prisma.io/docs/reference/api-reference/error-reference#p2025 - // @ts-ignore - if (error.code === "P2025") return null; + if (error instanceof PrismaClientKnownRequestError) { + if (error.code === "P2025") return null; + } throw error; } }, diff --git a/apps/web/lib/doWorkAsync.ts b/apps/web/lib/doWorkAsync.ts index 9033db4c..dc9ee623 100644 --- a/apps/web/lib/doWorkAsync.ts +++ b/apps/web/lib/doWorkAsync.ts @@ -1,3 +1,5 @@ +import noop from "lodash/noop"; + const data: Record = {}; /** * Starts an iteration from `0` to `length - 1` with batch size `batch` @@ -20,9 +22,9 @@ export const doWorkAsync = ({ }: { name: string; length: number; - callback: Function; - done?: Function; - batchDone?: Function; + callback: (i: number, b?: boolean) => void; + done?: () => void; + batchDone?: () => void; batch: number; offsetStart?: number; __pending?: boolean; @@ -32,8 +34,8 @@ export const doWorkAsync = ({ const lastIndex = length - 1; const offsetEndExclusive = offsetStart + stepLength; - batchDone = batchDone || (() => {}); - done = done || (() => {}); + batchDone = batchDone || noop; + done = done || noop; if (!__pending && data[name]) { cancelAnimationFrame(data[name]); diff --git a/apps/web/lib/emails/email-manager.ts b/apps/web/lib/emails/email-manager.ts index 6cb35484..ae230c1e 100644 --- a/apps/web/lib/emails/email-manager.ts +++ b/apps/web/lib/emails/email-manager.ts @@ -1,4 +1,3 @@ -import { recurringEvent } from "@calcom/prisma/zod-utils"; import type { CalendarEvent, Person, RecurringEvent } from "@calcom/types/Calendar"; import AttendeeAwaitingPaymentEmail from "@lib/emails/templates/attendee-awaiting-payment-email"; diff --git a/apps/web/lib/emails/templates/_base-email.ts b/apps/web/lib/emails/templates/_base-email.ts index 3cd5b44e..58808c45 100644 --- a/apps/web/lib/emails/templates/_base-email.ts +++ b/apps/web/lib/emails/templates/_base-email.ts @@ -4,7 +4,7 @@ import { getErrorFromUnknown } from "@calcom/lib/errors"; import { serverConfig } from "@calcom/lib/serverConfig"; export default class BaseEmail { - name: string = ""; + name = ""; protected getNodeMailerPayload(): Record { return {}; diff --git a/apps/web/lib/hooks/useTheme.tsx b/apps/web/lib/hooks/useTheme.tsx index b0afafe6..5c93b746 100644 --- a/apps/web/lib/hooks/useTheme.tsx +++ b/apps/web/lib/hooks/useTheme.tsx @@ -1,5 +1,4 @@ import Head from "next/head"; -import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { useEmbedTheme } from "@calcom/embed-core"; diff --git a/apps/web/lib/parseDate.ts b/apps/web/lib/parseDate.ts index c0a05057..19de2750 100644 --- a/apps/web/lib/parseDate.ts +++ b/apps/web/lib/parseDate.ts @@ -2,7 +2,6 @@ import dayjs, { Dayjs } from "dayjs"; import { I18n } from "next-i18next"; import { RRule } from "rrule"; -import { recurringEvent } from "@calcom/prisma/zod-utils"; import { RecurringEvent } from "@calcom/types/Calendar"; import { detectBrowserTimeFormat } from "@lib/timeFormat"; @@ -29,6 +28,7 @@ export const parseRecurringDates = ( }: { startDate: string | null | Dayjs; recurringEvent: RecurringEvent; recurringCount: number }, i18n: I18n ): [string[], Date[]] => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { count, ...restRecurringEvent } = recurringEvent; const rule = new RRule({ ...restRecurringEvent, diff --git a/apps/web/lib/slots.ts b/apps/web/lib/slots.ts index 2fb9920b..f7011edf 100644 --- a/apps/web/lib/slots.ts +++ b/apps/web/lib/slots.ts @@ -89,7 +89,7 @@ const getSlots = ({ inviteeDate, frequency, minimumBookingNotice, workingHours, computedLocalWorkingHours.push(tempComputeTimeFrame); } }); - computedLocalWorkingHours.forEach((item, index) => { + computedLocalWorkingHours.forEach((item) => { slotsTimeFrameAvailable.push(...splitAvailableTime(item.startTime, item.endTime, frequency, eventLength)); }); diff --git a/apps/web/lib/telemetry.ts b/apps/web/lib/telemetry.ts index a54fdbbe..b7d66d0f 100644 --- a/apps/web/lib/telemetry.ts +++ b/apps/web/lib/telemetry.ts @@ -1,6 +1,11 @@ import { jitsuClient, JitsuClient } from "@jitsu/sdk-js"; import React, { useContext } from "react"; +declare global { + // eslint-disable-next-line no-var + var jitsu: JitsuClient | undefined; +} + /** * Enumeration of all event types that are being sent * to telemetry collection. @@ -49,7 +54,10 @@ function isLocalhost(host: string) { * Collects page parameters and makes sure no sensitive data made it to telemetry * @param route current next.js route */ -export function collectPageParameters(route?: string, extraData: Record = {}): any { +export function collectPageParameters( + route?: string, + extraData: Record = {} +): Record { const host = document.location.hostname; const maskedHost = isLocalhost(host) ? "localhost" : "masked"; //starts with '' @@ -78,13 +86,7 @@ function createTelemetryClient(): TelemetryClient { if (!window) { console.warn("Jitsu has been called during SSR, this scenario isn't supported yet"); return; - } else if ( - // FIXME - // @ts-ignore - !window["jitsu"] - ) { - // FIXME - // @ts-ignore + } else if (!window["jitsu"]) { window["jitsu"] = jitsuClient({ log_level: "ERROR", tracking_host: "https://t.calendso.com", @@ -93,8 +95,6 @@ function createTelemetryClient(): TelemetryClient { capture_3rd_party_cookies: false, }); } - // FIXME - // @ts-ignore const res = callback(window["jitsu"]); if (res && typeof res["catch"] === "function") { res.catch((e) => { diff --git a/apps/web/pages/[user].tsx b/apps/web/pages/[user].tsx index 58854b3c..5d92b486 100644 --- a/apps/web/pages/[user].tsx +++ b/apps/web/pages/[user].tsx @@ -6,7 +6,7 @@ import { GetServerSidePropsContext } from "next"; import dynamic from "next/dynamic"; import Link from "next/link"; import { useRouter } from "next/router"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { Toaster } from "react-hot-toast"; import { JSONObject } from "superjson/dist/types"; @@ -18,7 +18,6 @@ import defaultEvents, { getUsernameSlugLink, } from "@calcom/lib/defaultEvents"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { RecurringEvent } from "@calcom/types/Calendar"; import { useExposePlanGlobally } from "@lib/hooks/useExposePlanGlobally"; import useTheme from "@lib/hooks/useTheme"; @@ -287,7 +286,7 @@ const getEventTypesWithHiddenFromDB = async (userId: number, plan: UserPlan) => export const getServerSideProps = async (context: GetServerSidePropsContext) => { const ssr = await ssrInit(context); - const crypto = require("crypto"); + const crypto = await import("crypto"); const usernameList = getUsernameList(context.query.user as string); const dataFetchStart = Date.now(); diff --git a/apps/web/pages/_app.tsx b/apps/web/pages/_app.tsx index 77b39982..794102f2 100644 --- a/apps/web/pages/_app.tsx +++ b/apps/web/pages/_app.tsx @@ -1,7 +1,5 @@ import { DefaultSeo } from "next-seo"; import Head from "next/head"; -import { useEffect } from "react"; -// import { ReactQueryDevtools } from "react-query/devtools"; import superjson from "superjson"; import "@calcom/embed-core/src/embed-iframe"; diff --git a/apps/web/pages/_error.tsx b/apps/web/pages/_error.tsx index 9b52f31c..16b292c7 100644 --- a/apps/web/pages/_error.tsx +++ b/apps/web/pages/_error.tsx @@ -34,7 +34,7 @@ const CustomError: NextPage = (props) => { // getInitialProps is not called in case of // https://github.com/vercel/next.js/issues/8592. As a workaround, we pass // err via _app.tsx so it can be captured - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars const e = getErrorFromUnknown(err); // can be captured here // e.g. Sentry.captureException(e); diff --git a/apps/web/pages/api/auth/[...nextauth].tsx b/apps/web/pages/api/auth/[...nextauth].tsx index b30a18a3..ccd947bc 100644 --- a/apps/web/pages/api/auth/[...nextauth].tsx +++ b/apps/web/pages/api/auth/[...nextauth].tsx @@ -1,4 +1,3 @@ -import { PrismaAdapter } from "@next-auth/prisma-adapter"; import { IdentityProvider, UserPermissionRole } from "@prisma/client"; import { readFileSync } from "fs"; import Handlebars from "handlebars"; @@ -185,6 +184,7 @@ if (true) { } const calcomAdapter = CalComAdapter(prisma); export default NextAuth({ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore adapter: calcomAdapter, session: { @@ -203,6 +203,7 @@ export default NextAuth({ const autoMergeIdentities = async () => { if (!hostedCal) { const existingUser = await prisma.user.findFirst({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion where: { email: token.email! }, }); diff --git a/apps/web/pages/api/auth/saml/authorize.ts b/apps/web/pages/api/auth/saml/authorize.ts index 3ed076e9..5fd4ac93 100644 --- a/apps/web/pages/api/auth/saml/authorize.ts +++ b/apps/web/pages/api/auth/saml/authorize.ts @@ -1,6 +1,8 @@ import { OAuthReqBody } from "@boxyhq/saml-jackson"; import { NextApiRequest, NextApiResponse } from "next"; +import { HttpError } from "@calcom/lib/http-error"; + import jackson from "@lib/jackson"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -12,10 +14,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const { oauthController } = await jackson(); const { redirect_url } = await oauthController.authorize(req.query as unknown as OAuthReqBody); res.redirect(302, redirect_url); - } catch (err: any) { - console.error("authorize error:", err); - const { message, statusCode = 500 } = err; - - res.status(statusCode).send(message); + } catch (err: unknown) { + if (err instanceof HttpError) { + console.error("authorize error:", err); + const { message, statusCode = 500 } = err; + return res.status(statusCode).send(message); + } + return res.status(500).send("Unknown error"); } } diff --git a/apps/web/pages/api/auth/saml/callback.ts b/apps/web/pages/api/auth/saml/callback.ts index 2b74d1f2..5221198d 100644 --- a/apps/web/pages/api/auth/saml/callback.ts +++ b/apps/web/pages/api/auth/saml/callback.ts @@ -1,5 +1,7 @@ import { NextApiRequest, NextApiResponse } from "next"; +import { HttpError } from "@calcom/lib/http-error"; + import jackson from "@lib/jackson"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -12,10 +14,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const { redirect_url } = await oauthController.samlResponse(req.body); res.redirect(302, redirect_url); - } catch (err: any) { - console.error("callback error:", err); - const { message, statusCode = 500 } = err; - - res.status(statusCode).send(message); + } catch (err: unknown) { + if (err instanceof HttpError) { + console.error("callback error:", err); + const { message, statusCode = 500 } = err; + return res.status(statusCode).send(message); + } + return res.status(500).send("Unknown error"); } } diff --git a/apps/web/pages/api/book/confirm.ts b/apps/web/pages/api/book/confirm.ts index 4e06c67e..8b8261dd 100644 --- a/apps/web/pages/api/book/confirm.ts +++ b/apps/web/pages/api/book/confirm.ts @@ -1,6 +1,5 @@ import { Prisma, User, Booking, SchedulingType, BookingStatus } from "@prisma/client"; import type { NextApiRequest, NextApiResponse } from "next"; -import rrule from "rrule"; import EventManager from "@calcom/core/EventManager"; import logger from "@calcom/lib/logger"; diff --git a/apps/web/pages/api/integrations/[...args].ts b/apps/web/pages/api/integrations/[...args].ts index 27ebf58b..a03b5a96 100644 --- a/apps/web/pages/api/integrations/[...args].ts +++ b/apps/web/pages/api/integrations/[...args].ts @@ -25,7 +25,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { if (typeof handler !== "function") throw new HttpError({ statusCode: 404, message: `API handler not found` }); - const response = await handler(req, res); + await handler(req, res); return res.status(200); } catch (error) { diff --git a/apps/web/pages/api/upgrade.ts b/apps/web/pages/api/upgrade.ts index 9baddda3..6e79f197 100644 --- a/apps/web/pages/api/upgrade.ts +++ b/apps/web/pages/api/upgrade.ts @@ -1,4 +1,3 @@ -import { Prisma } from "@prisma/client"; import type { NextApiRequest, NextApiResponse } from "next"; import { getStripeCustomerId } from "@calcom/stripe/customer"; diff --git a/apps/web/pages/availability/troubleshoot.tsx b/apps/web/pages/availability/troubleshoot.tsx index 9d6330cc..1ec71249 100644 --- a/apps/web/pages/availability/troubleshoot.tsx +++ b/apps/web/pages/availability/troubleshoot.tsx @@ -2,8 +2,9 @@ import dayjs, { Dayjs } from "dayjs"; import utc from "dayjs/plugin/utc"; import { useEffect, useState } from "react"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; + import { QueryCell } from "@lib/QueryCell"; -import { useLocale } from "@lib/hooks/useLocale"; import { inferQueryOutput, trpc } from "@lib/trpc"; import Loader from "@components/Loader"; @@ -20,10 +21,10 @@ const AvailabilityView = ({ user }: { user: User }) => { const [selectedDate, setSelectedDate] = useState(dayjs()); function convertMinsToHrsMins(mins: number) { - let h = Math.floor(mins / 60); - let m = mins % 60; - let hs = h < 10 ? "0" + h : h; - let ms = m < 10 ? "0" + m : m; + const h = Math.floor(mins / 60); + const m = mins % 60; + const hs = h < 10 ? "0" + h : h; + const ms = m < 10 ? "0" + m : m; return `${hs}:${ms}`; } diff --git a/apps/web/pages/d/[link]/[slug].tsx b/apps/web/pages/d/[link]/[slug].tsx index 49aa8136..4e779996 100644 --- a/apps/web/pages/d/[link]/[slug].tsx +++ b/apps/web/pages/d/[link]/[slug].tsx @@ -157,7 +157,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => eventTypeObject.schedule = null; eventTypeObject.availability = []; - let booking: GetBookingType | null = null; + const booking: GetBookingType | null = null; const profile = { name: user.name || user.username, diff --git a/apps/web/pages/d/[link]/book.tsx b/apps/web/pages/d/[link]/book.tsx index 48b0eafc..ed99d6c2 100644 --- a/apps/web/pages/d/[link]/book.tsx +++ b/apps/web/pages/d/[link]/book.tsx @@ -30,7 +30,6 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { const ssr = await ssrInit(context); const link = asStringOrThrow(context.query.link as string); const recurringEventCountQuery = asStringOrNull(context.query.count); - const slug = context.query.slug as string; const eventTypeSelect = Prisma.validator()({ id: true, diff --git a/apps/web/pages/event-types/[type].tsx b/apps/web/pages/event-types/[type].tsx index dae70aa5..c08f27de 100644 --- a/apps/web/pages/event-types/[type].tsx +++ b/apps/web/pages/event-types/[type].tsx @@ -21,13 +21,13 @@ import classNames from "classnames"; import dayjs from "dayjs"; import timezone from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; -import { isValidPhoneNumber, parsePhoneNumber } from "libphonenumber-js"; +import { isValidPhoneNumber } from "libphonenumber-js"; import { GetServerSidePropsContext } from "next"; import { useRouter } from "next/router"; import React, { useEffect, useState } from "react"; import { Controller, Noop, useForm, UseFormReturn } from "react-hook-form"; import { FormattedNumber, IntlProvider } from "react-intl"; -import short, { generate } from "short-uuid"; +import short from "short-uuid"; import { JSONObject } from "superjson/dist/types"; import { v5 as uuidv5 } from "uuid"; import { z } from "zod"; @@ -101,7 +101,44 @@ type OptionTypeBase = { disabled?: boolean; }; -const SuccessRedirectEdit = >({ +export type FormValues = { + title: string; + eventTitle: string; + smartContractAddress: string; + eventName: string; + slug: string; + length: number; + description: string; + disableGuests: boolean; + requiresConfirmation: boolean; + recurringEvent: RecurringEvent; + schedulingType: SchedulingType | null; + price: number; + currency: string; + hidden: boolean; + hideCalendarNotes: boolean; + hashedLink: string | undefined; + locations: { type: LocationType; address?: string; link?: string; hostPhoneNumber?: string }[]; + customInputs: EventTypeCustomInput[]; + users: string[]; + schedule: number; + periodType: PeriodType; + periodDays: number; + periodCountCalendarDays: "1" | "0"; + periodDates: { startDate: Date; endDate: Date }; + minimumBookingNotice: number; + beforeBufferTime: number; + afterBufferTime: number; + slotInterval: number | null; + destinationCalendar: { + integration: string; + externalId: string; + }; + successRedirectUrl: string; + giphyThankYouPage: string; +}; + +const SuccessRedirectEdit = >({ eventType, formMethods, }: { @@ -524,42 +561,7 @@ const EventTypePage = (props: inferSSRProps) => { avatar: `${process.env.NEXT_PUBLIC_WEBSITE_URL}/${username}/avatar.png`, }); - const formMethods = useForm<{ - title: string; - eventTitle: string; - smartContractAddress: string; - eventName: string; - slug: string; - length: number; - description: string; - disableGuests: boolean; - requiresConfirmation: boolean; - recurringEvent: RecurringEvent; - schedulingType: SchedulingType | null; - price: number; - currency: string; - hidden: boolean; - hideCalendarNotes: boolean; - hashedLink: string | undefined; - locations: { type: LocationType; address?: string; link?: string; hostPhoneNumber?: string }[]; - customInputs: EventTypeCustomInput[]; - users: string[]; - schedule: number; - periodType: PeriodType; - periodDays: number; - periodCountCalendarDays: "1" | "0"; - periodDates: { startDate: Date; endDate: Date }; - minimumBookingNotice: number; - beforeBufferTime: number; - afterBufferTime: number; - slotInterval: number | null; - destinationCalendar: { - integration: string; - externalId: string; - }; - successRedirectUrl: string; - giphyThankYouPage: string; - }>({ + const formMethods = useForm({ defaultValues: { locations: eventType.locations || [], recurringEvent: eventType.recurringEvent || {}, diff --git a/apps/web/pages/event-types/index.tsx b/apps/web/pages/event-types/index.tsx index 8b1b077a..6c3dd0bd 100644 --- a/apps/web/pages/event-types/index.tsx +++ b/apps/web/pages/event-types/index.tsx @@ -2,22 +2,22 @@ import { CalendarIcon } from "@heroicons/react/outline"; import { ArrowDownIcon, ArrowUpIcon, - DotsHorizontalIcon, - ExternalLinkIcon, - DuplicateIcon, - LinkIcon, - UploadIcon, ClipboardCopyIcon, - TrashIcon, + DotsHorizontalIcon, + DuplicateIcon, + ExternalLinkIcon, + LinkIcon, PencilIcon, - CodeIcon, + TrashIcon, + UploadIcon, + UsersIcon, } from "@heroicons/react/solid"; -import { UsersIcon } from "@heroicons/react/solid"; +import { UserPlan } from "@prisma/client"; import { Trans } from "next-i18next"; import Head from "next/head"; import Link from "next/link"; import { useRouter } from "next/router"; -import React, { Fragment, useEffect, useRef, useState } from "react"; +import React, { Fragment, useEffect, useState } from "react"; import { WEBAPP_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -26,10 +26,10 @@ import { Button } from "@calcom/ui"; import { Alert } from "@calcom/ui/Alert"; import { Dialog, DialogTrigger } from "@calcom/ui/Dialog"; import Dropdown, { - DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, + DropdownMenuTrigger, } from "@calcom/ui/Dropdown"; import { Tooltip } from "@calcom/ui/Tooltip"; @@ -49,13 +49,6 @@ import Avatar from "@components/ui/Avatar"; import AvatarGroup from "@components/ui/AvatarGroup"; import Badge from "@components/ui/Badge"; -type Profiles = inferQueryOutput<"viewer.eventTypes">["profiles"]; - -interface CreateEventTypeProps { - canAddEvents: boolean; - profiles: Profiles; -} - type EventTypeGroups = inferQueryOutput<"viewer.eventTypes">["eventTypeGroups"]; type EventTypeGroupProfile = EventTypeGroups[number]["profile"]; interface EventTypeListHeadingProps { @@ -72,7 +65,7 @@ interface EventTypeListProps { types: EventType[]; } -const Item = ({ type, group, readOnly }: any) => { +const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGroup; readOnly: boolean }) => { const { t } = useLocale(); return ( @@ -135,17 +128,19 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL } utils.cancelQuery(["viewer.eventTypes"]); - utils.setQueryData(["viewer.eventTypes"], (data) => - Object.assign(data, { + utils.setQueryData(["viewer.eventTypes"], (data) => { + // tRPC is very strict with the return signature... + if (!data) + return { eventTypeGroups: [], profiles: [], viewer: { canAddEvents: false, plan: UserPlan.FREE } }; + return { + ...data, eventTypesGroups: [ - data?.eventTypeGroups.slice(0, groupIndex), - Object.assign(group, { - eventTypes: newList, - }), - data?.eventTypeGroups.slice(groupIndex + 1), + ...data.eventTypeGroups.slice(0, groupIndex), + { ...group, eventTypes: newList }, + ...data.eventTypeGroups.slice(groupIndex + 1), ], - }) - ); + }; + }); mutation.mutate({ ids: newList.map((type) => type.id), @@ -528,7 +523,7 @@ const EventTypeListHeading = ({ profile, membershipCount }: EventTypeListHeading ); }; -const CreateFirstEventTypeView = ({ canAddEvents, profiles }: CreateEventTypeProps) => { +const CreateFirstEventTypeView = () => { const { t } = useLocale(); return ( @@ -603,10 +598,8 @@ const EventTypesPage = () => { ))} - {data.eventTypeGroups.length === 0 && ( - - )} - + {data.eventTypeGroups.length === 0 && } + )} /> diff --git a/apps/web/pages/getting-started.tsx b/apps/web/pages/getting-started.tsx index b8313674..807432a8 100644 --- a/apps/web/pages/getting-started.tsx +++ b/apps/web/pages/getting-started.tsx @@ -145,7 +145,6 @@ export default function Onboarding(props: inferSSRProps(null); - const usernameRef = useRef(null); const bioRef = useRef(null); /** End Name */ /** TimeZone */ diff --git a/apps/web/pages/success.tsx b/apps/web/pages/success.tsx index 618806a1..d7e7426b 100644 --- a/apps/web/pages/success.tsx +++ b/apps/web/pages/success.tsx @@ -63,8 +63,9 @@ function RedirectionToast({ url }: { url: string }) { const parsedSuccessUrl = new URL(document.URL); const parsedExternalUrl = new URL(url); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment /* @ts-ignore */ //https://stackoverflow.com/questions/49218765/typescript-and-iterator-type-iterableiteratort-is-not-an-array-type - for (let [name, value] of parsedExternalUrl.searchParams.entries()) { + for (const [name, value] of parsedExternalUrl.searchParams.entries()) { parsedSuccessUrl.searchParams.set(name, value); } @@ -185,8 +186,9 @@ export default function Success(props: SuccessProps) { useEffect(() => { const users = eventType.users; + if (!sdkActionManager) return; // TODO: We should probably make it consistent with Webhook payload. Some data is not available here, as and when requirement comes we can add - sdkActionManager!.fire("bookingSuccessful", { + sdkActionManager.fire("bookingSuccessful", { eventType, date: date.toString(), duration: eventType.length, @@ -476,7 +478,10 @@ export default function Success(props: SuccessProps) {
{ e.preventDefault(); - router.push(`https://cal.com/signup?email=` + (e as any).target.email.value); + const target = e.target as typeof e.target & { + email: { value: string }; + }; + router.push(`https://cal.com/signup?email=${target.email.value}`); }} className="mt-4 flex"> =12.0.0", "@types/node@>=8.1.0": +"@types/node@*", "@types/node@14.17.6", "@types/node@16.9.1", "@types/node@>=12.0.0", "@types/node@>=8.1.0", "@types/node@^12.12.6": version "14.17.6" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.6.tgz#cc61c8361c89e70c468cda464d1fa3dd7e5ebd62" integrity sha512-iBxsxU7eswQDGhlr3AiamBxOssaYxbM+NKXVil8jg9yFXvrfEFbDumLD/2dMTB+zYyg7w+Xjt8yuxfdbUHAtcQ== -"@types/node@16.9.1": - version "16.9.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708" - integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g== - -"@types/node@^12.12.6": - version "12.20.52" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.52.tgz#2fd2dc6bfa185601b15457398d4ba1ef27f81251" - integrity sha512-cfkwWw72849SNYp3Zx0IcIs25vABmFh73xicxhCkTcvtZQeIez15PpwQN8fY3RD7gv1Wrxlc9MEtfMORZDEsGw== - "@types/nodemailer@^6.4.4": version "6.4.4" resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.4.tgz#c265f7e7a51df587597b3a49a023acaf0c741f4b"