Skip to content

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

Merged
uz1mani merged 4 commits intomainfrom
staging
Feb 7, 2026
Merged

[PULSE-45] Integrations page overhaul — 75 guides, SEO, search & filters#13
uz1mani merged 4 commits intomainfrom
staging

Conversation

@uz1mani
Copy link
Copy Markdown
Member

@uz1mani uz1mani commented Feb 7, 2026

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

@uz1mani uz1mani merged commit 78d0ac6 into main Feb 7, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Feb 7, 2026

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
Loading

Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

13 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +26 to +28
const { slug } = await params
const integration = getIntegration(slug)
if (!integration) return {}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@@ -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} didn’t 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.1c4ca5773e357da2.js",revision:"1c4ca5773e357da2"},{url:"/_next/static/chunks/c9d33fe5-86a53cc7ffd40559.js",revision:"86a53cc7ffd40559"},{url:"/_next/static/chunks/ee560e2c-d269d85d3d10b933.js",revision:"d269d85d3d10b933"},{url:"/_next/static/chunks/f4898fe8-8bc2ae55335cc57d.js",revision:"8bc2ae55335cc57d"},{url:"/_next/static/chunks/framework-81b2e59ffe13bb24.js",revision:"81b2e59ffe13bb24"},{url:"/_next/static/chunks/main-2aa77a4a2c547977.js",revision:"2aa77a4a2c547977"},{url:"/_next/static/chunks/main-app-e646227faa58df82.js",revision:"e646227faa58df82"},{url:"/_next/static/chunks/next/dist/client/components/builtin/app-error-85628b53985e66de.js",revision:"85628b53985e66de"},{url:"/_next/static/chunks/next/dist/client/components/builtin/forbidden-85628b53985e66de.js",revision:"85628b53985e66de"},{url:"/_next/static/chunks/next/dist/client/components/builtin/global-error-68b36e74ca32f8b6.js",revision:"68b36e74ca32f8b6"},{url:"/_next/static/chunks/next/dist/client/components/builtin/unauthorized-85628b53985e66de.js",revision:"85628b53985e66de"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-4c5e072e59638097.js",revision:"4c5e072e59638097"},{url:"/_next/static/css/9f42e6b7c3cab03c.css",revision:"9f42e6b7c3cab03c"},{url:"/_next/static/media/636a5ac981f94f8b-s.p.woff2",revision:"52d04440a9faae0db9adc6cdc844099b"},{url:"/_next/static/media/6fe53d21e6e7ebd8-s.woff2",revision:"2591db816b61d44b6e87ba79d13622b2"},{url:"/_next/static/media/8ebc6e9dde468c4a-s.woff2",revision:"196acbb650c75807ea2f0ef36edbd186"},{url:"/_next/static/media/9e7b0a821b9dfcb4-s.woff2",revision:"5ffe46eeb00dd9fa8a70cb10ccc3817e"},{url:"/_next/static/yV0hJZnpczr3hupoivroz/_buildManifest.js",revision:"f603baae985f0e99def0a2c51922a169"},{url:"/_next/static/yV0hJZnpczr3hupoivroz/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/ciphera_icon_no_margins.png",revision:"eccd951fa34bd0901dcc37461b605e5b"},{url:"/favicon.ico",revision:"d256b0f8c516bbd3e93edf343876b462"},{url:"/icon-192x192.png",revision:"a9bae788a2253361a0e295deadbcc083"},{url:"/icon-512x512.png",revision:"d2efb02d7b9a0a627387d334201e6ea7"},{url:"/manifest.json",revision:"9c84335d179dcab05bf43ff586f2420d"},{url:"/pulse_icon_no_margins.png",revision:"a7cd4faa1c8e42ea5654451a31565642"},{url:"/pulse_logo_no_margins.png",revision:"6d6553cc7ea96597ffdcb69db9e0d11f"},{url:"/script.js",revision:"c579f58e59235c8575fb8262a26f411f"}],{ignoreURLParametersMatching:[/^utm_/,/^fbclid$/]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({response:e})=>e&&"opaqueredirect"===e.type?new Response(e.body,{status:200,statusText:"OK",headers:e.headers}):e}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:2592e3})]}),"GET"),e.registerRoute(/\/_next\/static.+\.js$/i,new e.CacheFirst({cacheName:"next-static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4|webm)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:48,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({sameOrigin:e,url:{pathname:s}})=>!(!e||s.startsWith("/api/auth/callback")||!s.startsWith("/api/")),new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({request:e,url:{pathname:s},sameOrigin:a})=>"1"===e.headers.get("RSC")&&"1"===e.headers.get("Next-Router-Prefetch")&&a&&!s.startsWith("/api/"),new e.NetworkFirst({cacheName:"pages-rsc-prefetch",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({request:e,url:{pathname:s},sameOrigin:a})=>"1"===e.headers.get("RSC")&&a&&!s.startsWith("/api/"),new e.NetworkFirst({cacheName:"pages-rsc",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:{pathname:e},sameOrigin:s})=>s&&!e.startsWith("/api/"),new e.NetworkFirst({cacheName:"pages",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({sameOrigin:e})=>!e,new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@uz1mani uz1mani self-assigned this Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant