diff --git a/apps/web/components/apps/AllApps.tsx b/apps/web/components/apps/AllApps.tsx
index fd203ee695..26e4ad8bf3 100644
--- a/apps/web/components/apps/AllApps.tsx
+++ b/apps/web/components/apps/AllApps.tsx
@@ -1,12 +1,10 @@
-import { appRegistry } from "pages/apps/_appRegistry";
-
+import { App } from "@lib/apps/interfaces/App";
import { useLocale } from "@lib/hooks/useLocale";
import AppCard from "./AppCard";
-export default function AllApps() {
+export default function AllApps({ apps }: { apps: App[] }) {
const { t } = useLocale();
- const apps = appRegistry();
return (
diff --git a/apps/web/components/apps/Slider.tsx b/apps/web/components/apps/Slider.tsx
index 8ab1cb8a9c..7eefa54ed9 100644
--- a/apps/web/components/apps/Slider.tsx
+++ b/apps/web/components/apps/Slider.tsx
@@ -2,16 +2,15 @@ import Glide from "@glidejs/glide";
import "@glidejs/glide/dist/css/glide.core.min.css";
import "@glidejs/glide/dist/css/glide.theme.min.css";
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/solid";
-import { appRegistry } from "pages/apps/_appRegistry";
import { useEffect, useState } from "react";
+import type { App } from "@lib/apps/interfaces/App";
import { useLocale } from "@lib/hooks/useLocale";
import useMediaQuery from "@lib/hooks/useMediaQuery";
import AppCard from "./AppCard";
-export default function Slider() {
- const apps = appRegistry();
+const Slider = ({ items }: { items: T[] }) => {
const { t } = useLocale();
const isMobile = useMediaQuery("(max-width: 767px)");
const [size, setSize] = useState(3);
@@ -49,7 +48,7 @@ export default function Slider() {
- {apps.map((app) => {
+ {items.map((app) => {
return (
app.trending && (
-
@@ -71,4 +70,6 @@ export default function Slider() {
);
-}
+};
+
+export default Slider;
diff --git a/apps/web/lib/apps/interfaces/App.ts b/apps/web/lib/apps/interfaces/App.ts
index 065cb2777b..5bbe60999b 100644
--- a/apps/web/lib/apps/interfaces/App.ts
+++ b/apps/web/lib/apps/interfaces/App.ts
@@ -18,4 +18,14 @@ export type App = {
description: string;
imageSrc: string;
variant: "calendar" | "payment" | "conferencing";
+ label: string;
+ slug: string;
+ category: string;
+ logo: string;
+ publisher: string;
+ url: string;
+ verified: boolean;
+ trending: boolean;
+ rating: number;
+ reviews: number;
};
diff --git a/apps/web/pages/apps/categories/[category].tsx b/apps/web/pages/apps/categories/[category].tsx
index 58e7ce2340..fa56c124f2 100644
--- a/apps/web/pages/apps/categories/[category].tsx
+++ b/apps/web/pages/apps/categories/[category].tsx
@@ -1,18 +1,18 @@
import { ChevronLeftIcon } from "@heroicons/react/solid";
+import { InferGetStaticPropsType } from "next";
import { useRouter } from "next/router";
+import { getAppRegistry } from "@calcom/app-store/_appRegistry";
+
import { useLocale } from "@lib/hooks/useLocale";
import Shell from "@components/Shell";
import AppCard from "@components/apps/AppCard";
import Button from "@components/ui/Button";
-import { appRegistry } from "../_appRegistry";
-
-export default function Apps() {
+export default function Apps({ appStore }: InferGetStaticPropsType) {
const { t } = useLocale();
const router = useRouter();
- const apps = appRegistry();
return (
All {router.query.category} apps
- {apps.map((app) => {
+ {appStore.map((app) => {
return (
app.category === router.query.category && (
);
}
+
+export const getStaticPaths = async () => {
+ const appStore = getAppRegistry();
+ const paths = appStore.reduce((categories, app) => {
+ if (!categories.includes(app.category)) {
+ categories.push(app.category);
+ }
+ return categories;
+ }, [] as string[]);
+
+ return {
+ paths: paths.map((category) => ({ params: { category } })),
+ fallback: false,
+ };
+};
+
+export const getStaticProps = async () => {
+ return {
+ props: {
+ appStore: getAppRegistry(),
+ },
+ };
+};
diff --git a/apps/web/pages/apps/index.tsx b/apps/web/pages/apps/index.tsx
index 1a37703b54..9571309750 100644
--- a/apps/web/pages/apps/index.tsx
+++ b/apps/web/pages/apps/index.tsx
@@ -1,3 +1,5 @@
+import { getAppRegistry } from "@calcom/app-store/_appRegistry";
+
import { useLocale } from "@lib/hooks/useLocale";
import AppsShell from "@components/AppsShell";
@@ -6,31 +8,35 @@ import AllApps from "@components/apps/AllApps";
import AppStoreCategories from "@components/apps/Categories";
import Slider from "@components/apps/Slider";
-export default function Apps() {
+export default function Apps({ appStore, categories }) {
const { t } = useLocale();
- const popularCategories = [
- {
- name: "Payments",
- count: 1,
- },
- {
- name: "Video",
- count: 3,
- },
- {
- name: "Calendar",
- count: 4,
- },
- ];
-
return (
-
-
-
+
+
+
);
}
+
+export const getStaticProps = async () => {
+ const appStore = getAppRegistry();
+ const categories = appStore.reduce((c, app) => {
+ if (c[app.category]) {
+ c[app.category] = c[app.category]++;
+ } else {
+ c[app.category] = 1;
+ }
+ return c;
+ }, {});
+
+ return {
+ props: {
+ categories: Object.entries(categories).map(([name, count]) => ({ name, count })),
+ appStore,
+ },
+ };
+};
diff --git a/apps/web/pages/apps/_appRegistry.ts b/packages/app-store/_appRegistry.ts
similarity index 71%
rename from apps/web/pages/apps/_appRegistry.ts
rename to packages/app-store/_appRegistry.ts
index 868e117450..90ca55523d 100644
--- a/apps/web/pages/apps/_appRegistry.ts
+++ b/packages/app-store/_appRegistry.ts
@@ -1,38 +1,9 @@
-import fs from "fs";
-import path from "path";
-
-// It won't be called on client-side.
-export async function getStaticProps() {
- const appStoreDir = path.join(process.cwd(), "packages/appStore");
- const filenames = fs.readdirSync(appStoreDir);
-
- const apps = filenames.map((filename) => {
- const filePath = path.join(appStoreDir, filename);
- const fileContents = fs.readFileSync(filePath, "utf8");
-
- // Generally you would parse/transform the contents
- // For example you can transform markdown to HTML here
-
- return {
- filename,
- content: fileContents,
- };
- });
- // By returning { props: posts }, the Blog component
- // will receive `posts` as a prop at build time
- return {
- props: {
- posts: apps,
- },
- };
-}
-
-export function appRegistry() {
+export function getAppRegistry() {
return [
{
name: "Zoom",
slug: "zoom", // needs to be the same as the folder name
- category: "Video Conferencing",
+ category: "video",
description:
"Zoom is the most popular video conferencing platform, joinable on the web or via desktop/mobile apps.",
logo: "/apps/zoom.svg",
@@ -45,7 +16,7 @@ export function appRegistry() {
{
name: "Cal Video",
slug: "cal-video",
- category: "Video Conferencing",
+ category: "video",
description:
"Cal Video is the in-house web-based video conferencing platform powered by Daily.co, which is minimalistic and lightweight, but has most of the features you need.",
logo: "/apps/daily.svg",
@@ -59,7 +30,7 @@ export function appRegistry() {
{
name: "Google Meet",
slug: "google-meet",
- category: "Video Conferencing",
+ category: "video",
description:
"Google Meet is Google's web-based video conferencing platform, designed to compete with major conferencing platforms.",
logo: "https://cdn.iconscout.com/icon/free/png-256/google-meet-2923654-2416657.png",
@@ -69,8 +40,8 @@ export function appRegistry() {
},
{
name: "Stripe",
- slug: "stripe",
- category: "Payments",
+ slug: "stripe_payment",
+ category: "payment",
description: "Stripe is the world's leading payment provider. Start charging for your bookings today.",
logo: "/apps/stripe.svg",
rating: 4.6,
@@ -80,7 +51,7 @@ export function appRegistry() {
{
name: "Google Calendar",
slug: "google-calendar",
- category: "Calendar",
+ category: "calendar",
description:
"Google Calendar is the most popular calendar platform for personal and business calendars.",
logo: "/apps/google-calendar.svg",
@@ -90,7 +61,7 @@ export function appRegistry() {
{
name: "Microsoft 365/Outlook Calendar",
slug: "microsoft-365",
- category: "Calendar",
+ category: "calendar",
description:
"Microsoft 365 calendars for business users, and Outlook is a popular calendar platform for personal users.",
logo: "/apps/outlook.svg",
@@ -100,7 +71,7 @@ export function appRegistry() {
{
name: "CalDAV",
slug: "caldav",
- category: "Calendar",
+ category: "calendar",
description: "CalDAV is an open calendar standard which connects to virtually every calendar.",
logo: "/apps/caldav.svg",
rating: 3.6,
@@ -109,7 +80,7 @@ export function appRegistry() {
{
name: "iCloud Calendar",
slug: "icloud-calendar",
- category: "Calendar",
+ category: "calendar",
description:
"iCloud Calendar is Apple's calendar platform for users of iCloud, and is used in the Apple Calendar app on iOS and macOS.",
logo: "/apps/apple-calendar.svg",