Load rule templates from API

This commit is contained in:
adilallo
2026-04-12 21:56:34 -06:00
parent cae4df261e
commit a39b4aa04b
17 changed files with 698 additions and 429 deletions
@@ -0,0 +1,23 @@
import dynamic from "next/dynamic";
import { listRuleTemplatesFromDb } from "../../lib/server/ruleTemplates";
import { GOVERNANCE_TEMPLATE_HOME_SLUGS } from "../../lib/templates/governanceTemplateCatalog";
import { gridEntriesForSlugOrderWithCatalogFallback } from "../../lib/templates/templateGridPresentation";
const RuleStack = dynamic(() => import("../components/sections/RuleStack"), {
loading: () => (
<section className="py-[var(--spacing-scale-032)] min-h-[400px]" />
),
ssr: true,
});
/**
* Server-loaded “Popular templates” row so the first paint has card data without a client fetch.
*/
export async function MarketingRuleStackSection() {
const rows = await listRuleTemplatesFromDb();
const initialGridEntries = gridEntriesForSlugOrderWithCatalogFallback(
rows,
GOVERNANCE_TEMPLATE_HOME_SLUGS,
);
return <RuleStack initialGridEntries={initialGridEntries} />;
}
+9 -8
View File
@@ -1,8 +1,10 @@
import dynamic from "next/dynamic";
import { Suspense } from "react";
import messages from "../../messages/en/index";
import { getTranslation } from "../../lib/i18n/getTranslation";
import HeroBanner from "../components/sections/HeroBanner";
import AskOrganizer from "../components/sections/AskOrganizer";
import { MarketingRuleStackSection } from "./MarketingRuleStackSection";
// Code split below-the-fold components to reduce initial bundle size
const LogoWall = dynamic(() => import("../components/sections/LogoWall"), {
@@ -22,13 +24,6 @@ const NumberedCards = dynamic(
},
);
const RuleStack = dynamic(() => import("../components/sections/RuleStack"), {
loading: () => (
<section className="py-[var(--spacing-scale-032)] min-h-[400px]" />
),
ssr: true,
});
const FeatureGrid = dynamic(
() => import("../components/sections/FeatureGrid"),
{
@@ -98,7 +93,13 @@ export default function Page() {
<HeroBanner {...heroBannerData} />
<LogoWall />
<NumberedCards {...numberedCardsData} />
<RuleStack />
<Suspense
fallback={
<section className="py-[var(--spacing-scale-032)] min-h-[400px]" />
}
>
<MarketingRuleStackSection />
</Suspense>
<FeatureGrid {...featureGridData} />
<QuoteBlock />
<AskOrganizer {...askOrganizerData} />
@@ -0,0 +1,54 @@
"use client";
import { useRouter } from "next/navigation";
import HeaderLockup from "../../components/type/HeaderLockup";
import { GovernanceTemplateGrid } from "../../components/sections/GovernanceTemplateGrid";
import type { TemplateGridCardEntry } from "../../../lib/templates/templateGridPresentation";
import { useTranslation } from "../../contexts/MessagesContext";
export interface TemplatesPageClientProps {
initialGridEntries: TemplateGridCardEntry[];
}
/**
* Full templates index — Figma 22142-898446 (title, intro, 2-col card grid).
* `initialGridEntries` is computed on the server to avoid a client-side loading flash.
*/
export default function TemplatesPageClient({
initialGridEntries,
}: TemplatesPageClientProps) {
const router = useRouter();
const t = useTranslation("pages.templates");
return (
<div className="w-full bg-black text-[var(--color-content-default-primary,white)]">
<div
className="
mx-auto w-full max-w-[1280px]
px-[20px] py-[32px]
min-[640px]:px-[32px] min-[640px]:py-[40px]
min-[1024px]:px-[64px] min-[1024px]:py-[48px]
"
>
<div className="flex w-full flex-col gap-2 py-3">
<HeaderLockup
title={t("title")}
description={t("subtitle")}
justification="left"
size="L"
/>
</div>
<div className="mt-6 min-[1024px]:mt-8">
<GovernanceTemplateGrid
entries={initialGridEntries}
onTemplateClick={(slug) => {
router.push(
`/create/review-template/${encodeURIComponent(slug)}`,
);
}}
/>
</div>
</div>
</div>
);
}
+7 -45
View File
@@ -1,47 +1,9 @@
"use client";
import { listRuleTemplatesFromDb } from "../../../lib/server/ruleTemplates";
import { gridEntriesForFullCatalogWithFallback } from "../../../lib/templates/templateGridPresentation";
import TemplatesPageClient from "./TemplatesPageClient";
import { useRouter } from "next/navigation";
import HeaderLockup from "../../components/type/HeaderLockup";
import { GovernanceTemplateGrid } from "../../components/sections/GovernanceTemplateGrid";
import { GOVERNANCE_TEMPLATE_CATALOG } from "../../../lib/templates/governanceTemplateCatalog";
import { useTranslation } from "../../contexts/MessagesContext";
/**
* Full templates index — Figma 22142-898446 (title, intro, 2-col card grid).
*/
export default function TemplatesPage() {
const router = useRouter();
const t = useTranslation("pages.templates");
return (
<div className="w-full bg-black text-[var(--color-content-default-primary,white)]">
<div
className="
mx-auto w-full max-w-[1280px]
px-[20px] py-[32px]
min-[640px]:px-[32px] min-[640px]:py-[40px]
min-[1024px]:px-[64px] min-[1024px]:py-[48px]
"
>
<div className="flex w-full flex-col gap-2 py-3">
<HeaderLockup
title={t("title")}
description={t("subtitle")}
justification="left"
size="L"
/>
</div>
<div className="mt-6 min-[1024px]:mt-8">
<GovernanceTemplateGrid
entries={GOVERNANCE_TEMPLATE_CATALOG}
onTemplateClick={(slug) => {
router.push(
`/create/review-template/${encodeURIComponent(slug)}`,
);
}}
/>
</div>
</div>
</div>
);
export default async function TemplatesPage() {
const rows = await listRuleTemplatesFromDb();
const initialGridEntries = gridEntriesForFullCatalogWithFallback(rows);
return <TemplatesPageClient initialGridEntries={initialGridEntries} />;
}