diff --git a/apps/web/abTest/middlewareFactory.ts b/apps/web/abTest/middlewareFactory.ts index ef5c93ebc7..b7c709708f 100644 --- a/apps/web/abTest/middlewareFactory.ts +++ b/apps/web/abTest/middlewareFactory.ts @@ -3,6 +3,8 @@ import type { NextMiddleware, NextRequest } from "next/server"; import { NextResponse, URLPattern } from "next/server"; import z from "zod"; +import { FUTURE_ROUTES_ENABLED_COOKIE_NAME, FUTURE_ROUTES_OVERRIDE_COOKIE_NAME } from "@calcom/lib/constants"; + const ROUTES: [URLPattern, boolean][] = [ ["/event-types", process.env.APP_ROUTER_EVENT_TYPES_ENABLED === "1"] as const, ["/settings/admin/:path*", process.env.APP_ROUTER_SETTINGS_ADMIN_ENABLED === "1"] as const, @@ -25,9 +27,6 @@ const ROUTES: [URLPattern, boolean][] = [ enabled, ]); -const FUTURE_ROUTES_OVERRIDE_COOKIE_NAME = "x-calcom-future-routes-override"; -const FUTURE_ROUTES_ENABLED_COOKIE_NAME = "x-calcom-future-routes-enabled"; - const bucketSchema = z.union([z.literal("legacy"), z.literal("future")]); export const abTestMiddlewareFactory = diff --git a/apps/web/pages/api/future-opt-in.ts b/apps/web/pages/api/future-opt-in.ts new file mode 100644 index 0000000000..3d127d0296 --- /dev/null +++ b/apps/web/pages/api/future-opt-in.ts @@ -0,0 +1,39 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; +import { FUTURE_ROUTES_OVERRIDE_COOKIE_NAME as COOKIE_NAME } from "@calcom/lib/constants"; +import { defaultHandler, defaultResponder } from "@calcom/lib/server"; + +async function handler(req: NextApiRequest, res: NextApiResponse): Promise { + const session = await getServerSession({ req, res }); + + if (!session || !session.user || !session.user.email) { + res.status(401).json({ message: "Not authenticated" }); + return; + } + + let redirectUrl = "/"; + + // We take you back where you came from if possible + if (typeof req.headers["referer"] === "string") redirectUrl = req.headers["referer"]; + + /* Only admins can opt-in to future routes for now */ + if (session.user.role !== "ADMIN") { + res.redirect(redirectUrl); + return; + } + + // If has the cookie, Opt-out of V2 + if (COOKIE_NAME in req.cookies && req.cookies[COOKIE_NAME] === "1") { + res.setHeader("Set-Cookie", `${COOKIE_NAME}=0; Max-Age=0; Path=/`); + } else { + /* Opt-int to V2 */ + res.setHeader("Set-Cookie", `${COOKIE_NAME}=1; Path=/`); + } + + res.redirect(redirectUrl); +} + +export default defaultHandler({ + GET: Promise.resolve({ default: defaultResponder(handler) }), +}); diff --git a/packages/features/shell/Shell.tsx b/packages/features/shell/Shell.tsx index 538f9e234c..e620ce68b1 100644 --- a/packages/features/shell/Shell.tsx +++ b/packages/features/shell/Shell.tsx @@ -29,6 +29,7 @@ import AdminPasswordBanner, { import CalendarCredentialBanner, { type CalendarCredentialBannerProps, } from "@calcom/features/users/components/CalendarCredentialBanner"; +import UserV2OptInBanner from "@calcom/features/users/components/UserV2OptInBanner"; import VerifyEmailBanner, { type VerifyEmailBannerProps, } from "@calcom/features/users/components/VerifyEmailBanner"; @@ -249,6 +250,7 @@ const Layout = (props: LayoutProps) => {
{banners && (
+ {Object.keys(banners).map((key) => { if (key === "teamUpgradeBanner") { const Banner = BannerComponent[key]; diff --git a/packages/features/users/components/UserV2OptInBanner.tsx b/packages/features/users/components/UserV2OptInBanner.tsx index 7658574443..f6b9356633 100644 --- a/packages/features/users/components/UserV2OptInBanner.tsx +++ b/packages/features/users/components/UserV2OptInBanner.tsx @@ -1,30 +1,48 @@ -import { APP_NAME } from "@calcom/lib/constants"; +import { useSession } from "next-auth/react"; +import { + APP_NAME, + FUTURE_ROUTES_OVERRIDE_COOKIE_NAME as COOKIE_NAME, + IS_CALCOM, +} from "@calcom/lib/constants"; +import { TopBanner } from "@calcom/ui"; + +/** Repurposing this component so we can opt-in and out from app router */ function UserV2OptInBanner() { + const session = useSession(); + // Only show on client-side if (typeof document === "undefined") return null; + // Only Admins can opt-in for now + if (session.data?.user.role !== "ADMIN") return null; + // Only Cal.com ADMINs can opt-in for now, also show on dev + if (process.env.NODE_ENV === "production" && !IS_CALCOM) return null; - const hasV2OptInCookie = document.cookie.includes("calcom-v2-early-access=1"); + const hasV2OptInCookie = document.cookie.includes(`${COOKIE_NAME}=1`); if (hasV2OptInCookie) return ( -

- You're using the new version of {APP_NAME}.{" "} - - Go back - - . -

+ + Go back + + } + /> ); return ( -

- Want to try the new version of {APP_NAME}?{" "} - - Opt-in to our v2.0 beta - - . -

+ + Opt-in to future routes + + } + /> ); } diff --git a/packages/lib/constants.ts b/packages/lib/constants.ts index e6087bcc33..70a6a2ba99 100644 --- a/packages/lib/constants.ts +++ b/packages/lib/constants.ts @@ -126,6 +126,8 @@ export const IS_PREMIUM_USERNAME_ENABLED = export const MAX_NB_INVITES = 100; export const URL_PROTOCOL_REGEX = /(^\w+:|^)\/\//; +export const FUTURE_ROUTES_OVERRIDE_COOKIE_NAME = "x-calcom-future-routes-override"; +export const FUTURE_ROUTES_ENABLED_COOKIE_NAME = "x-calcom-future-routes-enabled"; export const IS_VISUAL_REGRESSION_TESTING = Boolean(globalThis.window?.Meticulous?.isRunningAsTest);