Skip to content

Commit

Permalink
chore: migrate apps/categories page
Browse files Browse the repository at this point in the history
  • Loading branch information
DmytroHryshyn committed Dec 12, 2023
1 parent 50e4053 commit 317b43b
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 6 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -297,3 +297,7 @@ APP_ROUTER_EVENT_TYPES_ENABLED=1
APP_ROUTER_SETTINGS_ADMIN_ENABLED=1
APP_ROUTER_APPS_SLUG_ENABLED=1
APP_ROUTER_APPS_SLUG_SETUP_ENABLED=1
# whether we redirect to the future/apps/categories from /apps/categories or not
APP_ROUTER_APPS_CATEGORIES_ENABLED=1
# whether we redirect to the future/apps/categories/[category] from /apps/categories/[category] or not
APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED=1
6 changes: 4 additions & 2 deletions apps/web/abTest/middlewareFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import z from "zod";
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,
["/apps/:slug", Boolean(process.env.APP_ROUTER_APPS_SLUG_ENABLED)] as const,
["/apps/:slug/setup", Boolean(process.env.APP_ROUTER_APPS_SLUG_SETUP_ENABLED)] 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,
].map(([pathname, enabled]) => [
new URLPattern({
pathname,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { headers } from "next/headers";
import { type ReactElement } from "react";

import PageWrapper from "@components/PageWrapperAppDir";

type WrapperWithLayoutProps = {
children: ReactElement;
};

export default async function WrapperWithLayout({ children }: WrapperWithLayoutProps) {
const h = headers();
const nonce = h.get("x-nonce") ?? undefined;

return (
<PageWrapper getLayout={null} requiresLicense={false} nonce={nonce} themeBasis={null}>
{children}
</PageWrapper>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import CategoryPage from "@pages/apps/categories/[category]";
import { Prisma } from "@prisma/client";
import { _generateMetadata } from "app/_utils";
import { notFound } from "next/navigation";
import z from "zod";

import { getAppRegistry } from "@calcom/app-store/_appRegistry";
import { APP_NAME } from "@calcom/lib/constants";
import prisma from "@calcom/prisma";
import { AppCategories } from "@calcom/prisma/enums";

export const generateMetadata = async () => {
return await _generateMetadata(
() => `${APP_NAME} | ${APP_NAME}`,
() => ""
);
};

export const getStaticParams = async () => {
const paths = Object.keys(AppCategories);

try {
await prisma.$queryRaw`SELECT 1`;
} catch (e: unknown) {
if (e instanceof Prisma.PrismaClientInitializationError) {
// Database is not available at build time. Make sure we fall back to building these pages on demand
return [];
} else {
throw e;
}
}

return paths.map((category) => ({ category }));
};

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

const getPageProps = async ({ params }: { params: Record<string, string | string[]> }) => {
const p = querySchema.safeParse(params);

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

const appQuery = await prisma.app.findMany({
where: {
categories: {
has: p.data.category,
},
},
select: {
slug: true,
},
});

const dbAppsSlugs = appQuery.map((category) => category.slug);

const appStore = await getAppRegistry();

const apps = appStore.filter((app) => dbAppsSlugs.includes(app.slug));
return {
apps,
};
};

export default async function Page({ params }: { params: Record<string, string | string[]> }) {
const { apps } = await getPageProps({ params });
return <CategoryPage apps={apps} />;
}

export const dynamic = "force-static";
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import LegacyPage from "@pages/apps/categories/index";
import { ssrInit } from "app/_trpc/ssrInit";
import { _generateMetadata } from "app/_utils";
import { cookies, headers } from "next/headers";

import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { APP_NAME } from "@calcom/lib/constants";

import PageWrapper from "@components/PageWrapperAppDir";

export const generateMetadata = async () => {
return await _generateMetadata(
() => `Categories | ${APP_NAME}`,
() => ""
);
};

async function getPageProps() {
const ssr = await ssrInit();
const req = { headers: headers(), cookies: cookies() };

// @ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest | IncomingMessage
const session = await getServerSession({ req });

let appStore;
if (session?.user?.id) {
appStore = await getAppRegistryWithCredentials(session.user.id);
} else {
appStore = await getAppRegistry();
}

const categories = appStore.reduce((c, app) => {
for (const category of app.categories) {
c[category] = c[category] ? c[category] + 1 : 1;
}
return c;
}, {} as Record<string, number>);

return {
categories: Object.entries(categories).map(([name, count]) => ({ name, count })),
dehydratedState: await ssr.dehydrate(),
};
}

export default async function Page() {
const props = await getPageProps();
const h = headers();
const nonce = h.get("x-nonce") ?? undefined;

return (
<PageWrapper getLayout={null} requiresLicense={false} nonce={nonce} themeBasis={null} {...props}>
<LegacyPage {...props} />
</PageWrapper>
);
}
6 changes: 4 additions & 2 deletions apps/web/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,14 @@ export const config = {
"/future/event-types/",
"/settings/admin/:path*",
"/future/settings/admin/:path*",

"/apps/:slug/",
"/future/apps/:slug/",

"/apps/:slug/setup/",
"/future/apps/:slug/setup/",
"/apps/categories/",
"/future/apps/categories/",
"/apps/categories/:category/",
"/future/apps/categories/:category/",
],
};

Expand Down
2 changes: 2 additions & 0 deletions apps/web/pages/apps/categories/[category].tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import { Prisma } from "@prisma/client";
import type { GetStaticPropsContext, InferGetStaticPropsType } from "next";
import Link from "next/link";
Expand Down
4 changes: 3 additions & 1 deletion apps/web/pages/apps/categories/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import type { GetServerSidePropsContext } from "next";
import Link from "next/link";

Expand All @@ -13,7 +15,7 @@ import PageWrapper from "@components/PageWrapper";

import { ssrInit } from "@server/lib/ssr";

export default function Apps({ categories }: inferSSRProps<typeof getServerSideProps>) {
export default function Apps({ categories }: Omit<inferSSRProps<typeof getServerSideProps>, "trpcState">) {
const { t, isLocaleReady } = useLocale();

return (
Expand Down
56 changes: 55 additions & 1 deletion apps/web/playwright/ab-tests-redirect.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,58 @@ test.describe("apps/ A/B tests", () => {

await expect(locator).toBeVisible();
});
});

test("should point to the /future/apps/categories", async ({ page, users, context }) => {
await context.addCookies([
{
name: "x-calcom-future-routes-override",
value: "1",
url: "http://localhost:3000",
},
]);
const user = await users.create();

await user.apiLogin();

await page.goto("/apps/categories");

await page.waitForLoadState();

const dataNextJsRouter = await page.evaluate(() =>
window.document.documentElement.getAttribute("data-nextjs-router")
);

expect(dataNextJsRouter).toEqual("app");

const locator = page.getByTestId("app-store-category-messaging");

await expect(locator).toBeVisible();
});

test("should point to the /future/apps/categories/[category]", async ({ page, users, context }) => {
await context.addCookies([
{
name: "x-calcom-future-routes-override",
value: "1",
url: "http://localhost:3000",
},
]);
const user = await users.create();

await user.apiLogin();

await page.goto("/apps/categories/messaging");

await page.waitForLoadState();

const dataNextJsRouter = await page.evaluate(() =>
window.document.documentElement.getAttribute("data-nextjs-router")
);

expect(dataNextJsRouter).toEqual("app");

const locator = page.getByText(/messaging apps/i);

await expect(locator).toBeVisible();
});
});
2 changes: 2 additions & 0 deletions turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@
"ALLOWED_HOSTNAMES",
"ANALYZE",
"API_KEY_PREFIX",
"APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED",
"APP_ROUTER_APPS_CATEGORIES_ENABLED",
"APP_ROUTER_APPS_SLUG_ENABLED",
"APP_ROUTER_APPS_SLUG_SETUP_ENABLED",
"APP_ROUTER_EVENT_TYPES_ENABLED",
Expand Down

0 comments on commit 317b43b

Please sign in to comment.