From 48985ef55c21cc138726ba50f231e65b294822c5 Mon Sep 17 00:00:00 2001 From: Boaz Poolman Date: Mon, 29 Jun 2026 16:09:34 +0200 Subject: [PATCH 1/2] feat: render default cta under community pages --- apps/web/.env.example | 3 ++ .../web/src/features/cms/lib/community-cta.ts | 29 +++++++++++++++++++ .../features/cms/pages/organization/page.tsx | 9 ++++-- .../cms/pages/organization/template.tsx | 17 ++++++++++- .../src/features/cms/pages/package/page.tsx | 12 +++++--- .../features/cms/pages/package/template.tsx | 13 ++++++++- .../src/features/cms/pages/template/page.tsx | 12 +++++--- .../features/cms/pages/template/template.tsx | 13 ++++++++- apps/web/src/features/cms/pages/user/page.tsx | 16 +++++++--- .../src/features/cms/pages/user/template.tsx | 13 ++++++++- 10 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 apps/web/src/features/cms/lib/community-cta.ts diff --git a/apps/web/.env.example b/apps/web/.env.example index f592d8a..de263e2 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -6,6 +6,9 @@ NEXT_PUBLIC_SEARCH_API_KEY=E8H-DDQUGhZhFWhTq263Ohd80UErhFmLIFnlQK81oeQ CMS_BEARER_TOKEN=tobemodified +# Document ID of the CTA block to render on community content pages (user, org, template, package) +COMMUNITY_CTA_ID= + STRAPI_UI_URL=tobemodified STRAPI_UI_TOKEN=tobemodified NEXT_PUBLIC_STRAPI_UI_URL=tobemodified diff --git a/apps/web/src/features/cms/lib/community-cta.ts b/apps/web/src/features/cms/lib/community-cta.ts new file mode 100644 index 0000000..75100dd --- /dev/null +++ b/apps/web/src/features/cms/lib/community-cta.ts @@ -0,0 +1,29 @@ +import type { GetQueryParams } from "@repo/strapi-client"; +import type { Modules, UID } from "@strapi/types"; +import { cmsClient } from "@/features/cms/lib/strapi"; + +const contentType = "api::cta.cta" satisfies UID.ContentType; + +const query = { + populate: { + image: true, + button: true, + }, +} satisfies GetQueryParams; + +export type CommunityCTAData = Modules.Documents.Result< + typeof contentType, + typeof query +>; + +export async function fetchCommunityCTA(): Promise { + const id = process.env.COMMUNITY_CTA_ID; + if (!id) return null; + + try { + const result = await cmsClient.collection(contentType).findOne(id, query); + return result.data; + } catch { + return null; + } +} diff --git a/apps/web/src/features/cms/pages/organization/page.tsx b/apps/web/src/features/cms/pages/organization/page.tsx index 64196d0..38121b4 100644 --- a/apps/web/src/features/cms/pages/organization/page.tsx +++ b/apps/web/src/features/cms/pages/organization/page.tsx @@ -1,5 +1,6 @@ import type { GetQueryParams } from "@repo/strapi-client"; import type { Data, Modules, UID } from "@strapi/types"; +import { fetchCommunityCTA } from "@/features/cms/lib/community-cta"; import { cmsClient } from "@/features/cms/lib/strapi"; import { OrganizationTemplate } from "@/features/cms/pages/organization"; import type { RelatedContentItems } from "@/utils/types"; @@ -21,9 +22,10 @@ type Props = { }; const OrganizationPage = async ({ documentId }: Props) => { - const document = await cmsClient - .collection(contentType) - .findOne(documentId, query); + const [document, communityCta] = await Promise.all([ + cmsClient.collection(contentType).findOne(documentId, query), + fetchCommunityCTA(), + ]); const content: RelatedContentItems = await cmsClient .fetch( @@ -46,6 +48,7 @@ const OrganizationPage = async ({ documentId }: Props) => { document={document.data} relatedContent={content} members={members} + communityCta={communityCta} /> ); }; diff --git a/apps/web/src/features/cms/pages/organization/template.tsx b/apps/web/src/features/cms/pages/organization/template.tsx index 8f0aa97..7089a62 100644 --- a/apps/web/src/features/cms/pages/organization/template.tsx +++ b/apps/web/src/features/cms/pages/organization/template.tsx @@ -18,17 +18,25 @@ import { Breadcrumbs } from "@/components/layout/breadcrumbs"; import { Hero, HeroSection } from "@/components/layout/hero"; import { Navigation } from "@/components/layout/navigation"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import type { CommunityCTAData } from "@/features/cms/lib/community-cta"; import { cmsImageUrl } from "@/features/cms/lib/image-url"; import type { OrganizationPageData } from "@/features/cms/pages/organization/page"; +import { CTASection } from "@/features/cms/sections/cta/cta"; import type { RelatedContentItems } from "@/utils/types"; type Props = { document: OrganizationPageData; relatedContent: RelatedContentItems; members: Data.ContentType<"plugin::better-auth.user">[]; + communityCta?: CommunityCTAData | null; }; -const OrganizationTemplate = ({ document, members, relatedContent }: Props) => { +const OrganizationTemplate = ({ + document, + members, + relatedContent, + communityCta, +}: Props) => { const { templates, packages } = relatedContent; const noRelatedContent = templates.length === 0 && packages.length === 0; @@ -274,6 +282,13 @@ const OrganizationTemplate = ({ document, members, relatedContent }: Props) => { + {communityCta && ( + + } + /> + )} ); }; diff --git a/apps/web/src/features/cms/pages/package/page.tsx b/apps/web/src/features/cms/pages/package/page.tsx index c3a0d65..13a50a7 100644 --- a/apps/web/src/features/cms/pages/package/page.tsx +++ b/apps/web/src/features/cms/pages/package/page.tsx @@ -1,5 +1,6 @@ import type { GetQueryParams } from "@repo/strapi-client"; import type { Modules, UID } from "@strapi/types"; +import { fetchCommunityCTA } from "@/features/cms/lib/community-cta"; import { cmsClient } from "@/features/cms/lib/strapi"; import { PackageTemplate } from "@/features/cms/pages/package"; @@ -37,14 +38,17 @@ type Props = { }; const PackagePage = async ({ documentId }: Props) => { - const document = await cmsClient - .collection(contentType) - .findOne(documentId, query); + const [document, communityCta] = await Promise.all([ + cmsClient.collection(contentType).findOne(documentId, query), + fetchCommunityCTA(), + ]); /** * @todo Check for any existing security scans on the latest version. */ - return ; + return ( + + ); }; export { PackagePage }; diff --git a/apps/web/src/features/cms/pages/package/template.tsx b/apps/web/src/features/cms/pages/package/template.tsx index b20bfb7..b811586 100644 --- a/apps/web/src/features/cms/pages/package/template.tsx +++ b/apps/web/src/features/cms/pages/package/template.tsx @@ -1,4 +1,5 @@ import { Button, Container } from "@repo/strapi-ui"; +import type { Data } from "@strapi/types"; import { Download, Star } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; @@ -9,15 +10,18 @@ import { RegistryLogo } from "@/components/content/registry-logo"; import { SidebarSection } from "@/components/content/sidebar-section"; import { VersionSecurityBadge } from "@/components/content/version-info"; import { Navigation } from "@/components/layout/navigation"; +import type { CommunityCTAData } from "@/features/cms/lib/community-cta"; import { cmsImageUrl } from "@/features/cms/lib/image-url"; import type { PackagePageData } from "@/features/cms/pages/package"; +import { CTASection } from "@/features/cms/sections/cta/cta"; import type { Owner } from "@/utils/types"; type Props = { document: PackagePageData; + communityCta?: CommunityCTAData | null; }; -const PackageTemplate = ({ document }: Props) => { +const PackageTemplate = ({ document, communityCta }: Props) => { const categories = (document.categories ?? []) as { documentId: string; name: string; @@ -177,6 +181,13 @@ const PackageTemplate = ({ document }: Props) => { + {communityCta && ( + + } + /> + )} ); }; diff --git a/apps/web/src/features/cms/pages/template/page.tsx b/apps/web/src/features/cms/pages/template/page.tsx index 0048888..0008049 100644 --- a/apps/web/src/features/cms/pages/template/page.tsx +++ b/apps/web/src/features/cms/pages/template/page.tsx @@ -1,5 +1,6 @@ import type { GetQueryParams } from "@repo/strapi-client"; import type { Modules, UID } from "@strapi/types"; +import { fetchCommunityCTA } from "@/features/cms/lib/community-cta"; import { cmsClient } from "@/features/cms/lib/strapi"; import { TemplateTemplate } from "@/features/cms/pages/template"; @@ -36,11 +37,14 @@ type Props = { }; const TemplatePage = async ({ documentId }: Props) => { - const document = await cmsClient - .collection(contentType) - .findOne(documentId, query); + const [document, communityCta] = await Promise.all([ + cmsClient.collection(contentType).findOne(documentId, query), + fetchCommunityCTA(), + ]); - return ; + return ( + + ); }; export { TemplatePage }; diff --git a/apps/web/src/features/cms/pages/template/template.tsx b/apps/web/src/features/cms/pages/template/template.tsx index cc32afa..dc5a433 100644 --- a/apps/web/src/features/cms/pages/template/template.tsx +++ b/apps/web/src/features/cms/pages/template/template.tsx @@ -1,4 +1,5 @@ import { Button, Container } from "@repo/strapi-ui"; +import type { Data } from "@strapi/types"; import { ExternalLink, Star } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; @@ -7,15 +8,18 @@ import { GitProviderLogo } from "@/components/content/git-provider-logo"; import { Markdown } from "@/components/content/markdown"; import { SidebarSection } from "@/components/content/sidebar-section/sidebar-section"; import { Navigation } from "@/components/layout/navigation"; +import type { CommunityCTAData } from "@/features/cms/lib/community-cta"; import { cmsImageUrl } from "@/features/cms/lib/image-url"; import type { TemplatePageData } from "@/features/cms/pages/template/page"; +import { CTASection } from "@/features/cms/sections/cta/cta"; import type { Owner } from "@/utils/types"; type Props = { document: TemplatePageData; + communityCta?: CommunityCTAData | null; }; -const TemplateTemplate = ({ document }: Props) => { +const TemplateTemplate = ({ document, communityCta }: Props) => { const categories = (document.categories ?? []) as { documentId: string; name: string; @@ -184,6 +188,13 @@ const TemplateTemplate = ({ document }: Props) => { + {communityCta && ( + + } + /> + )} ); }; diff --git a/apps/web/src/features/cms/pages/user/page.tsx b/apps/web/src/features/cms/pages/user/page.tsx index 103a97b..93c6573 100644 --- a/apps/web/src/features/cms/pages/user/page.tsx +++ b/apps/web/src/features/cms/pages/user/page.tsx @@ -1,5 +1,6 @@ import type { GetQueryParams } from "@repo/strapi-client"; import type { Modules, UID } from "@strapi/types"; +import { fetchCommunityCTA } from "@/features/cms/lib/community-cta"; import { cmsClient } from "@/features/cms/lib/strapi"; import { UserTemplate } from "@/features/cms/pages/user"; import type { RelatedContentItems } from "@/utils/types"; @@ -20,9 +21,10 @@ type Props = { }; const UserPage = async ({ documentId }: Props) => { - const document = await cmsClient - .collection(contentType) - .findOne(documentId, query); + const [document, communityCta] = await Promise.all([ + cmsClient.collection(contentType).findOne(documentId, query), + fetchCommunityCTA(), + ]); const content: RelatedContentItems = await cmsClient .fetch( @@ -30,7 +32,13 @@ const UserPage = async ({ documentId }: Props) => { ) .then((res) => res.json()); - return ; + return ( + + ); }; export { UserPage }; diff --git a/apps/web/src/features/cms/pages/user/template.tsx b/apps/web/src/features/cms/pages/user/template.tsx index e983107..02e7c0f 100644 --- a/apps/web/src/features/cms/pages/user/template.tsx +++ b/apps/web/src/features/cms/pages/user/template.tsx @@ -1,4 +1,5 @@ import { Container } from "@repo/strapi-ui"; +import type { Data } from "@strapi/types"; import { AppWindow, CalendarDays, @@ -15,16 +16,19 @@ import { Breadcrumbs } from "@/components/layout/breadcrumbs"; import { Hero, HeroSection } from "@/components/layout/hero"; import { Navigation } from "@/components/layout/navigation"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import type { CommunityCTAData } from "@/features/cms/lib/community-cta"; import { cmsImageUrl } from "@/features/cms/lib/image-url"; import type { UserPageData } from "@/features/cms/pages/user/page"; +import { CTASection } from "@/features/cms/sections/cta/cta"; import type { RelatedContentItems } from "@/utils/types"; type Props = { document: UserPageData; relatedContent: RelatedContentItems; + communityCta?: CommunityCTAData | null; }; -const UserTemplate = ({ document, relatedContent }: Props) => { +const UserTemplate = ({ document, relatedContent, communityCta }: Props) => { const { templates, packages } = relatedContent; const noRelatedContent = templates.length === 0 && packages.length === 0; @@ -229,6 +233,13 @@ const UserTemplate = ({ document, relatedContent }: Props) => { + {communityCta && ( + + } + /> + )} ); }; From f82c5d140c21e8806b485a5dd27a7bd47705507b Mon Sep 17 00:00:00 2001 From: Boaz Poolman Date: Mon, 29 Jun 2026 16:22:51 +0200 Subject: [PATCH 2/2] feat: highlighted integrations for homepage --- .../src/components/sections/highlights.json | 2 + apps/cms/types/generated/components.d.ts | 2 + .../cms/sections/highlights/highlights.tsx | 37 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/apps/cms/src/components/sections/highlights.json b/apps/cms/src/components/sections/highlights.json index b66c0a0..c3fdee1 100644 --- a/apps/cms/src/components/sections/highlights.json +++ b/apps/cms/src/components/sections/highlights.json @@ -22,6 +22,8 @@ "packages_newest", "templates_highlighted", "templates_newest", + "integrations_highlighted", + "integrations_newest", "recipes_highlighted", "recipes_newest", "showcases_highlighted", diff --git a/apps/cms/types/generated/components.d.ts b/apps/cms/types/generated/components.d.ts index 4c915a2..21aa9d3 100644 --- a/apps/cms/types/generated/components.d.ts +++ b/apps/cms/types/generated/components.d.ts @@ -78,6 +78,8 @@ export interface SectionsHighlights extends Struct.ComponentSchema { 'packages_newest', 'templates_highlighted', 'templates_newest', + 'integrations_highlighted', + 'integrations_newest', 'recipes_highlighted', 'recipes_newest', 'showcases_highlighted', diff --git a/apps/web/src/features/cms/sections/highlights/highlights.tsx b/apps/web/src/features/cms/sections/highlights/highlights.tsx index d9da58d..acd290d 100644 --- a/apps/web/src/features/cms/sections/highlights/highlights.tsx +++ b/apps/web/src/features/cms/sections/highlights/highlights.tsx @@ -50,6 +50,22 @@ async function fetchItems(query: string, amount: number) { return { type: "template" as const, items: res.data ?? [] }; } + if (query.startsWith("integrations_")) { + const res = await cmsClient + .collection("api::integration.integration") + .find({ + ...featuredFilter, + sort: ["createdAt:desc"], + pagination: { limit: amount }, + populate: { + logo: true, + url_alias: true, + labels: true, + }, + }); + return { type: "integration" as const, items: res.data ?? [] }; + } + if (query.startsWith("community_")) { const res = await cmsClient.collection("plugin::better-auth.user").find({ sort: [query === "community_newest" ? "createdAt:desc" : "name:asc"], @@ -136,6 +152,27 @@ const HighlightsSection = async ({ section }: Props) => { ), )} + {type === "integration" && + (items as Data.ContentType<"api::integration.integration">[]).map( + (integration) => ( + + ), + )} + {type === "user" && (items as Data.ContentType<"plugin::better-auth.user">[]).map( (user) => (