diff --git a/apps/web/app/future/auth/error/page.tsx b/apps/web/app/future/auth/error/page.tsx
new file mode 100644
index 00000000000000..25b885e454b553
--- /dev/null
+++ b/apps/web/app/future/auth/error/page.tsx
@@ -0,0 +1,19 @@
+import { withAppDirSsg } from "app/WithAppDirSsg";
+import { _generateMetadata } from "app/_utils";
+import { WithLayout } from "app/layoutHOC";
+
+import { getStaticProps } from "@server/lib/auth/error/getStaticProps";
+
+import Page from "~/auth/error/error-view";
+
+export const generateMetadata = async () => {
+ return await _generateMetadata(
+ () => "Error",
+ () => ""
+ );
+};
+
+const getData = withAppDirSsg(getStaticProps);
+
+export default WithLayout({ getData, Page, getLayout: null })<"P">;
+export const dynamic = "force-static";
diff --git a/apps/web/app/future/auth/oauth2/authorize/page.tsx b/apps/web/app/future/auth/oauth2/authorize/page.tsx
new file mode 100644
index 00000000000000..3fa8dca2358437
--- /dev/null
+++ b/apps/web/app/future/auth/oauth2/authorize/page.tsx
@@ -0,0 +1,16 @@
+import { _generateMetadata } from "app/_utils";
+import { WithLayout } from "app/layoutHOC";
+
+import Page from "~/auth/oauth2/authorize-view";
+
+export const generateMetadata = async () => {
+ return await _generateMetadata(
+ () => "Authorize",
+ () => ""
+ );
+};
+
+export default WithLayout({
+ getLayout: null,
+ Page,
+});
diff --git a/apps/web/app/future/auth/platform/authorize/page.tsx b/apps/web/app/future/auth/platform/authorize/page.tsx
new file mode 100644
index 00000000000000..4f263ae4c03d54
--- /dev/null
+++ b/apps/web/app/future/auth/platform/authorize/page.tsx
@@ -0,0 +1,16 @@
+import { _generateMetadata } from "app/_utils";
+import { WithLayout } from "app/layoutHOC";
+
+import Page from "~/auth/platform/authorize-view";
+
+export const generateMetadata = async () => {
+ return await _generateMetadata(
+ () => "Authorize",
+ () => ""
+ );
+};
+
+export default WithLayout({
+ getLayout: null,
+ Page,
+});
diff --git a/apps/web/lib/metadata.ts b/apps/web/lib/metadata.ts
index b3b576ce6d4d7f..bf98f6560c8831 100644
--- a/apps/web/lib/metadata.ts
+++ b/apps/web/lib/metadata.ts
@@ -67,10 +67,11 @@ export const prepareRootMetadata = (recipe: RootMetadataRecipe): Metadata => ({
});
export const preparePageMetadata = (recipe: PageMetadataRecipe): Metadata => {
+ const { title, description } = recipe;
const titleSuffix = `| ${APP_NAME}`;
- const { description } = recipe;
+
return {
- title: recipe.title.includes(titleSuffix) ? recipe.title : `${recipe.title} ${titleSuffix}`,
+ title: title.length === 0 ? APP_NAME : title.includes(titleSuffix) ? title : `${title} ${titleSuffix}`,
description,
alternates: {
canonical: recipe.canonical,
@@ -80,7 +81,7 @@ export const preparePageMetadata = (recipe: PageMetadataRecipe): Metadata => {
url: recipe.canonical,
type: "website",
siteName: recipe.siteName,
- title: recipe.title,
+ title,
images: [recipe.image],
},
metadataBase: recipe.metadataBase,
diff --git a/apps/web/modules/auth/error/error-view.tsx b/apps/web/modules/auth/error/error-view.tsx
new file mode 100644
index 00000000000000..2d034a53a0d3d5
--- /dev/null
+++ b/apps/web/modules/auth/error/error-view.tsx
@@ -0,0 +1,42 @@
+"use client";
+
+import Link from "next/link";
+import { useSearchParams } from "next/navigation";
+import z from "zod";
+
+import { useLocale } from "@calcom/lib/hooks/useLocale";
+import { Button, Icon } from "@calcom/ui";
+
+import AuthContainer from "@components/ui/AuthContainer";
+
+import type { PageProps } from "@server/lib/auth/error/getStaticProps";
+
+const querySchema = z.object({
+ error: z.string().optional(),
+});
+
+export default function Error(props: PageProps) {
+ const { t } = useLocale();
+ const searchParams = useSearchParams();
+ const { error } = querySchema.parse({ error: searchParams?.get("error") || undefined });
+ const errorMsg = error || t("error_during_login");
+ return (
+
+
+
+
+
+
+
+ {errorMsg}
+
+
+
+
+
+ {t("go_back_login")}
+
+
+
+ );
+}
diff --git a/apps/web/modules/auth/oauth2/authorize-view.tsx b/apps/web/modules/auth/oauth2/authorize-view.tsx
new file mode 100644
index 00000000000000..61efe7eec78f1e
--- /dev/null
+++ b/apps/web/modules/auth/oauth2/authorize-view.tsx
@@ -0,0 +1,177 @@
+"use client";
+
+/* eslint-disable react-hooks/exhaustive-deps */
+import { useSession } from "next-auth/react";
+import { useRouter } from "next/navigation";
+import { useState, useEffect } from "react";
+
+import { APP_NAME } from "@calcom/lib/constants";
+import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
+import { useLocale } from "@calcom/lib/hooks/useLocale";
+import { trpc } from "@calcom/trpc/react";
+import { Avatar, Button, Icon, Select } from "@calcom/ui";
+
+export default function Authorize() {
+ const { t } = useLocale();
+ const { status } = useSession();
+
+ const router = useRouter();
+ const searchParams = useCompatSearchParams();
+
+ const client_id = (searchParams?.get("client_id") as string) || "";
+ const state = searchParams?.get("state") as string;
+ const scope = searchParams?.get("scope") as string;
+
+ const queryString = searchParams?.toString();
+
+ const [selectedAccount, setSelectedAccount] = useState<{ value: string; label: string } | null>();
+ const scopes = scope ? scope.toString().split(",") : [];
+
+ const { data: client, isPending: isPendingGetClient } = trpc.viewer.oAuth.getClient.useQuery(
+ {
+ clientId: client_id as string,
+ },
+ {
+ enabled: status !== "loading",
+ }
+ );
+
+ const { data, isPending: isPendingProfiles } = trpc.viewer.teamsAndUserProfilesQuery.useQuery();
+
+ const generateAuthCodeMutation = trpc.viewer.oAuth.generateAuthCode.useMutation({
+ onSuccess: (data) => {
+ window.location.href = `${client?.redirectUri}?code=${data.authorizationCode}&state=${state}`;
+ },
+ });
+
+ const mappedProfiles = data
+ ? data
+ .filter((profile) => !profile.readOnly)
+ .map((profile) => ({
+ label: profile.name || profile.slug || "",
+ value: profile.slug || "",
+ }))
+ : [];
+
+ useEffect(() => {
+ if (mappedProfiles.length > 0) {
+ setSelectedAccount(mappedProfiles[0]);
+ }
+ }, [isPendingProfiles]);
+
+ useEffect(() => {
+ if (status === "unauthenticated") {
+ const urlSearchParams = new URLSearchParams({
+ callbackUrl: `auth/oauth2/authorize?${queryString}`,
+ });
+ router.replace(`/auth/login?${urlSearchParams.toString()}`);
+ }
+ }, [status]);
+
+ const isPending = isPendingGetClient || isPendingProfiles || status !== "authenticated";
+
+ if (isPending) {
+ return <>>;
+ }
+
+ if (!client) {
+ return
{t("unauthorized")}
;
+ }
+
+ return (
+
+
+
+
}
+ className="items-center"
+ imageSrc={client.logo}
+ size="lg"
+ />
+
+
+
+
+
+
+
+
+
+ {t("access_cal_account", { clientName: client.name, appName: APP_NAME })}
+
+
{t("select_account_team")}
+
{
+ setSelectedAccount(value);
+ }}
+ className="w-52"
+ defaultValue={selectedAccount || mappedProfiles[0]}
+ options={mappedProfiles}
+ />
+ {t("allow_client_to", { clientName: client.name })}
+
+
+ ✓ {" "}
+ {t("associate_with_cal_account", { clientName: client.name })}
+
+
+ ✓ {t("see_personal_info")}
+
+
+ ✓ {t("see_primary_email_address")}
+
+
+ ✓ {t("connect_installed_apps")}
+
+
+ ✓ {t("access_event_type")}
+
+
+ ✓ {t("access_availability")}
+
+
+ ✓ {t("access_bookings")}
+
+
+
+
+
+
+
+
+ {t("allow_client_to_do", { clientName: client.name })}
+
+
{t("oauth_access_information", { appName: APP_NAME })}
{" "}
+
+
+
+
+ {
+ window.location.href = `${client.redirectUri}`;
+ }}>
+ {t("go_back")}
+
+ {
+ generateAuthCodeMutation.mutate({
+ clientId: client_id as string,
+ scopes,
+ teamSlug: selectedAccount?.value.startsWith("team/")
+ ? selectedAccount?.value.substring(5)
+ : undefined, // team account starts with /team/
+ });
+ }}
+ data-testid="allow-button">
+ {t("allow")}
+
+
+
+
+ );
+}
diff --git a/apps/web/modules/auth/platform/authorize-view.tsx b/apps/web/modules/auth/platform/authorize-view.tsx
new file mode 100644
index 00000000000000..6a5739d2a7067a
--- /dev/null
+++ b/apps/web/modules/auth/platform/authorize-view.tsx
@@ -0,0 +1,126 @@
+"use client";
+
+import { useRouter } from "next/navigation";
+
+import { APP_NAME } from "@calcom/lib/constants";
+import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
+import { useLocale } from "@calcom/lib/hooks/useLocale";
+import { PERMISSIONS_GROUPED_MAP } from "@calcom/platform-constants/permissions";
+import { Avatar, Button, Icon } from "@calcom/ui";
+
+import { hasPermission } from "../../../../../packages/platform/utils/permissions";
+
+export default function Authorize() {
+ const { t } = useLocale();
+ const router = useRouter();
+
+ const searchParams = useCompatSearchParams();
+ const queryString = searchParams?.toString();
+
+ // const { isLoading, error, data: client } = useOAuthClient(queryString);
+
+ const client: {
+ name: string;
+ logo?: string;
+ redirect_uris: string[];
+ permissions: number;
+ } = {
+ name: "Acme.com",
+ redirect_uris: ["", ""],
+ permissions: 7,
+ };
+
+ console.log("These are the search params:", queryString);
+
+ const permissions = Object.values(PERMISSIONS_GROUPED_MAP).map((value) => {
+ let permissionsMessage = "";
+ const hasReadPermission = hasPermission(client.permissions, value.read);
+ const hasWritePermission = hasPermission(client.permissions, value.write);
+
+ if (hasReadPermission || hasWritePermission) {
+ permissionsMessage = hasReadPermission ? "Read" : "Write";
+ }
+
+ if (hasReadPermission && hasWritePermission) {
+ permissionsMessage = "Read, write";
+ }
+
+ return (
+ !!permissionsMessage && (
+
+ ✓
+ {permissionsMessage} your {`${value.label}s`.toLocaleLowerCase()}
+
+ )
+ );
+ });
+
+ return (
+
+
+
+ {/*
+ below is where the client logo will be displayed
+ first we check if the client has a logo property and display logo if present
+ else we take logo from user profile pic
+ */}
+ {client.logo ? (
+
}
+ className="items-center"
+ imageSrc={client.logo}
+ size="lg"
+ />
+ ) : (
+
}
+ className="items-center"
+ imageSrc="/cal-com-icon.svg"
+ size="lg"
+ />
+ )}
+
+
+
+
+
+
+
+
+
+ {t("access_cal_account", { clientName: client.name, appName: APP_NAME })}
+
+
+ {t("allow_client_to", { clientName: client.name })}
+
+
+
+
+
+
+
+
+ {t("allow_client_to_do", { clientName: client.name })}
+
+
{t("oauth_access_information", { appName: APP_NAME })}
{" "}
+
+
+
+
+ {
+ router.back();
+ }}>
+ {t("go_back")}
+
+
+ {t("allow")}
+
+
+
+
+ );
+}
diff --git a/apps/web/pages/auth/error.tsx b/apps/web/pages/auth/error.tsx
index 0a849c3661cd23..2995d07aafb97b 100644
--- a/apps/web/pages/auth/error.tsx
+++ b/apps/web/pages/auth/error.tsx
@@ -1,54 +1,11 @@
-import type { GetStaticPropsContext } from "next";
-import Link from "next/link";
-import { useSearchParams } from "next/navigation";
-import z from "zod";
-
-import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { Button, Icon } from "@calcom/ui";
-
import PageWrapper from "@components/PageWrapper";
-import AuthContainer from "@components/ui/AuthContainer";
-
-import { getTranslations } from "@server/lib/getTranslations";
-
-const querySchema = z.object({
- error: z.string().optional(),
-});
-
-export default function Error() {
- const { t } = useLocale();
- const searchParams = useSearchParams();
- const { error } = querySchema.parse({ error: searchParams?.get("error") || undefined });
- const errorMsg = error || t("error_during_login");
- return (
-
-
-
-
-
-
-
- {errorMsg}
-
-
-
-
-
- {t("go_back_login")}
-
-
-
- );
-}
-Error.PageWrapper = PageWrapper;
+import type { PageProps } from "@server/lib/auth/error/getStaticProps";
+import { getStaticProps } from "@server/lib/auth/error/getStaticProps";
-export const getStaticProps = async (context: GetStaticPropsContext) => {
- const i18n = await getTranslations(context);
+import Error from "~/auth/error/error-view";
- return {
- props: {
- i18n,
- },
- };
-};
+const Page = (props: PageProps) => ;
+Page.PageWrapper = PageWrapper;
+export default Page;
+export { getStaticProps };
diff --git a/apps/web/pages/auth/oauth2/authorize.tsx b/apps/web/pages/auth/oauth2/authorize.tsx
index b44ff58fc36c59..0783bd442d9296 100644
--- a/apps/web/pages/auth/oauth2/authorize.tsx
+++ b/apps/web/pages/auth/oauth2/authorize.tsx
@@ -1,179 +1,7 @@
-/* eslint-disable react-hooks/exhaustive-deps */
-import { useSession } from "next-auth/react";
-import { useRouter } from "next/navigation";
-import { useState, useEffect } from "react";
-
-import { APP_NAME } from "@calcom/lib/constants";
-import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
-import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { trpc } from "@calcom/trpc/react";
-import { Avatar, Button, Icon, Select } from "@calcom/ui";
-
import PageWrapper from "@components/PageWrapper";
-export default function Authorize() {
- const { t } = useLocale();
- const { status } = useSession();
-
- const router = useRouter();
- const searchParams = useCompatSearchParams();
-
- const client_id = (searchParams?.get("client_id") as string) || "";
- const state = searchParams?.get("state") as string;
- const scope = searchParams?.get("scope") as string;
-
- const queryString = searchParams?.toString();
-
- const [selectedAccount, setSelectedAccount] = useState<{ value: string; label: string } | null>();
- const scopes = scope ? scope.toString().split(",") : [];
-
- const { data: client, isPending: isPendingGetClient } = trpc.viewer.oAuth.getClient.useQuery(
- {
- clientId: client_id as string,
- },
- {
- enabled: status !== "loading",
- }
- );
-
- const { data, isPending: isPendingProfiles } = trpc.viewer.teamsAndUserProfilesQuery.useQuery();
-
- const generateAuthCodeMutation = trpc.viewer.oAuth.generateAuthCode.useMutation({
- onSuccess: (data) => {
- window.location.href = `${client?.redirectUri}?code=${data.authorizationCode}&state=${state}`;
- },
- });
-
- const mappedProfiles = data
- ? data
- .filter((profile) => !profile.readOnly)
- .map((profile) => ({
- label: profile.name || profile.slug || "",
- value: profile.slug || "",
- }))
- : [];
-
- useEffect(() => {
- if (mappedProfiles.length > 0) {
- setSelectedAccount(mappedProfiles[0]);
- }
- }, [isPendingProfiles]);
-
- useEffect(() => {
- if (status === "unauthenticated") {
- const urlSearchParams = new URLSearchParams({
- callbackUrl: `auth/oauth2/authorize?${queryString}`,
- });
- router.replace(`/auth/login?${urlSearchParams.toString()}`);
- }
- }, [status]);
-
- const isPending = isPendingGetClient || isPendingProfiles || status !== "authenticated";
-
- if (isPending) {
- return <>>;
- }
-
- if (!client) {
- return {t("unauthorized")}
;
- }
-
- return (
-
-
-
-
}
- className="items-center"
- imageSrc={client.logo}
- size="lg"
- />
-
-
-
-
-
-
-
-
-
- {t("access_cal_account", { clientName: client.name, appName: APP_NAME })}
-
-
{t("select_account_team")}
-
{
- setSelectedAccount(value);
- }}
- className="w-52"
- defaultValue={selectedAccount || mappedProfiles[0]}
- options={mappedProfiles}
- />
- {t("allow_client_to", { clientName: client.name })}
-
-
- ✓ {" "}
- {t("associate_with_cal_account", { clientName: client.name })}
-
-
- ✓ {t("see_personal_info")}
-
-
- ✓ {t("see_primary_email_address")}
-
-
- ✓ {t("connect_installed_apps")}
-
-
- ✓ {t("access_event_type")}
-
-
- ✓ {t("access_availability")}
-
-
- ✓ {t("access_bookings")}
-
-
-
-
-
-
-
-
- {t("allow_client_to_do", { clientName: client.name })}
-
-
{t("oauth_access_information", { appName: APP_NAME })}
{" "}
-
-
-
-
- {
- window.location.href = `${client.redirectUri}`;
- }}>
- {t("go_back")}
-
- {
- generateAuthCodeMutation.mutate({
- clientId: client_id as string,
- scopes,
- teamSlug: selectedAccount?.value.startsWith("team/")
- ? selectedAccount?.value.substring(5)
- : undefined, // team account starts with /team/
- });
- }}
- data-testid="allow-button">
- {t("allow")}
-
-
-
-
- );
-}
+import Authorize from "~/auth/oauth2/authorize-view";
-Authorize.PageWrapper = PageWrapper;
+const Page = () => ;
+Page.PageWrapper = PageWrapper;
+export default Page;
diff --git a/apps/web/pages/auth/platform/authorize.tsx b/apps/web/pages/auth/platform/authorize.tsx
index ca877f05c62968..fd68c83625a1b4 100644
--- a/apps/web/pages/auth/platform/authorize.tsx
+++ b/apps/web/pages/auth/platform/authorize.tsx
@@ -1,128 +1,7 @@
-import { useRouter } from "next/navigation";
-
-import { APP_NAME } from "@calcom/lib/constants";
-import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
-import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { Avatar, Button, Icon } from "@calcom/ui";
-
import PageWrapper from "@components/PageWrapper";
-import { PERMISSIONS_GROUPED_MAP } from "../../../../../packages/platform/constants/permissions";
-import { hasPermission } from "../../../../../packages/platform/utils/permissions";
-
-export default function Authorize() {
- const { t } = useLocale();
- const router = useRouter();
-
- const searchParams = useCompatSearchParams();
- const queryString = searchParams?.toString();
-
- // const { isLoading, error, data: client } = useOAuthClient(queryString);
-
- const client: {
- name: string;
- logo?: string;
- redirect_uris: string[];
- permissions: number;
- } = {
- name: "Acme.com",
- redirect_uris: ["", ""],
- permissions: 7,
- };
-
- console.log("These are the search params:", queryString);
-
- const permissions = Object.values(PERMISSIONS_GROUPED_MAP).map((value) => {
- let permissionsMessage = "";
- const hasReadPermission = hasPermission(client.permissions, value.read);
- const hasWritePermission = hasPermission(client.permissions, value.write);
-
- if (hasReadPermission || hasWritePermission) {
- permissionsMessage = hasReadPermission ? "Read" : "Write";
- }
-
- if (hasReadPermission && hasWritePermission) {
- permissionsMessage = "Read, write";
- }
-
- return (
- !!permissionsMessage && (
-
- ✓
- {permissionsMessage} your {`${value.label}s`.toLocaleLowerCase()}
-
- )
- );
- });
-
- return (
-
-
-
- {/*
- below is where the client logo will be displayed
- first we check if the client has a logo property and display logo if present
- else we take logo from user profile pic
- */}
- {client.logo ? (
-
}
- className="items-center"
- imageSrc={client.logo}
- size="lg"
- />
- ) : (
-
}
- className="items-center"
- imageSrc="/cal-com-icon.svg"
- size="lg"
- />
- )}
-
-
-
-
-
-
-
-
-
- {t("access_cal_account", { clientName: client.name, appName: APP_NAME })}
-
-
- {t("allow_client_to", { clientName: client.name })}
-
-
-
-
-
-
-
-
- {t("allow_client_to_do", { clientName: client.name })}
-
-
{t("oauth_access_information", { appName: APP_NAME })}
{" "}
-
-
-
-
- {
- router.back();
- }}>
- {t("go_back")}
-
-
- {t("allow")}
-
-
-
-
- );
-}
+import Authorize from "~/auth/platform/authorize-view";
-Authorize.PageWrapper = PageWrapper;
+const Page = () => ;
+Page.PageWrapper = PageWrapper;
+export default Page;
diff --git a/apps/web/server/lib/auth/error/getStaticProps.ts b/apps/web/server/lib/auth/error/getStaticProps.ts
new file mode 100644
index 00000000000000..cc917a12da0ef6
--- /dev/null
+++ b/apps/web/server/lib/auth/error/getStaticProps.ts
@@ -0,0 +1,14 @@
+import type { GetStaticPropsContext, InferGetStaticPropsType } from "next";
+
+import { getTranslations } from "@server/lib/getTranslations";
+
+export type PageProps = InferGetStaticPropsType;
+export const getStaticProps = async (context: GetStaticPropsContext) => {
+ const i18n = await getTranslations(context);
+
+ return {
+ props: {
+ i18n,
+ },
+ };
+};