diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index e196dae09e..0000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,42 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the -// README at: https://github.com/devcontainers/templates/tree/main/src/docker-outside-of-docker -{ - "name": "Docker outside of Docker", - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/base:bullseye", - - "features": { - "ghcr.io/devcontainers/features/docker-from-docker:1": { - "version": "latest", - "enableNonRootDocker": "true", - "moby": "true" - }, - "ghcr.io/devcontainers/features/node:1": {}, - "ghcr.io/devcontainers-contrib/features/npm-package:1": {}, - "ghcr.io/devcontainers-contrib/features/jest:2": {}, - "ghcr.io/devcontainers-contrib/features/prisma:2": {}, - "ghcr.io/guiyomh/features/vim:0": {} - }, - - // Use this environment variable if you need to bind mount your local source code into a new container. - "remoteEnv": { - "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" - }, - - "hostRequirements": { - "cpus": 4, - "memory": "8gb" - }, - - // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "./deploy/install.sh" - - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // Configure tool-specific properties. - // "customizations": {}, - - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" -} diff --git a/.env.example b/.env.example index 4ddb81f5eb..0a75c7390a 100644 --- a/.env.example +++ b/.env.example @@ -178,4 +178,4 @@ CSP_POLICY= # Vercel Edge Config EDGE_CONFIG= -NEXT_PUBLIC_MINUTES_TO_BOOK=5 # Minutes \ No newline at end of file +NEXT_PUBLIC_MINUTES_TO_BOOK=5 # Minutes diff --git a/.github/workflows/scripts/apply-issue-labels-to-pr.ts b/.github/workflows/scripts/apply-issue-labels-to-pr.ts index 0993c6455f..cfcfb25981 100644 --- a/.github/workflows/scripts/apply-issue-labels-to-pr.ts +++ b/.github/workflows/scripts/apply-issue-labels-to-pr.ts @@ -1,7 +1,6 @@ const https = require("https"); async function applyLabelFromLinkedIssueToPR(pr, token) { - // Get the labels from issues linked to the PR const query = ` query GetLinkedIssues($owner: String!, $repo: String!, $prNumber: Int!) { @@ -38,7 +37,7 @@ async function applyLabelFromLinkedIssueToPR(pr, token) { headers: { "Content-Type": "application/json", "Content-Length": graphqlData.length, - "Authorization": "Bearer " + token, + Authorization: "Bearer " + token, "User-Agent": "Node.js", }, }; @@ -52,14 +51,7 @@ async function applyLabelFromLinkedIssueToPR(pr, token) { responseBody += chunk; }); response.on("end", () => { - resolve( - JSON.parse(responseBody) - ?.data - ?.repository - ?.pullRequest - ?.closingIssuesReferences - ?.nodes - ); + resolve(JSON.parse(responseBody)?.data?.repository?.pullRequest?.closingIssuesReferences?.nodes); }); }); @@ -96,7 +88,7 @@ async function applyLabelFromLinkedIssueToPR(pr, token) { headers: { "Content-Type": "application/json", "Content-Length": labelsData.length, - "Authorization": "Bearer " + token, + Authorization: "Bearer " + token, "User-Agent": "Node.js", }, }; @@ -126,11 +118,9 @@ async function applyLabelFromLinkedIssueToPR(pr, token) { console.log(`Error labelling PR: ${labelResult.message}`); continue; } - + console.log( - `Applied labels: ${labels.join(", ")} to PR #${ - pr.number - } from linked issue #${issue.number}` + `Applied labels: ${labels.join(", ")} to PR #${pr.number} from linked issue #${issue.number}` ); } } diff --git a/.vscode/settings.json b/.vscode/settings.json index 919ae19b36..4c07cd934a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,6 @@ }, "typescript.preferences.importModuleSpecifier": "non-relative", "spellright.language": ["en"], - "spellright.documentTypes": ["markdown", "typescript"], + "spellright.documentTypes": ["markdown", "typescript", "typescriptreact"], "tailwindCSS.experimental.classRegex": [["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]] } diff --git a/apps/storybook/.storybook/main.js b/apps/storybook/.storybook/main.js index 3b7382f1ed..ae653ae06e 100644 --- a/apps/storybook/.storybook/main.js +++ b/apps/storybook/.storybook/main.js @@ -4,6 +4,7 @@ module.exports = { stories: [ "../intro.stories.mdx", "../../../packages/ui/components/**/*.stories.mdx", + "../../../packages/atoms/**/*.stories.mdx", "../../../packages/features/**/*.stories.mdx", "../../../packages/ui/components/**/*.stories.@(js|jsx|ts|tsx)", ], @@ -70,4 +71,5 @@ module.exports = { return config; }, + typescript: { reactDocgen: "react-docgen" }, }; diff --git a/apps/web/components/booking/AvailableTimes.tsx b/apps/web/components/booking/AvailableTimes.tsx index 19cabe0b85..394225ba3a 100644 --- a/apps/web/components/booking/AvailableTimes.tsx +++ b/apps/web/components/booking/AvailableTimes.tsx @@ -11,7 +11,7 @@ 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"; +import type { Slot } from "@calcom/trpc/server/routers/viewer/slots/types"; import { SkeletonContainer, SkeletonText, ToggleGroup } from "@calcom/ui"; import classNames from "@lib/classNames"; diff --git a/apps/web/components/booking/BookingListItem.tsx b/apps/web/components/booking/BookingListItem.tsx index 8e22549663..63a8a9fe2d 100644 --- a/apps/web/components/booking/BookingListItem.tsx +++ b/apps/web/components/booking/BookingListItem.tsx @@ -10,6 +10,7 @@ import "@calcom/dayjs/locales"; import ViewRecordingsDialog from "@calcom/features/ee/video/ViewRecordingsDialog"; import classNames from "@calcom/lib/classNames"; import { formatTime } from "@calcom/lib/date-fns"; +import getPaymentAppData from "@calcom/lib/getPaymentAppData"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { getEveryFreqFor } from "@calcom/lib/recurringStrings"; import type { RouterInputs, RouterOutputs } from "@calcom/trpc/react"; @@ -86,6 +87,8 @@ function BookingListItem(booking: BookingItemProps) { const isTabRecurring = booking.listingStatus === "recurring"; const isTabUnconfirmed = booking.listingStatus === "unconfirmed"; + const paymentAppData = getPaymentAppData(booking.eventType); + const bookingConfirm = async (confirm: boolean) => { let body = { bookingId: booking.id, @@ -277,13 +280,13 @@ function BookingListItem(booking: BookingItemProps) { isOpenDialog={isOpenSetLocationDialog} setShowLocationModal={setIsOpenLocationDialog} /> - {booking.paid && ( + {booking.paid && booking.payment[0] && ( )} {showRecordingsButtons && ( @@ -351,11 +354,15 @@ function BookingListItem(booking: BookingItemProps) { {booking.eventType.team.name} )} - {booking.paid && ( + {booking.paid && !booking.payment[0] ? ( + + {t("error_collecting_card")} + + ) : booking.paid ? ( {booking.payment[0].paymentOption === "HOLD" ? t("card_held") : t("paid")} - )} + ) : null} {recurringDates !== undefined && (
@@ -413,7 +420,7 @@ function BookingListItem(booking: BookingItemProps) { {title} - {!!booking?.eventType?.price && !booking.paid && ( + {paymentAppData.enabled && !booking.paid && booking.payment.length && ( {t("pending_payment")} @@ -455,7 +462,7 @@ function BookingListItem(booking: BookingItemProps) {
)} - {booking.status === "ACCEPTED" && booking.paid && booking?.payment[0]?.paymentOption === "HOLD" && ( + {booking.status === "ACCEPTED" && booking.paid && booking.payment[0]?.paymentOption === "HOLD" && (
@@ -480,12 +487,12 @@ const RecurringBookingsTooltip = ({ booking, recurringDates }: RecurringBookings i18n: { language }, } = useLocale(); const now = new Date(); - const recurringCount = recurringDates.filter((date) => { + const recurringCount = recurringDates.filter((recurringDate) => { return ( - date >= now && + recurringDate >= now && !booking.recurringInfo?.bookings[BookingStatus.CANCELLED] .map((date) => date.toDateString()) - .includes(date.toDateString()) + .includes(recurringDate.toDateString()) ); }).length; diff --git a/apps/web/components/booking/TimeOptions.tsx b/apps/web/components/booking/TimeOptions.tsx index 401497a1b0..e50cab498b 100644 --- a/apps/web/components/booking/TimeOptions.tsx +++ b/apps/web/components/booking/TimeOptions.tsx @@ -29,7 +29,7 @@ const TimeOptions: FC = ({ onSelectTimeZone }) => { classNames={{ singleValue: () => "text-default", dropdownIndicator: () => "text-default", - menu: () => "!w-64 max-w-[90vw]", + menu: () => "!w-64 max-w-[90vw] shadow-dropdown bg-default border-subtle border rounded-md mt-1", }} variant="minimal" value={selectedTimeZone} diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 9ed76360b4..dbfa63657e 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -36,7 +36,7 @@ import type { AvailabilityPageProps } from "../../../pages/[user]/[type]"; import type { DynamicAvailabilityPageProps } from "../../../pages/d/[link]/[slug]"; import type { AvailabilityTeamPageProps } from "../../../pages/team/[slug]/[type]"; -const PoweredByCal = dynamic(() => import("@components/ui/PoweredByCal")); +const PoweredBy = dynamic(() => import("@calcom/ee/components/PoweredBy")); const Toaster = dynamic(() => import("react-hot-toast").then((mod) => mod.Toaster), { ssr: false }); /*const SlotPicker = dynamic(() => import("../SlotPicker").then((mod) => mod.SlotPicker), { @@ -301,7 +301,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => { {/* FIXME: We don't show branding in Embed yet because we need to place branding on top of the main content. Keeping it outside the main content would have visibility issues because outside main content background is transparent */} - {!restProps.isBrandingHidden && !isEmbed && } + {!restProps.isBrandingHidden && !isEmbed && } diff --git a/apps/web/components/booking/pages/BookingPage.tsx b/apps/web/components/booking/pages/BookingPage.tsx index ff2dd7ca07..42e9bb454d 100644 --- a/apps/web/components/booking/pages/BookingPage.tsx +++ b/apps/web/components/booking/pages/BookingPage.tsx @@ -21,6 +21,7 @@ import { useIsBackgroundTransparent, useIsEmbed, } from "@calcom/embed-core/embed-iframe"; +import { createBooking, createRecurringBooking } from "@calcom/features/bookings/lib"; import { getBookingFieldsWithSystemFields, SystemField, @@ -38,6 +39,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import useTheme from "@calcom/lib/hooks/useTheme"; import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery"; import { HttpError } from "@calcom/lib/http-error"; +import { parseDate, parseRecurringDates } from "@calcom/lib/parse-dates"; import { getEveryFreqFor } from "@calcom/lib/recurringStrings"; import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import { TimeFormat } from "@calcom/lib/timeFormat"; @@ -47,9 +49,6 @@ import { AlertTriangle, Calendar, RefreshCw, User } from "@calcom/ui/components/ import { timeZone } from "@lib/clock"; import useRouterQuery from "@lib/hooks/useRouterQuery"; -import createBooking from "@lib/mutations/bookings/create-booking"; -import createRecurringBooking from "@lib/mutations/bookings/create-recurring-booking"; -import { parseRecurringDates, parseDate } from "@lib/parseDate"; import type { Gate, GateState } from "@components/Gates"; import Gates from "@components/Gates"; @@ -433,7 +432,6 @@ const BookingPage = ({ // Calculate the booking date(s) let recurringStrings: string[] = [], recurringDates: Date[] = []; - if (eventType.recurringEvent?.freq && recurringEventCount !== null) { [recurringStrings, recurringDates] = parseRecurringDates( { @@ -443,7 +441,7 @@ const BookingPage = ({ recurringCount: parseInt(recurringEventCount.toString()), selectedTimeFormat: timeFormat, }, - i18n + i18n.language ); } @@ -572,7 +570,7 @@ const BookingPage = ({
{isClientTimezoneAvailable && (rescheduleUid || !eventType.recurringEvent?.freq) && - `${parseDate(date, i18n, timeFormat)}`} + `${parseDate(date, i18n.language, { selectedTimeFormat: timeFormat })}`} {isClientTimezoneAvailable && !rescheduleUid && eventType.recurringEvent?.freq && @@ -602,7 +600,9 @@ const BookingPage = ({ {isClientTimezoneAvailable && typeof booking.startTime === "string" && - parseDate(dayjs(booking.startTime), i18n, timeFormat)} + parseDate(dayjs(booking.startTime), i18n.language, { + selectedTimeFormat: timeFormat, + })}

)} diff --git a/apps/web/components/error/error-page.tsx b/apps/web/components/error/error-page.tsx index b394191129..e3102f230b 100644 --- a/apps/web/components/error/error-page.tsx +++ b/apps/web/components/error/error-page.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { HttpError } from "@lib/core/http/error"; +import { HttpError } from "@calcom/lib/http-error"; type Props = { statusCode?: number | null; diff --git a/apps/web/components/ui/PoweredByCal.tsx b/apps/web/components/ui/PoweredByCal.tsx deleted file mode 100644 index 3825fc11f4..0000000000 --- a/apps/web/components/ui/PoweredByCal.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import Link from "next/link"; - -import { useIsEmbed } from "@calcom/embed-core/embed-iframe"; -import { POWERED_BY_URL } from "@calcom/lib/constants"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; - -const PoweredByCal = () => { - const { t } = useLocale(); - const isEmbed = useIsEmbed(); - return ( -
- - {t("powered_by")}{" "} - Cal.com Logo - Cal.com Logo - -
- ); -}; - -export default PoweredByCal; diff --git a/apps/web/lib/app-providers.tsx b/apps/web/lib/app-providers.tsx index dad9e15d33..f965b6a636 100644 --- a/apps/web/lib/app-providers.tsx +++ b/apps/web/lib/app-providers.tsx @@ -19,6 +19,8 @@ import { MetaProvider } from "@calcom/ui"; import usePublicPage from "@lib/hooks/usePublicPage"; import type { WithNonceProps } from "@lib/withNonce"; +import { useViewerI18n } from "@components/I18nLanguageHandler"; + const I18nextAdapter = appWithTranslation & { children: React.ReactNode }>( ({ children }) => <>{children} ); @@ -46,9 +48,7 @@ const CustomI18nextProvider = (props: AppPropsWithChildren) => { * 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 } = trpc.viewer.public.i18n.useQuery(undefined, { - trpc: { context: { skipBatch: true } }, - }).data ?? { + const { i18n, locale } = useViewerI18n().data ?? { locale: "en", }; diff --git a/apps/web/lib/mutations/bookings/create-booking.ts b/apps/web/lib/mutations/bookings/create-booking.ts deleted file mode 100644 index d0b677adab..0000000000 --- a/apps/web/lib/mutations/bookings/create-booking.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { BookingCreateBody } from "@calcom/prisma/zod-utils"; - -import * as fetch from "@lib/core/http/fetch-wrapper"; -import type { BookingResponse } from "@lib/types/booking"; - -type BookingCreateBodyForMutation = Omit; -const createBooking = async (data: BookingCreateBodyForMutation) => { - const response = await fetch.post("/api/book/event", data); - - return response; -}; - -export default createBooking; diff --git a/apps/web/next.config.js b/apps/web/next.config.js index e3108db4c3..f859499763 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -1,6 +1,7 @@ require("dotenv").config({ path: "../../.env" }); const CopyWebpackPlugin = require("copy-webpack-plugin"); const os = require("os"); +const glob = require("glob"); const { withAxiom } = require("next-axiom"); const { i18n } = require("./next-i18next.config"); @@ -66,6 +67,18 @@ if (process.env.ANALYZE === "true") { } plugins.push(withAxiom); + +/** Needed to rewrite public booking page, gets all static pages but [user] */ +const pages = glob + .sync("pages/**/[^_]*.{tsx,js,ts}", { cwd: __dirname }) + .map((filename) => + filename + .substr(6) + .replace(/(\.tsx|\.js|\.ts)/, "") + .replace(/\/.*/, "") + ) + .filter((v, i, self) => self.indexOf(v) === i && !v.startsWith("[user]")); + /** @type {import("next").NextConfig} */ const nextConfig = { i18n, @@ -198,6 +211,16 @@ const nextConfig = { source: "/embed/embed.js", destination: process.env.NEXT_PUBLIC_EMBED_LIB_URL?, }, */ + { + source: `/:user((?!${pages.join("|")}).*)/:type`, + destination: "/new-booker/:user/:type", + has: [{ type: "cookie", key: "new-booker-enabled" }], + }, + { + source: "/team/:slug/:type", + destination: "/new-booker/team/:slug/:type", + has: [{ type: "cookie", key: "new-booker-enabled" }], + }, ]; }, async headers() { diff --git a/apps/web/package.json b/apps/web/package.json index c97a62366e..2d31d2a7dd 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@calcom/web", - "version": "2.8.8", + "version": "2.8.10", "private": true, "scripts": { "analyze": "ANALYZE=true next build", diff --git a/apps/web/pages/[user].tsx b/apps/web/pages/[user].tsx index bafaacc57c..2ec25163fd 100644 --- a/apps/web/pages/[user].tsx +++ b/apps/web/pages/[user].tsx @@ -161,7 +161,7 @@ export default function User(props: inferSSRProps & E
+ className="bg-default border-subtle dark:bg-muted dark:hover:bg-emphasis hover:bg-muted group relative border-b first:rounded-t-md last:rounded-b-md last:border-b-0"> {/* Don't prefetch till the time we drop the amount of javascript in [user][type] page which is impacting score for [user] page */}
diff --git a/apps/web/pages/[user]/book.tsx b/apps/web/pages/[user]/book.tsx index 1a914ef05d..741f35882d 100644 --- a/apps/web/pages/[user]/book.tsx +++ b/apps/web/pages/[user]/book.tsx @@ -5,6 +5,8 @@ import type { LocationObject } from "@calcom/app-store/locations"; import { privacyFilteredLocations } from "@calcom/app-store/locations"; import { getAppFromSlug } from "@calcom/app-store/utils"; import dayjs from "@calcom/dayjs"; +import getBooking from "@calcom/features/bookings/lib/get-booking"; +import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking"; import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields"; import { parseRecurringEvent } from "@calcom/lib"; import { @@ -13,8 +15,6 @@ import { getGroupName, getUsernameList, } from "@calcom/lib/defaultEvents"; -import getBooking from "@calcom/lib/getBooking"; -import type { GetBookingType } from "@calcom/lib/getBooking"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; import prisma, { bookEventTypeSelect } from "@calcom/prisma"; diff --git a/apps/web/pages/_error.tsx b/apps/web/pages/_error.tsx index 59869c4697..fb0ce29505 100644 --- a/apps/web/pages/_error.tsx +++ b/apps/web/pages/_error.tsx @@ -8,10 +8,9 @@ import NextError from "next/error"; import React from "react"; import { getErrorFromUnknown } from "@calcom/lib/errors"; +import { HttpError } from "@calcom/lib/http-error"; import logger from "@calcom/lib/logger"; -import { HttpError } from "@lib/core/http/error"; - import { ErrorPage } from "@components/error/error-page"; // Adds HttpException to the list of possible error types. diff --git a/apps/web/pages/api/auth/oidc.ts b/apps/web/pages/api/auth/oidc.ts index f8cd8799eb..0e65b2b551 100644 --- a/apps/web/pages/api/auth/oidc.ts +++ b/apps/web/pages/api/auth/oidc.ts @@ -1,8 +1,7 @@ import type { NextApiRequest, NextApiResponse } from "next"; import jackson from "@calcom/features/ee/sso/lib/jackson"; - -import { HttpError } from "@lib/core/http/error"; +import { HttpError } from "@calcom/lib/http-error"; // This is the callback endpoint for the OIDC provider // A team must set this endpoint in the OIDC provider's configuration diff --git a/apps/web/pages/api/auth/saml/authorize.ts b/apps/web/pages/api/auth/saml/authorize.ts index 8d42bec42e..337406cb4f 100644 --- a/apps/web/pages/api/auth/saml/authorize.ts +++ b/apps/web/pages/api/auth/saml/authorize.ts @@ -2,8 +2,7 @@ import type { OAuthReq } from "@boxyhq/saml-jackson"; import type { NextApiRequest, NextApiResponse } from "next"; import jackson from "@calcom/features/ee/sso/lib/jackson"; - -import type { HttpError } from "@lib/core/http/error"; +import type { HttpError } from "@calcom/lib/http-error"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { const { oauthController } = await jackson(); diff --git a/apps/web/pages/api/integrations/[...args].ts b/apps/web/pages/api/integrations/[...args].ts index 228b896125..5b789e8ffd 100644 --- a/apps/web/pages/api/integrations/[...args].ts +++ b/apps/web/pages/api/integrations/[...args].ts @@ -4,12 +4,11 @@ import type { Session } from "next-auth"; import getInstalledAppPath from "@calcom/app-store/_utils/getInstalledAppPath"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; import { deriveAppDictKeyFromType } from "@calcom/lib/deriveAppDictKeyFromType"; +import { HttpError } from "@calcom/lib/http-error"; import { revalidateCalendarCache } from "@calcom/lib/server/revalidateCalendarCache"; import prisma from "@calcom/prisma"; import type { AppDeclarativeHandler, AppHandler } from "@calcom/types/AppHandler"; -import { HttpError } from "@lib/core/http/error"; - const defaultIntegrationAddHandler = async ({ slug, supportsMultipleInstalls, diff --git a/apps/web/pages/api/link.ts b/apps/web/pages/api/link.ts index 9b692d95de..33a9e87ca2 100644 --- a/apps/web/pages/api/link.ts +++ b/apps/web/pages/api/link.ts @@ -7,7 +7,7 @@ import { defaultResponder } from "@calcom/lib/server"; import prisma from "@calcom/prisma"; import { TRPCError } from "@calcom/trpc/server"; import { createContext } from "@calcom/trpc/server/createContext"; -import { viewerRouter } from "@calcom/trpc/server/routers/viewer"; +import { viewerRouter } from "@calcom/trpc/server/routers/viewer/_router"; enum DirectAction { ACCEPT = "accept", @@ -51,7 +51,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { try { /** @see https://trpc.io/docs/server-side-calls */ const ctx = await createContext({ req, res }, sessionGetter); - const caller = viewerRouter.createCaller(ctx); + const caller = viewerRouter.createCaller({ ...ctx, req, res }); + await caller.bookings.confirm({ bookingId: booking.id, recurringEventId: booking.recurringEventId || undefined, diff --git a/apps/web/pages/api/newbooker/[status].tsx b/apps/web/pages/api/newbooker/[status].tsx new file mode 100644 index 0000000000..13bc1db346 --- /dev/null +++ b/apps/web/pages/api/newbooker/[status].tsx @@ -0,0 +1,26 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import { z } from "zod"; + +import { defaultResponder } from "@calcom/lib/server"; + +const newBookerSchema = z.object({ + status: z.enum(["enable", "disable"]), +}); + +/** + * Very basic temporary api route to enable/disable new booker access. + */ +async function handler(req: NextApiRequest, res: NextApiResponse) { + const { status } = newBookerSchema.parse(req.query); + + if (status === "enable") { + const expires = new Date(); + expires.setFullYear(expires.getFullYear() + 1); + res.setHeader("Set-Cookie", `new-booker-enabled=true; path=/; expires=${expires.toUTCString()}`); + } else { + res.setHeader("Set-Cookie", "new-booker-enabled=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"); + } + res.send({ status: 200, body: `Done – ${status}` }); +} + +export default defaultResponder(handler); diff --git a/apps/web/pages/apps/[slug]/index.tsx b/apps/web/pages/apps/[slug]/index.tsx index adc35ee727..c961f547e2 100644 --- a/apps/web/pages/apps/[slug]/index.tsx +++ b/apps/web/pages/apps/[slug]/index.tsx @@ -6,6 +6,7 @@ import path from "path"; import { z } from "zod"; import { getAppWithMetadata } from "@calcom/app-store/_appRegistry"; +import { getAppAssetFullPath } from "@calcom/app-store/getAppAssetFullPath"; import prisma from "@calcom/prisma"; import type { inferSSRProps } from "@lib/types/inferSSRProps"; @@ -108,9 +109,11 @@ export const getStaticProps = async (ctx: GetStaticPropsContext) => { const { content, data } = sourceSchema.parse({ content: result.content, data: result.data }); if (data.items) { data.items = data.items.map((item) => { - if (typeof item === "string" && !item.includes("/api/app-store")) { - // Make relative paths absolute - return `/api/app-store/${appDirname}/${item}`; + if (typeof item === "string") { + return getAppAssetFullPath(item, { + dirName: singleApp.dirName, + isTemplate: singleApp.isTemplate, + }); } return item; }); diff --git a/apps/web/pages/apps/[slug]/setup.tsx b/apps/web/pages/apps/[slug]/setup.tsx index c53b61aea1..ca9b08138b 100644 --- a/apps/web/pages/apps/[slug]/setup.tsx +++ b/apps/web/pages/apps/[slug]/setup.tsx @@ -4,6 +4,7 @@ import { useRouter } from "next/router"; import { AppSetupPage } from "@calcom/app-store/_pages/setup"; import { getStaticProps } from "@calcom/app-store/_pages/setup/_getStaticProps"; +import { HeadSeo } from "@calcom/ui"; import PageWrapper from "@components/PageWrapper"; @@ -25,7 +26,13 @@ export default function SetupInformation(props: InferGetStaticPropsType; + return ( + <> + {/* So that the set up page does not get indexed by search engines */} + + + + ); } SetupInformation.PageWrapper = PageWrapper; diff --git a/apps/web/pages/availability/[schedule].tsx b/apps/web/pages/availability/[schedule].tsx index b019cb9594..aa25e7e84b 100644 --- a/apps/web/pages/availability/[schedule].tsx +++ b/apps/web/pages/availability/[schedule].tsx @@ -10,6 +10,7 @@ import { availabilityAsString } from "@calcom/lib/availability"; import { yyyymmdd } from "@calcom/lib/date-fns"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery"; +import { HttpError } from "@calcom/lib/http-error"; import { trpc } from "@calcom/trpc/react"; import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery"; import type { Schedule as ScheduleType, TimeRange, WorkingHours } from "@calcom/types/schedule"; @@ -35,8 +36,6 @@ import { } from "@calcom/ui"; import { Info, Plus, Trash, MoreHorizontal } from "@calcom/ui/components/icon"; -import { HttpError } from "@lib/core/http/error"; - import PageWrapper from "@components/PageWrapper"; import { SelectSkeletonLoader } from "@components/availability/SkeletonLoader"; import EditableHeading from "@components/ui/EditableHeading"; diff --git a/apps/web/pages/availability/index.tsx b/apps/web/pages/availability/index.tsx index 093992eaf7..8c0cb069d7 100644 --- a/apps/web/pages/availability/index.tsx +++ b/apps/web/pages/availability/index.tsx @@ -3,13 +3,13 @@ import { useAutoAnimate } from "@formkit/auto-animate/react"; import { NewScheduleButton, ScheduleListItem } from "@calcom/features/schedules"; import Shell from "@calcom/features/shell/Shell"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { HttpError } from "@calcom/lib/http-error"; import type { RouterOutputs } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react"; import { EmptyScreen, showToast } from "@calcom/ui"; import { Clock } from "@calcom/ui/components/icon"; import { withQuery } from "@lib/QueryCell"; -import { HttpError } from "@lib/core/http/error"; import PageWrapper from "@components/PageWrapper"; import SkeletonLoader from "@components/availability/SkeletonLoader"; diff --git a/apps/web/pages/booking/[uid].tsx b/apps/web/pages/booking/[uid].tsx index ca02ce8376..20a5bd6141 100644 --- a/apps/web/pages/booking/[uid].tsx +++ b/apps/web/pages/booking/[uid].tsx @@ -24,6 +24,7 @@ import { useIsEmbed, } from "@calcom/embed-core/embed-iframe"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; +import { getBookingWithResponses } from "@calcom/features/bookings/lib/get-booking"; import { SystemField, getBookingFieldsWithSystemFields, @@ -36,7 +37,6 @@ import { formatToLocalizedTimezone, } from "@calcom/lib/date-fns"; import { getDefaultEvent } from "@calcom/lib/defaultEvents"; -import { getBookingWithResponses } from "@calcom/lib/getBooking"; import useGetBrandingColours from "@calcom/lib/getBrandColours"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import useTheme from "@calcom/lib/hooks/useTheme"; diff --git a/apps/web/pages/d/[link]/[slug].tsx b/apps/web/pages/d/[link]/[slug].tsx index 4ab511ec1b..3017918cdb 100644 --- a/apps/web/pages/d/[link]/[slug].tsx +++ b/apps/web/pages/d/[link]/[slug].tsx @@ -3,9 +3,9 @@ import { z } from "zod"; import type { LocationObject } from "@calcom/core/location"; import { privacyFilteredLocations } from "@calcom/core/location"; +import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking"; import { parseRecurringEvent } from "@calcom/lib"; import { getWorkingHours } from "@calcom/lib/availability"; -import type { GetBookingType } from "@calcom/lib/getBooking"; import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; import { availiblityPageEventTypeSelect } from "@calcom/prisma"; import prisma from "@calcom/prisma"; diff --git a/apps/web/pages/event-types/[type]/index.tsx b/apps/web/pages/event-types/[type]/index.tsx index c40404e61d..6786481aed 100644 --- a/apps/web/pages/event-types/[type]/index.tsx +++ b/apps/web/pages/event-types/[type]/index.tsx @@ -136,6 +136,13 @@ const EventTypePage = (props: EventTypeSetupProps) => { const updateMutation = trpc.viewer.eventTypes.update.useMutation({ onSuccess: async () => { + formMethods.setValue( + "children", + formMethods.getValues().children.map((child) => ({ + ...child, + created: true, + })) + ); showToast( t("event_type_updated_successfully", { eventTypeTitle: eventType.title, diff --git a/apps/web/pages/event-types/index.tsx b/apps/web/pages/event-types/index.tsx index e2ec5c7043..0f679ea447 100644 --- a/apps/web/pages/event-types/index.tsx +++ b/apps/web/pages/event-types/index.tsx @@ -17,6 +17,7 @@ import { APP_NAME, CAL_URL, WEBAPP_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import useMediaQuery from "@calcom/lib/hooks/useMediaQuery"; import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery"; +import { HttpError } from "@calcom/lib/http-error"; import type { RouterOutputs } from "@calcom/trpc/react"; import { trpc, TRPCClientError } from "@calcom/trpc/react"; import { @@ -59,7 +60,6 @@ import { } from "@calcom/ui/components/icon"; import { withQuery } from "@lib/QueryCell"; -import { HttpError } from "@lib/core/http/error"; import { EmbedButton, EmbedDialog } from "@components/Embed"; import PageWrapper from "@components/PageWrapper"; diff --git a/apps/web/pages/new-booker/[user]/[type].tsx b/apps/web/pages/new-booker/[user]/[type].tsx new file mode 100644 index 0000000000..6a6db3e0e8 --- /dev/null +++ b/apps/web/pages/new-booker/[user]/[type].tsx @@ -0,0 +1,112 @@ +import type { GetServerSidePropsContext } from "next"; +import { z } from "zod"; + +import { Booker } from "@calcom/atoms"; +import { getBookingByUidOrRescheduleUid } from "@calcom/features/bookings/lib/get-booking"; +import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking"; +import { getUsernameList } from "@calcom/lib/defaultEvents"; +import prisma from "@calcom/prisma"; + +import type { inferSSRProps } from "@lib/types/inferSSRProps"; + +import PageWrapper from "@components/PageWrapper"; + +type PageProps = inferSSRProps; + +export default function Type({ slug, user, booking, away }: PageProps) { + return ( +
+ +
+ ); +} + +Type.PageWrapper = PageWrapper; + +async function getDynamicGroupPageProps(context: GetServerSidePropsContext) { + const { user, type: slug } = paramsSchema.parse(context.params); + const { rescheduleUid } = context.query; + + const { ssgInit } = await import("@server/lib/ssg"); + const ssg = await ssgInit(context); + const usernameList = getUsernameList(user); + + const users = await prisma.user.findMany({ + where: { + username: { + in: usernameList, + }, + }, + select: { + allowDynamicBooking: true, + }, + }); + + if (!users.length) { + return { + notFound: true, + }; + } + + let booking: GetBookingType | null = null; + if (rescheduleUid) { + booking = await getBookingByUidOrRescheduleUid(`${rescheduleUid}`); + } + + return { + props: { + booking, + user, + slug, + away: false, + trpcState: ssg.dehydrate(), + }, + }; +} + +async function getUserPageProps(context: GetServerSidePropsContext) { + const { user: username, type: slug } = paramsSchema.parse(context.params); + const { rescheduleUid } = context.query; + const { ssgInit } = await import("@server/lib/ssg"); + const ssg = await ssgInit(context); + const user = await prisma.user.findUnique({ + where: { + username, + }, + select: { + away: true, + }, + }); + + if (!user) { + return { + notFound: true, + }; + } + + let booking: GetBookingType | null = null; + if (rescheduleUid) { + booking = await getBookingByUidOrRescheduleUid(`${rescheduleUid}`); + } + + return { + props: { + booking, + away: user?.away, + user: username, + slug, + trpcState: ssg.dehydrate(), + }, + }; +} + +const paramsSchema = z.object({ type: z.string(), user: z.string() }); + +// Booker page fetches a tiny bit of data server side, to determine early +// whether the page should show an away state or dynamic booking not allowed. +export const getServerSideProps = async (context: GetServerSidePropsContext) => { + const { user } = paramsSchema.parse(context.params); + const isDynamicGroup = user.includes("+"); + + return isDynamicGroup ? await getDynamicGroupPageProps(context) : await getUserPageProps(context); +}; diff --git a/apps/web/pages/new-booker/team/[slug]/[type].tsx b/apps/web/pages/new-booker/team/[slug]/[type].tsx new file mode 100644 index 0000000000..46a91e6890 --- /dev/null +++ b/apps/web/pages/new-booker/team/[slug]/[type].tsx @@ -0,0 +1,65 @@ +import type { GetServerSidePropsContext } from "next"; +import { z } from "zod"; + +import { Booker } from "@calcom/atoms"; +import { getBookingByUidOrRescheduleUid } from "@calcom/features/bookings/lib/get-booking"; +import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking"; +import prisma from "@calcom/prisma"; + +import type { inferSSRProps } from "@lib/types/inferSSRProps"; + +import PageWrapper from "@components/PageWrapper"; + +type PageProps = inferSSRProps; + +export default function Type({ slug, user, booking, away }: PageProps) { + return ( +
+ +
+ ); +} + +Type.PageWrapper = PageWrapper; + +const paramsSchema = z.object({ type: z.string(), slug: z.string() }); + +// Booker page fetches a tiny bit of data server side: +// 1. Check if team exists, to show 404 +// 2. If rescheduling, get the booking details +export const getServerSideProps = async (context: GetServerSidePropsContext) => { + const { slug: teamSlug, type: meetingSlug } = paramsSchema.parse(context.params); + const { rescheduleUid } = context.query; + const { ssgInit } = await import("@server/lib/ssg"); + const ssg = await ssgInit(context); + + const team = await prisma.team.findFirst({ + where: { + slug: teamSlug, + }, + select: { + id: true, + }, + }); + + if (!team) { + return { + notFound: true, + }; + } + + let booking: GetBookingType | null = null; + if (rescheduleUid) { + booking = await getBookingByUidOrRescheduleUid(`${rescheduleUid}`); + } + + return { + props: { + booking, + away: false, + user: teamSlug, + slug: meetingSlug, + trpcState: ssg.dehydrate(), + }, + }; +}; diff --git a/apps/web/pages/settings/security/password.tsx b/apps/web/pages/settings/security/password.tsx index e57d853e0d..28c0326087 100644 --- a/apps/web/pages/settings/security/password.tsx +++ b/apps/web/pages/settings/security/password.tsx @@ -199,6 +199,7 @@ const PasswordView = () => { color="primary" className="mt-8" type="submit" + onClick={() => formMethods.clearErrors("apiError")} disabled={isDisabled || passwordMutation.isLoading || sessionMutation.isLoading}> {t("update")} diff --git a/apps/web/pages/settings/security/two-factor-auth.tsx b/apps/web/pages/settings/security/two-factor-auth.tsx index 5d8f6373cf..811f9d2d03 100644 --- a/apps/web/pages/settings/security/two-factor-auth.tsx +++ b/apps/web/pages/settings/security/two-factor-auth.tsx @@ -46,10 +46,10 @@ const TwoFactorAuthView = () => { user?.twoFactorEnabled ? setDisableModalOpen(true) : setEnableModalOpen(true) } /> -
+

{t("two_factor_auth")}

- + {user?.twoFactorEnabled ? t("enabled") : t("disabled")}
diff --git a/apps/web/pages/team/[slug].tsx b/apps/web/pages/team/[slug].tsx index e5f1237b63..f38b1b11c4 100644 --- a/apps/web/pages/team/[slug].tsx +++ b/apps/web/pages/team/[slug].tsx @@ -56,12 +56,12 @@ function TeamPage({ team, isUnpublished }: TeamPageProps) { } const EventTypes = () => ( -
    +
      {team.eventTypes.map((type, index) => (
    • @@ -105,7 +105,7 @@ function TeamPage({ team, isUnpublished }: TeamPageProps) { }} />
      -
      +

      {teamName}

      {!isBioEmpty && ( @@ -126,7 +126,7 @@ function TeamPage({ team, isUnpublished }: TeamPageProps) {