[PULSE-45] Integrations page overhaul — 75 guides, SEO, search & filters #13

Merged
uz1mani merged 4 commits from staging into main 2026-02-07 00:28:58 +00:00
uz1mani commented 2026-02-07 00:28:44 +00:00 (Migrated from github.com)

Work Item

PULSE-45

Summary

  • Expanded integrations from 16 to 75 with real brand logos (simple-icons), centralized data store, and detailed guide content with external/internal links
  • Added search (with / keyboard shortcut), category filter chips, popular integrations row, count badge, and result count to the overview page
  • Implemented per-page SEO (generateMetadata, JSON-LD HowTo schema, SSG via generateStaticParams) and fixed card height inconsistency

Changes

  • lib/integrations.tsx — New centralized data store: Integration interface, 75 entries with official SVG paths, brand colors, officialUrl, seoDescription, relatedIds, and helper functions (getIntegration, getGroupedIntegrations)
  • lib/integration-guides.tsx — New file: getGuideContent(slug) returning JSX guide content for all 75 integrations with CodeBlock snippets, external plugin/doc links, and internal cross-links
  • app/integrations/[slug]/page.tsx — New dynamic route replacing 16 static directories; generateStaticParams for SSG, generateMetadata for unique SEO per page, HowTo JSON-LD structured data
  • components/IntegrationGuide.tsx — New server component: shared guide layout with hero, prose content area, "Related Integrations" cross-linking section, and back/CTA navigation
  • components/CodeBlock.tsx — New server component: VS-Code-style code snippet renderer
  • app/integrations/page.tsx — Rewrote overview page:
    • Search input with / keyboard shortcut and clear button
    • Category filter chips (All + 7 categories) with active state
    • Pinned "Popular" row (8 featured integrations) shown when unfiltered
    • Integration count badge ("75+") next to heading
    • Dynamic result count shown when searching/filtering
    • "Missing something?" card on zero results + persistent CTA at bottom
    • Fixed card height inconsistency (added h-full to Link elements)
  • Deleted 16 individual integration directories (angular, svelte, nuxt, astro, remix, gatsby, hugo, shopify, webflow, squarespace, wix, ghost, etc.) — replaced by [slug] dynamic route

Test Plan

[ ] Navigate to /integrations — verify count badge, popular row, category chips, and all 75 cards render
[ ] Search for "react" — verify filtered results and result count message
[ ] Search for "xyznonexistent" — verify "Missing something?" card appears
[ ] Click a category chip (e.g. "CMS") — verify only CMS integrations shown, chip highlighted
[ ] Press "/" on keyboard — verify search input receives focus
[ ] Open an integration guide (e.g. /integrations/nextjs) — verify logo, guide content, external links, related integrations section
[ ] Inspect page source of a guide page — verify meta title, description, OG tags, canonical URL, and JSON-LD HowTo script
[ ] Check last row of integration cards — verify all cards have consistent height
[ ] Verify all 75 slugs resolve (no 404s) by checking generateStaticParams output or spot-checking several

## Work Item PULSE-45 ## Summary - Expanded integrations from 16 to 75 with real brand logos (simple-icons), centralized data store, and detailed guide content with external/internal links - Added search (with `/` keyboard shortcut), category filter chips, popular integrations row, count badge, and result count to the overview page - Implemented per-page SEO (generateMetadata, JSON-LD HowTo schema, SSG via generateStaticParams) and fixed card height inconsistency ## Changes - **`lib/integrations.tsx`** — New centralized data store: `Integration` interface, 75 entries with official SVG paths, brand colors, `officialUrl`, `seoDescription`, `relatedIds`, and helper functions (`getIntegration`, `getGroupedIntegrations`) - **`lib/integration-guides.tsx`** — New file: `getGuideContent(slug)` returning JSX guide content for all 75 integrations with CodeBlock snippets, external plugin/doc links, and internal cross-links - **`app/integrations/[slug]/page.tsx`** — New dynamic route replacing 16 static directories; `generateStaticParams` for SSG, `generateMetadata` for unique SEO per page, `HowTo` JSON-LD structured data - **`components/IntegrationGuide.tsx`** — New server component: shared guide layout with hero, prose content area, "Related Integrations" cross-linking section, and back/CTA navigation - **`components/CodeBlock.tsx`** — New server component: VS-Code-style code snippet renderer - **`app/integrations/page.tsx`** — Rewrote overview page: - Search input with `/` keyboard shortcut and clear button - Category filter chips (All + 7 categories) with active state - Pinned "Popular" row (8 featured integrations) shown when unfiltered - Integration count badge ("75+") next to heading - Dynamic result count shown when searching/filtering - "Missing something?" card on zero results + persistent CTA at bottom - Fixed card height inconsistency (added `h-full` to Link elements) - **Deleted** 16 individual integration directories (angular, svelte, nuxt, astro, remix, gatsby, hugo, shopify, webflow, squarespace, wix, ghost, etc.) — replaced by `[slug]` dynamic route ## Test Plan [ ] Navigate to /integrations — verify count badge, popular row, category chips, and all 75 cards render [ ] Search for "react" — verify filtered results and result count message [ ] Search for "xyznonexistent" — verify "Missing something?" card appears [ ] Click a category chip (e.g. "CMS") — verify only CMS integrations shown, chip highlighted [ ] Press "/" on keyboard — verify search input receives focus [ ] Open an integration guide (e.g. /integrations/nextjs) — verify logo, guide content, external links, related integrations section [ ] Inspect page source of a guide page — verify meta title, description, OG tags, canonical URL, and JSON-LD HowTo script [ ] Check last row of integration cards — verify all cards have consistent height [ ] Verify all 75 slugs resolve (no 404s) by checking generateStaticParams output or spot-checking several
greptile-apps[bot] commented 2026-02-07 00:30:44 +00:00 (Migrated from github.com)

Greptile Overview

Greptile Summary

  • Replaces multiple hard-coded integration guide routes with a single dynamic app/integrations/[slug] page that statically generates all guides and emits per-page SEO + HowTo JSON-LD.
  • Introduces a centralized integration registry (lib/integrations.tsx) and a large JSX guide-content registry (lib/integration-guides.tsx) to support ~75 integrations.
  • Reworks /integrations into a searchable, filterable overview with category chips, a pinned Popular row, result counts, and consistent card heights.
  • Adds shared UI components (IntegrationGuide, CodeBlock) used by the new guide pages.
  • Adds new icon assets and (unexpectedly) a generated service worker bundle under public/.

Confidence Score: 2/5

  • This PR has merge-blocking runtime and deployment issues that should be fixed before merging.
  • app/integrations/[slug]/page.tsx incorrectly treats params as a Promise and awaits it, which will break routing at runtime. Additionally, public/sw 2.js appears to be a generated Workbox service worker artifact with build-specific precache revisions; committing it will go stale and can cause incorrect caching after subsequent builds.
  • app/integrations/[slug]/page.tsx, public/sw 2.js

Important Files Changed

Filename Overview
app/integrations/[slug]/page.tsx Adds dynamic integration guide route with SSG + metadata + JSON-LD, but params is incorrectly typed as a Promise and awaited, which will break runtime routing.
app/integrations/nextjs/page.tsx Deletes legacy static Next.js integration page in favor of the new dynamic [slug] route.
app/integrations/page.tsx Overhauls integrations overview with search, category chips, popular row, and grouped cards; no blocking issues found in the new filtering/render logic.
app/integrations/react/page.tsx Deletes legacy static React integration page replaced by dynamic [slug] guide.
app/integrations/vue/page.tsx Deletes legacy static Vue integration page replaced by dynamic [slug] guide.
app/integrations/wordpress/page.tsx Deletes legacy static WordPress integration page replaced by dynamic [slug] guide.
components/CodeBlock.tsx Adds a simple reusable code block component for guide snippets; straightforward server component rendering preformatted text.
components/IntegrationGuide.tsx Adds shared guide page layout with hero/header and related integrations section; uses registry data for cross-linking.
lib/integration-guides.tsx Adds JSX guide content registry for all integrations; large content-only file, no obvious runtime issues found in the export pattern.
lib/integrations.tsx Adds centralized integration metadata (75 entries) plus helper lookups/grouping; appears consistent and used by overview + guide pages.
public/Icon Padding left & right 192x192.png Adds a new 192x192 icon PNG asset.
public/Icon Padding left & right 512x512.png Adds a new 512x512 icon PNG asset.
public/sw 2.js Adds a minified generated service worker bundle with build-specific precache entries; should not be committed as source.

Sequence Diagram

sequenceDiagram
  participant U as User
  participant IP as /integrations (client page)
  participant IR as integrations registry
  participant GP as /integrations/[slug] (server page)
  participant GG as guide registry

  U->>IP: Visit /integrations
  IP->>IR: Load integrations + category labels/order
  IP->>IP: Filter by query/category
  IP-->>U: Render grouped cards + Popular row

  U->>GP: Click /integrations/{slug}
  GP->>IR: getIntegration(slug)
  alt integration missing
    GP-->>U: 404 (notFound)
  else integration found
    GP->>GG: getGuideContent(slug)
    alt guide missing
      GP-->>U: 404 (notFound)
    else guide found
      GP-->>U: Render IntegrationGuide + JSON-LD
    end
  end
<h2>Greptile Overview</h2> <h3>Greptile Summary</h3> - Replaces multiple hard-coded integration guide routes with a single dynamic `app/integrations/[slug]` page that statically generates all guides and emits per-page SEO + HowTo JSON-LD. - Introduces a centralized integration registry (`lib/integrations.tsx`) and a large JSX guide-content registry (`lib/integration-guides.tsx`) to support ~75 integrations. - Reworks `/integrations` into a searchable, filterable overview with category chips, a pinned Popular row, result counts, and consistent card heights. - Adds shared UI components (`IntegrationGuide`, `CodeBlock`) used by the new guide pages. - Adds new icon assets and (unexpectedly) a generated service worker bundle under `public/`. <h3>Confidence Score: 2/5</h3> - This PR has merge-blocking runtime and deployment issues that should be fixed before merging. - `app/integrations/[slug]/page.tsx` incorrectly treats `params` as a Promise and awaits it, which will break routing at runtime. Additionally, `public/sw 2.js` appears to be a generated Workbox service worker artifact with build-specific precache revisions; committing it will go stale and can cause incorrect caching after subsequent builds. - app/integrations/[slug]/page.tsx, public/sw 2.js <details><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | app/integrations/[slug]/page.tsx | Adds dynamic integration guide route with SSG + metadata + JSON-LD, but `params` is incorrectly typed as a Promise and awaited, which will break runtime routing. | | app/integrations/nextjs/page.tsx | Deletes legacy static Next.js integration page in favor of the new dynamic `[slug]` route. | | app/integrations/page.tsx | Overhauls integrations overview with search, category chips, popular row, and grouped cards; no blocking issues found in the new filtering/render logic. | | app/integrations/react/page.tsx | Deletes legacy static React integration page replaced by dynamic `[slug]` guide. | | app/integrations/vue/page.tsx | Deletes legacy static Vue integration page replaced by dynamic `[slug]` guide. | | app/integrations/wordpress/page.tsx | Deletes legacy static WordPress integration page replaced by dynamic `[slug]` guide. | | components/CodeBlock.tsx | Adds a simple reusable code block component for guide snippets; straightforward server component rendering preformatted text. | | components/IntegrationGuide.tsx | Adds shared guide page layout with hero/header and related integrations section; uses registry data for cross-linking. | | lib/integration-guides.tsx | Adds JSX guide content registry for all integrations; large content-only file, no obvious runtime issues found in the export pattern. | | lib/integrations.tsx | Adds centralized integration metadata (75 entries) plus helper lookups/grouping; appears consistent and used by overview + guide pages. | | public/Icon Padding left & right 192x192.png | Adds a new 192x192 icon PNG asset. | | public/Icon Padding left & right 512x512.png | Adds a new 512x512 icon PNG asset. | | public/sw 2.js | Adds a minified generated service worker bundle with build-specific precache entries; should not be committed as source. | </details> </details> <h3>Sequence Diagram</h3> ```mermaid sequenceDiagram participant U as User participant IP as /integrations (client page) participant IR as integrations registry participant GP as /integrations/[slug] (server page) participant GG as guide registry U->>IP: Visit /integrations IP->>IR: Load integrations + category labels/order IP->>IP: Filter by query/category IP-->>U: Render grouped cards + Popular row U->>GP: Click /integrations/{slug} GP->>IR: getIntegration(slug) alt integration missing GP-->>U: 404 (notFound) else integration found GP->>GG: getGuideContent(slug) alt guide missing GP-->>U: 404 (notFound) else guide found GP-->>U: Render IntegrationGuide + JSON-LD end end ```
greptile-apps[bot] (Migrated from github.com) reviewed 2026-02-07 00:30:49 +00:00
greptile-apps[bot] (Migrated from github.com) left a comment

13 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

<sub>13 files reviewed, 2 comments</sub> <sub>[Edit Code Review Agent Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews)</sub>
@@ -0,0 +25,4 @@
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { slug } = await params
const integration = getIntegration(slug)
if (!integration) return {}
greptile-apps[bot] (Migrated from github.com) commented 2026-02-07 00:30:47 +00:00

Incorrect params typing

In the App Router, params is a plain object, not a Promise. Typing it as params: Promise<{ slug: string }> and then await params will break at runtime when Next passes { slug: '...' }. Update PageProps to params: { slug: string } and remove the await usages in both generateMetadata and the page component.

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/integrations/[slug]/page.tsx
Line: 26:28

Comment:
**Incorrect params typing**

In the App Router, `params` is a plain object, not a `Promise`. Typing it as `params: Promise<{ slug: string }>` and then `await params` will break at runtime when Next passes `{ slug: '...' }`. Update `PageProps` to `params: { slug: string }` and remove the `await` usages in both `generateMetadata` and the page component.

How can I resolve this? If you propose a fix, please make it concise.
**Incorrect params typing** In the App Router, `params` is a plain object, not a `Promise`. Typing it as `params: Promise<{ slug: string }>` and then `await params` will break at runtime when Next passes `{ slug: '...' }`. Update `PageProps` to `params: { slug: string }` and remove the `await` usages in both `generateMetadata` and the page component. <details><summary>Prompt To Fix With AI</summary> `````markdown This is a comment left during a code review. Path: app/integrations/[slug]/page.tsx Line: 26:28 Comment: **Incorrect params typing** In the App Router, `params` is a plain object, not a `Promise`. Typing it as `params: Promise<{ slug: string }>` and then `await params` will break at runtime when Next passes `{ slug: '...' }`. Update `PageProps` to `params: { slug: string }` and remove the `await` usages in both `generateMetadata` and the page component. How can I resolve this? If you propose a fix, please make it concise. ````` </details>
@@ -0,0 +1 @@
if(!self.define){let e,s={};const a=(a,n)=>(a=new URL(a+".js",n).href,s[a]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=a,e.onload=s,document.head.appendChild(e)}else e=a,importScripts(a),s()}).then(()=>{let e=s[a];if(!e)throw new Error(`Module ${a} didnt register its module`);return e}));self.define=(n,c)=>{const i=e||("document"in self?document.currentScript.src:"")||location.href;if(s[i])return;let t={};const r=e=>a(e,i),d={module:{uri:i},exports:t,require:r};s[i]=Promise.all(n.map(e=>d[e]||r(e))).then(e=>(c(...e),t))}}define(["./workbox-f1770938"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/Icon Padding left & right 192x192.png",revision:"a9bae788a2253361a0e295deadbcc083"},{url:"/Icon Padding left & right 512x512.png",revision:"d2efb02d7b9a0a627387d334201e6ea7"},{url:"/_next/static/chunks/0e5ce63c-0d9b48ee367f70a2.js",revision:"0d9b48ee367f70a2"},{url:"/_next/static/chunks/164f4fb6-67211e558fa5de32.js",revision:"67211e558fa5de32"},{url:"/_next/static/chunks/1795-59339c2cb7f5fe6c.js",revision:"59339c2cb7f5fe6c"},{url:"/_next/static/chunks/1992.d220c685821eae19.js",revision:"d220c685821eae19"},{url:"/_next/static/chunks/201a89a4-3d566664e60259ff.js",revision:"3d566664e60259ff"},{url:"/_next/static/chunks/2170a4aa-a1f2fd30878a3e91.js",revision:"a1f2fd30878a3e91"},{url:"/_next/static/chunks/2f0b94e8-ce53c98b232310fc.js",revision:"ce53c98b232310fc"},{url:"/_next/static/chunks/30a37ab2-30992cf05be9404a.js",revision:"30992cf05be9404a"},{url:"/_next/static/chunks/4573-3b13da9808e514ac.js",revision:"3b13da9808e514ac"},{url:"/_next/static/chunks/4bd1b696-e5d7c65570c947b7.js",revision:"e5d7c65570c947b7"},{url:"/_next/static/chunks/5902.6a7448e0dec4f9e1.js",revision:"6a7448e0dec4f9e1"},{url:"/_next/static/chunks/5909-cf645368838a6b20.js",revision:"cf645368838a6b20"},{url:"/_next/static/chunks/7620-7f79d135fa03ba32.js",revision:"7f79d135fa03ba32"},{url:"/_next/static/chunks/795d4814-4c5954750245c540.js",revision:"4c5954750245c540"},{url:"/_next/static/chunks/8500-98e13bcce54aa7a0.js",revision:"98e13bcce54aa7a0"},{url:"/_next/static/chunks/8928-835918834d3b3798.js",revision:"835918834d3b3798"},{url:"/_next/static/chunks/8e1d74a4-407f656e5bcc2171.js",revision:"407f656e5bcc2171"},{url:"/_next/static/chunks/ad2866b8.6c51983a1eb56136.js",revision:"6c51983a1eb56136"},{url:"/_next/static/chunks/app/_global-error/page-85628b53985e66de.js",revision:"85628b53985e66de"},{url:"/_next/static/chunks/app/_not-found/page-85628b53985e66de.js",revision:"85628b53985e66de"},{url:"/_next/static/chunks/app/about/page-961134dbb15bfcbf.js",revision:"961134dbb15bfcbf"},{url:"/_next/static/chunks/app/api/auth/refresh/route-85628b53985e66de.js",revision:"85628b53985e66de"},{url:"/_next/static/chunks/app/auth/callback/page-e02d5207bc967b15.js",revision:"e02d5207bc967b15"},{url:"/_next/static/chunks/app/faq/page-fac70f1a93ec3606.js",revision:"fac70f1a93ec3606"},{url:"/_next/static/chunks/app/installation/page-f2243076dd49d3d4.js",revision:"f2243076dd49d3d4"},{url:"/_next/static/chunks/app/integrations/nextjs/page-ba7437bf718723f9.js",revision:"ba7437bf718723f9"},{url:"/_next/static/chunks/app/integrations/page-cef8333acda65483.js",revision:"cef8333acda65483"},{url:"/_next/static/chunks/app/integrations/react/page-410fd66b109e333c.js",revision:"410fd66b109e333c"},{url:"/_next/static/chunks/app/integrations/vue/page-d815efd4c9a39306.js",revision:"d815efd4c9a39306"},{url:"/_next/static/chunks/app/integrations/wordpress/page-f03e2378c00b904a.js",revision:"f03e2378c00b904a"},{url:"/_next/static/chunks/app/layout-ce4924adc7c42dcc.js",revision:"ce4924adc7c42dcc"},{url:"/_next/static/chunks/app/login/page-96640fc203cae231.js",revision:"96640fc203cae231"},{url:"/_next/static/chunks/app/not-found-833b37ab1663c8a1.js",revision:"833b37ab1663c8a1"},{url:"/_next/static/chunks/app/onboarding/page-091dd498ed5d5805.js",revision:"091dd498ed5d5805"},{url:"/_next/static/chunks/app/org-settings/page-c3e177ad6171617e.js",revision:"c3e177ad6171617e"},{url:"/_next/static/chunks/app/page-1d0749d506de7405.js",revision:"1d0749d506de7405"},{url:"/_next/static/chunks/app/pricing/page-ff5fab384b0203d3.js",revision:"ff5fab384b0203d3"},{url:"/_next/static/chunks/app/settings/page-890f2c485c894bb2.js",revision:"890f2c485c894bb2"},{url:"/_next/static/chunks/app/share/%5Bid%5D/page-88df4e94e7b55109.js",revision:"88df4e94e7b55109"},{url:"/_next/static/chunks/app/signup/page-b17cb3afb210ddbb.js",revision:"b17cb3afb210ddbb"},{url:"/_next/static/chunks/app/sites/%5Bid%5D/page-616162d6a1d6e47c.js",revision:"616162d6a1d6e47c"},{url:"/_next/static/chunks/app/sites/%5Bid%5D/realtime/page-2a2d04563947fa67.js",revision:"2a2d04563947fa67"},{url:"/_next/static/chunks/app/sites/%5Bid%5D/settings/page-b0fd17efb70e114b.js",revision:"b0fd17efb70e114b"},{url:"/_next/static/chunks/app/sites/new/page-c8dddbd443e2a8ac.js",revision:"c8dddbd443e2a8ac"},{url:"/_next/static/chunks/bc98253f.1c4ca5773
greptile-apps[bot] (Migrated from github.com) commented 2026-02-07 00:30:48 +00:00

Built artifact committed

public/sw 2.js looks like a generated Workbox service worker bundle containing hashed /_next/static/... entries and build-specific revisions. Committing this will go stale on the next build and can cause clients to cache incorrect assets. This file should be generated during the build/deploy process (or removed from source control) rather than checked in.

Prompt To Fix With AI
This is a comment left during a code review.
Path: public/sw 2.js
Line: 1:1

Comment:
**Built artifact committed**

`public/sw 2.js` looks like a generated Workbox service worker bundle containing hashed `/_next/static/...` entries and build-specific revisions. Committing this will go stale on the next build and can cause clients to cache incorrect assets. This file should be generated during the build/deploy process (or removed from source control) rather than checked in.

How can I resolve this? If you propose a fix, please make it concise.
**Built artifact committed** `public/sw 2.js` looks like a generated Workbox service worker bundle containing hashed `/_next/static/...` entries and build-specific revisions. Committing this will go stale on the next build and can cause clients to cache incorrect assets. This file should be generated during the build/deploy process (or removed from source control) rather than checked in. <details><summary>Prompt To Fix With AI</summary> `````markdown This is a comment left during a code review. Path: public/sw 2.js Line: 1:1 Comment: **Built artifact committed** `public/sw 2.js` looks like a generated Workbox service worker bundle containing hashed `/_next/static/...` entries and build-specific revisions. Committing this will go stale on the next build and can cause clients to cache incorrect assets. This file should be generated during the build/deploy process (or removed from source control) rather than checked in. How can I resolve this? If you propose a fix, please make it concise. ````` </details>
Sign in to join this conversation.
No description provided.