Skip to content

Commit

Permalink
chore: app router - all sub-pages in /apps (calcom#16976)
Browse files Browse the repository at this point in the history
* chore: apps/[slug] remove pages router

* remove apps/[slug] pages from /future

* chore: apps/installed remove pages router

* chore: apps/installation remove pages router

* remove Head element

* fix metadata

* fix test

* fix another test

* chore: apps/categories remove pages router

* revert unneeded changes

* update middleware

* Remove <Head>

* remove unused import and code

* remove unused import and code again

* fix

* fix category page

* add split icon

* add /routing paths to middleware matcher

* wip

* remove HeadSeo from App.tsx

* clean up head-seo test

* add generateAppMetadata

* use generateAppMetadata in apps/[slug] page

* delete file

* remove log

* fix

* fix

* fix apps/installed pages

* fix cateogires pages

* fix

* fix imports

* wip

* fix

* fix

* fix metadata

* fix

* redirect /apps/routing-forms to /routing

* replace all usages of /apps/routing-forms to /routing

* better naming

* /routing -> /routing/forms

* fix

* fix

* fix

* fix

* remove backPath as it is irrelevant when withoutMain is true

* fix type checks

* fix type check in apps/[slug]

* refactors

* fix

* fix test

* fix

* fix

* fix

* Replace multiple leading slashes with a single slash

* migrate routing-forms too

* add re routing

* fix

* add redirection

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
  • Loading branch information
3 people authored and MuhammadAimanSulaiman committed Feb 25, 2025
1 parent 12d8790 commit ead43c9
Show file tree
Hide file tree
Showing 68 changed files with 554 additions and 561 deletions.
8 changes: 0 additions & 8 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -355,14 +355,6 @@ E2E_TEST_OIDC_USER_PASSWORD=
# provide a value between 0 and 100 to ensure the percentage of traffic
# redirected from the legacy to the future pages
AB_TEST_BUCKET_PROBABILITY=50
APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED=0
APP_ROUTER_APPS_SLUG_ENABLED=0
APP_ROUTER_APPS_SLUG_SETUP_ENABLED=0
# whether we redirect to the future/apps/categories from /apps/categories or not
APP_ROUTER_APPS_CATEGORIES_ENABLED=0
# whether we redirect to the future/apps/categories/[category] from /apps/categories/[category] or not
APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED=0
APP_ROUTER_APPS_ENABLED=0
APP_ROUTER_TEAM_ENABLED=0
APP_ROUTER_AUTH_FORGOT_PASSWORD_ENABLED=0
APP_ROUTER_AUTH_LOGIN_ENABLED=0
Expand Down
5 changes: 0 additions & 5 deletions apps/web/abTest/middlewareFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ import { NextResponse, URLPattern } from "next/server";
import { FUTURE_ROUTES_ENABLED_COOKIE_NAME, FUTURE_ROUTES_OVERRIDE_COOKIE_NAME } from "@calcom/lib/constants";

const ROUTES: [URLPattern, boolean][] = [
["/apps/installed/:category", process.env.APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED === "1"] as const,
["/apps/:slug", process.env.APP_ROUTER_APPS_SLUG_ENABLED === "1"] as const,
["/apps/:slug/setup", process.env.APP_ROUTER_APPS_SLUG_SETUP_ENABLED === "1"] as const,
["/apps/categories", process.env.APP_ROUTER_APPS_CATEGORIES_ENABLED === "1"] as const,
["/apps/categories/:category", process.env.APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED === "1"] as const,
["/auth/forgot-password/:path*", process.env.APP_ROUTER_AUTH_FORGOT_PASSWORD_ENABLED === "1"] as const,
["/auth/login", process.env.APP_ROUTER_AUTH_LOGIN_ENABLED === "1"] as const,
["/auth/logout", process.env.APP_ROUTER_AUTH_LOGOUT_ENABLED === "1"] as const,
Expand Down
30 changes: 26 additions & 4 deletions apps/web/app/_utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import i18next from "i18next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { headers } from "next/headers";

import { constructGenericImage } from "@calcom/lib/OgImages";
import type { AppImageProps } from "@calcom/lib/OgImages";
import { constructGenericImage, constructAppImage } from "@calcom/lib/OgImages";
import { IS_CALCOM, WEBAPP_URL, APP_NAME, SEO_IMG_OGIMG, CAL_URL } from "@calcom/lib/constants";
import { buildCanonical } from "@calcom/lib/next-seo.config";
import { truncateOnWord } from "@calcom/lib/text";
Expand Down Expand Up @@ -61,17 +62,18 @@ const _generateMetadataWithoutImage = async (
const t = await getTranslationWithCache(locale);

const title = getTitle(t);
const description = getDescription(t);

const titleSuffix = `| ${APP_NAME}`;
const displayedTitle = title.includes(titleSuffix) || hideBranding ? title : `${title} ${titleSuffix}`;
const metadataBase = new URL(IS_CALCOM ? "https://cal.com" : WEBAPP_URL);
const truncatedDescription = truncateOnWord(getDescription(t), 158);

return {
title: title.length === 0 ? APP_NAME : displayedTitle,
description,
description: truncatedDescription,
alternates: { canonical },
openGraph: {
description: truncateOnWord(description, 158),
description: truncatedDescription,
url: canonical,
type: "website",
siteName: APP_NAME,
Expand Down Expand Up @@ -103,3 +105,23 @@ export const _generateMetadata = async (
},
};
};

export const generateAppMetadata = async (
app: AppImageProps,
getTitle: (t: TFunction<string, undefined>) => string,
getDescription: (t: TFunction<string, undefined>) => string,
hideBranding?: boolean,
origin?: string
) => {
const metadata = await _generateMetadataWithoutImage(getTitle, getDescription, hideBranding, origin);

const image = SEO_IMG_OGIMG + constructAppImage({ ...app, description: metadata.description });

return {
...metadata,
openGraph: {
...metadata.openGraph,
images: [image],
},
};
};
68 changes: 68 additions & 0 deletions apps/web/app/apps/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { PageProps as _PageProps } from "app/_types";
import { generateAppMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { notFound } from "next/navigation";
import { z } from "zod";

import { AppRepository } from "@calcom/lib/server/repository/app";
import { isPrismaAvailableCheck } from "@calcom/prisma/is-prisma-available-check";

import { getStaticProps } from "@lib/apps/[slug]/getStaticProps";

import AppView from "~/apps/[slug]/slug-view";

const paramsSchema = z.object({
slug: z.string(),
});

export const generateMetadata = async ({ params }: _PageProps) => {
const p = paramsSchema.safeParse(params);

if (!p.success) {
return notFound();
}

const props = await getStaticProps(p.data.slug);

if (!props) {
notFound();
}
const { name, logo, description } = props.data;

return await generateAppMetadata(
{ slug: logo, name, description },
() => name,
() => description
);
};

export const generateStaticParams = async () => {
const isPrismaAvailable = await isPrismaAvailableCheck();

if (!isPrismaAvailable) {
// Database is not available at build time. Make sure we fall back to building these pages on demand
return [];
}
const appStore = await AppRepository.findAppStore();
return appStore.map(({ slug }) => ({ slug }));
};

async function Page({ params }: _PageProps) {
const p = paramsSchema.safeParse(params);

if (!p.success) {
return notFound();
}

const props = await getStaticProps(p.data.slug);

if (!props) {
notFound();
}

return <AppView {...props} />;
}

export default WithLayout({ getLayout: null, ServerPage: Page });

export const dynamic = "force-static";
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,34 @@ import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps as _PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { notFound } from "next/navigation";
import { z } from "zod";

import { getServerSideProps } from "@calcom/app-store/_pages/setup/_getServerSideProps";

import Page, { type PageProps } from "~/apps/[slug]/setup/setup-view";

const paramsSchema = z.object({
slug: z.string(),
});

export const generateMetadata = async ({ params }: _PageProps) => {
return await _generateMetadata(
() => `${params.slug}`,
const p = paramsSchema.safeParse(params);

if (!p.success) {
return notFound();
}
const metadata = await _generateMetadata(
() => `${p.data.slug}`,
() => ""
);
return {
...metadata,
robots: {
follow: false,
index: false,
},
};
};

const getData = withAppDirSsr<PageProps>(getServerSideProps);
Expand Down
40 changes: 40 additions & 0 deletions apps/web/app/apps/categories/[category]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { redirect } from "next/navigation";
import { z } from "zod";

import { AppCategories } from "@calcom/prisma/enums";

import { getStaticProps } from "@lib/apps/categories/[category]/getStaticProps";

import CategoryPage from "~/apps/categories/[category]/category-view";

const querySchema = z.object({
category: z.enum(Object.values(AppCategories) as [AppCategories, ...AppCategories[]]),
});

export const generateMetadata = async () => {
return await _generateMetadata(
(t) => t("app_store"),
(t) => t("app_store_description")
);
};

export const generateStaticParams = async () => {
const paths = Object.keys(AppCategories);
return paths.map((category) => ({ category }));
};

async function Page({ params, searchParams }: PageProps) {
const parsed = querySchema.safeParse({ ...params, ...searchParams });
if (!parsed.success) {
redirect("/apps/categories/calendar");
}

const props = await getStaticProps(parsed.data.category);

return <CategoryPage {...props} />;
}

export default WithLayout({ getLayout: null, ServerPage: Page });
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import Page from "~/apps/installation/[[...step]]/step-view";

export const generateMetadata = async ({ params, searchParams }: PageProps) => {
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);

const { appMetadata } = await getData(legacyCtx);
return await _generateMetadata(
(t) => `${t("install")} ${appMetadata?.name ?? ""}`,
Expand All @@ -23,3 +22,4 @@ export const generateMetadata = async ({ params, searchParams }: PageProps) => {
const getData = withAppDirSsr<OnboardingPageProps>(getServerSideProps);

export default WithLayout({ getLayout: null, getData, Page });

32 changes: 32 additions & 0 deletions apps/web/app/apps/installed/[category]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { redirect } from "next/navigation";
import { z } from "zod";

import { AppCategories } from "@calcom/prisma/enums";

import InstalledApps from "~/apps/installed/[category]/installed-category-view";

const querySchema = z.object({
category: z.nativeEnum(AppCategories),
});

export const generateMetadata = async () => {
return await _generateMetadata(
(t) => t("installed_apps"),
(t) => t("manage_your_connected_apps")
);
};

const InstalledAppsWrapper = async ({ params }: PageProps) => {
const parsedParams = querySchema.safeParse(params);

if (!parsedParams.success) {
redirect("/apps/installed/calendar");
}

return <InstalledApps category={parsedParams.data.category} />;
};

export default WithLayout({ getLayout: null, ServerPage: InstalledAppsWrapper });
File renamed without changes.
1 change: 1 addition & 0 deletions apps/web/app/apps/routing-forms/[...pages]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "app/routing-forms/[...pages]/page";
1 change: 1 addition & 0 deletions apps/web/app/apps/routing-forms/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "app/routing-forms/page";
45 changes: 0 additions & 45 deletions apps/web/app/future/apps/[slug]/page.tsx

This file was deleted.

34 changes: 0 additions & 34 deletions apps/web/app/future/apps/categories/[category]/page.tsx

This file was deleted.

18 changes: 0 additions & 18 deletions apps/web/app/future/apps/installed/[category]/page.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ const paramsSchema = z
pages: [],
});

const Page = ({ params, searchParams }: PageProps) => {
const { pages } = paramsSchema.parse({ ...params, ...searchParams });
const Page = ({ params }: PageProps) => {
const { pages } = paramsSchema.parse(params);

redirect(`/apps/routing-forms/${pages.length ? pages.join("/") : ""}`);
redirect(`/routing/${pages.length ? pages.join("/") : ""}`);
};

export default Page;
Loading

0 comments on commit ead43c9

Please sign in to comment.