diff --git a/apps/web/components/booking/DatePicker.tsx b/apps/web/components/booking/DatePicker.tsx index d4a68ba322..3877ef793c 100644 --- a/apps/web/components/booking/DatePicker.tsx +++ b/apps/web/components/booking/DatePicker.tsx @@ -127,6 +127,8 @@ function DatePicker({ eventLength, minimumBookingNotice, workingHours, + }: Omit & { + browsingDate: Dayjs; } ) => { const date = browsingDate.startOf("day").date(day); @@ -189,7 +191,7 @@ function DatePicker({ batch: 1, name: "DatePicker", length: daysInMonth, - callback: (i: number, isLast) => { + callback: (i: number) => { let day = i + 1; days[daysInitialOffset + i] = { disabled: isDisabledMemoized(day, { diff --git a/apps/web/components/booking/pages/BookingPage.tsx b/apps/web/components/booking/pages/BookingPage.tsx index a6e7cd9bb8..e23d1ba8b4 100644 --- a/apps/web/components/booking/pages/BookingPage.tsx +++ b/apps/web/components/booking/pages/BookingPage.tsx @@ -136,6 +136,7 @@ const BookingPage = ({ eventType, booking, profile }: BookingPageProps) => { const locationLabels = { [LocationType.InPerson]: t("in_person_meeting"), [LocationType.Phone]: t("phone_call"), + [LocationType.Link]: t("link_meeting"), [LocationType.GoogleMeet]: "Google Meet", [LocationType.Zoom]: "Zoom Video", [LocationType.Jitsi]: "Jitsi Meet", diff --git a/apps/web/components/seo/head-seo.tsx b/apps/web/components/seo/head-seo.tsx index ad71e84d3f..6b9c538932 100644 --- a/apps/web/components/seo/head-seo.tsx +++ b/apps/web/components/seo/head-seo.tsx @@ -74,7 +74,7 @@ const constructImage = (name: string, description: string, username: string): st ); }; -export const HeadSeo: React.FC = (props) => { +export const HeadSeo = (props: HeadSeoProps): JSX.Element => { const defaultUrl = getBrowserInfo()?.url; const image = getSeoImage("default"); @@ -113,3 +113,5 @@ export const HeadSeo: React.FC = (props) => return ; }; + +export default HeadSeo; diff --git a/apps/web/components/team/TeamSettings.tsx b/apps/web/components/team/TeamSettings.tsx index c3e9fc3a56..1d46392c04 100644 --- a/apps/web/components/team/TeamSettings.tsx +++ b/apps/web/components/team/TeamSettings.tsx @@ -1,12 +1,13 @@ import { HashtagIcon, InformationCircleIcon, LinkIcon, PhotographIcon } from "@heroicons/react/solid"; import React, { useRef, useState } from "react"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import showToast from "@calcom/lib/notification"; +import { objectKeys } from "@calcom/lib/objectKeys"; import { Alert } from "@calcom/ui/Alert"; import Button from "@calcom/ui/Button"; import { TextField } from "@calcom/ui/form/fields"; -import { useLocale } from "@lib/hooks/useLocale"; import { TeamWithMembers } from "@lib/queries/teams"; import { trpc } from "@lib/trpc"; @@ -54,9 +55,9 @@ export default function TeamSettings(props: Props) { hideBranding: hideBrandingRef.current?.checked, }; // remove unchanged variables - for (const key in variables) { - if (variables[key] === team?.[key]) delete variables[key]; - } + objectKeys(variables).forEach((key) => { + if (variables[key as keyof typeof variables] === team?.[key]) delete variables[key]; + }); mutation.mutate({ id: team.id, ...variables }); } diff --git a/apps/web/components/ui/AvatarSSR.tsx b/apps/web/components/ui/AvatarSSR.tsx index ec2e32d6cd..13c1e80929 100644 --- a/apps/web/components/ui/AvatarSSR.tsx +++ b/apps/web/components/ui/AvatarSSR.tsx @@ -11,7 +11,7 @@ export type AvatarProps = { }; // defaultAvatarSrc from profile.tsx can't be used as it imports crypto -function defaultAvatarSrc({ md5 }) { +function defaultAvatarSrc(md5: string) { return `https://www.gravatar.com/avatar/${md5}?s=160&d=identicon&r=PG`; } @@ -26,7 +26,7 @@ export function AvatarSSR(props: AvatarProps) { if (user.avatar) { imgSrc = user.avatar; } else if (user.emailMd5) { - imgSrc = defaultAvatarSrc({ md5: user.emailMd5 }); + imgSrc = defaultAvatarSrc(user.emailMd5); } return imgSrc ? {alt} : null; } diff --git a/apps/web/components/webhook/WebhookDialogForm.tsx b/apps/web/components/webhook/WebhookDialogForm.tsx index 87f24d13f2..6e44bdac0d 100644 --- a/apps/web/components/webhook/WebhookDialogForm.tsx +++ b/apps/web/components/webhook/WebhookDialogForm.tsx @@ -1,13 +1,13 @@ import { useState } from "react"; import { Controller, useForm } from "react-hook-form"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import showToast from "@calcom/lib/notification"; import Button from "@calcom/ui/Button"; import { DialogFooter } from "@calcom/ui/Dialog"; import Switch from "@calcom/ui/Switch"; import { FieldsetLegend, Form, InputGroupBox, TextArea, TextField } from "@calcom/ui/form/fields"; -import { useLocale } from "@lib/hooks/useLocale"; import { trpc } from "@lib/trpc"; import { WEBHOOK_TRIGGER_EVENTS } from "@lib/webhooks/constants"; import customTemplate, { hasTemplateIntegration } from "@lib/webhooks/integrationTemplate"; @@ -22,13 +22,6 @@ export default function WebhookDialogForm(props: { }) { const { t } = useLocale(); const utils = trpc.useContext(); - const handleSubscriberUrlChange = (e) => { - form.setValue("subscriberUrl", e.target.value); - if (hasTemplateIntegration({ url: e.target.value })) { - setUseCustomPayloadTemplate(true); - form.setValue("payloadTemplate", customTemplate({ url: e.target.value })); - } - }; const { defaultValues = { id: "", @@ -88,7 +81,13 @@ export default function WebhookDialogForm(props: { {...form.register("subscriberUrl")} required type="url" - onChange={handleSubscriberUrlChange} + onChange={(e) => { + form.setValue("subscriberUrl", e.target.value); + if (hasTemplateIntegration({ url: e.target.value })) { + setUseCustomPayloadTemplate(true); + form.setValue("payloadTemplate", customTemplate({ url: e.target.value })); + } + }} />
diff --git a/apps/web/lib/doWorkAsync.ts b/apps/web/lib/doWorkAsync.ts index 77ac24afce..9033db4c55 100644 --- a/apps/web/lib/doWorkAsync.ts +++ b/apps/web/lib/doWorkAsync.ts @@ -1,4 +1,4 @@ -const data = {}; +const data: Record = {}; /** * Starts an iteration from `0` to `length - 1` with batch size `batch` * diff --git a/apps/web/next.config.js b/apps/web/next.config.js index c1a3218d39..02ebea1769 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -65,13 +65,6 @@ plugins.push(withTM); /** @type {import("next").NextConfig} */ const nextConfig = { i18n, - eslint: { - // This allows production builds to successfully complete even if the project has ESLint errors. - ignoreDuringBuilds: true, - }, - typescript: { - ignoreBuildErrors: true, - }, webpack: (config) => { config.resolve.fallback = { ...config.resolve.fallback, // if you miss it, all the other options in fallback, specified diff --git a/apps/web/package.json b/apps/web/package.json index 02b0328ba9..2e604045f8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -90,7 +90,7 @@ "react-digit-input": "^2.1.0", "react-dom": "^17.0.2", "react-easy-crop": "^3.5.2", - "react-hook-form": "^7.20.4", + "react-hook-form": "^7.29.0", "react-hot-toast": "^2.1.0", "react-intl": "^5.22.0", "react-live-chat-loader": "^2.7.3", @@ -108,7 +108,7 @@ "superjson": "1.8.1", "uuid": "^8.3.2", "web3": "^1.6.1", - "zod": "^3.8.2" + "zod": "^3.14.3" }, "devDependencies": { "@calcom/config": "*", diff --git a/apps/web/pages/[user].tsx b/apps/web/pages/[user].tsx index 6e0358be0c..912f725ed2 100644 --- a/apps/web/pages/[user].tsx +++ b/apps/web/pages/[user].tsx @@ -21,7 +21,7 @@ import { AvatarSSR } from "@components/ui/AvatarSSR"; import { ssrInit } from "@server/lib/ssr"; const EventTypeDescription = dynamic(() => import("@components/eventtype/EventTypeDescription")); -const HeadSeo = dynamic(() => import("@components/seo/head-seo").then((mod) => mod.HeadSeo)); +const HeadSeo = dynamic(() => import("@components/seo/head-seo")); const CryptoSection = dynamic(() => import("../ee/components/web3/CryptoSection")); interface EvtsToVerify { diff --git a/apps/web/pages/api/integrations/[...args].ts b/apps/web/pages/api/integrations/[...args].ts index 8edfec1286..2da5df167b 100644 --- a/apps/web/pages/api/integrations/[...args].ts +++ b/apps/web/pages/api/integrations/[...args].ts @@ -1,4 +1,4 @@ -import { NextApiRequest, NextApiResponse } from "next"; +import { NextApiHandler, NextApiRequest, NextApiResponse } from "next"; import appStore from "@calcom/app-store"; @@ -26,7 +26,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { try { // TODO: Find a way to dynamically import these modules // const app = (await import(`@calcom/${appName}`)).default; - const handler = appStore[appName].api[apiEndpoint]; + const app = appStore[appName as keyof typeof appStore]; + if (!(app && "api" in app && apiEndpoint in app.api)) + throw new HttpError({ statusCode: 404, message: `API handler not found` }); + + const handler = app.api[apiEndpoint as keyof typeof app.api] as NextApiHandler; + if (typeof handler !== "function") throw new HttpError({ statusCode: 404, message: `API handler not found` }); diff --git a/apps/web/pages/apps/[slug].tsx b/apps/web/pages/apps/[slug].tsx index 6cc30ed52b..fc5d609656 100644 --- a/apps/web/pages/apps/[slug].tsx +++ b/apps/web/pages/apps/[slug].tsx @@ -16,13 +16,15 @@ import App from "@components/App"; import Slider from "@components/apps/Slider"; const components = { - a: ({ href, ...otherProps }) => ( + a: ({ href = "", ...otherProps }: JSX.IntrinsicElements["a"]) => ( ), - img: ({ src, alt = "", ...rest }) => {alt}, - Slider: ({ items }) => { + img: ({ src = "", alt = "", placeholder, ...rest }: JSX.IntrinsicElements["img"]) => ( + {alt} + ), + Slider: ({ items }: { items: string[] }) => { const isTabletAndUp = useMediaQuery("(min-width: 960px)"); return ( diff --git a/apps/web/pages/event-types/[type].tsx b/apps/web/pages/event-types/[type].tsx index 202c48fe62..0ac4511439 100644 --- a/apps/web/pages/event-types/[type].tsx +++ b/apps/web/pages/event-types/[type].tsx @@ -23,7 +23,7 @@ import utc from "dayjs/plugin/utc"; import { GetServerSidePropsContext } from "next"; import { useRouter } from "next/router"; import React, { useEffect, useState } from "react"; -import { Controller, useForm } from "react-hook-form"; +import { Controller, Noop, useForm } from "react-hook-form"; import { FormattedNumber, IntlProvider } from "react-intl"; import Select, { Props as SelectProps } from "react-select"; import { JSONObject } from "superjson/dist/types"; @@ -86,7 +86,21 @@ type OptionTypeBase = { disabled?: boolean; }; -const AvailabilitySelect = ({ className, ...props }: SelectProps) => { +type AvailabilityOption = { + label: string; + value: number; +}; + +const AvailabilitySelect = ({ + className = "", + ...props +}: { + className?: string; + name: string; + value: number; + onBlur: Noop; + onChange: (value: AvailabilityOption | null) => void; +}) => { const query = trpc.useQuery(["viewer.availability.list"]); return ( @@ -105,9 +119,9 @@ const AvailabilitySelect = ({ className, ...props }: SelectProps) => { ); return (