Skip to content
Last updated

Marketplace navigation and searchable catalog — design

Date: 2026-05-13 Area: agen-for-work/connectors/, @theme/, scripts/ Type: Documentation IA + tooling addition

Problem

agen-for-work/connectors/marketplace/ contains 96 integration guides. They are all listed alphabetically as a flat list inside the Marketplace group in agen-for-work/sidebars.yaml (lines 53–268), producing a 96-item "wall of text" in the sidebar.

For a user who knows the integration they need ("find Slack"), this is slow: scroll an unsegmented list of 96 items, or use the global Redocly search and hope it ranks the connector page first. For a user evaluating coverage ("do you support any CRM?"), there is no overview by domain — they cannot scan categories.

The product UI already groups connectors by category (see agen-for-work/sources/overview.md and the Marketplace tab → Category dropdown in-product), but no such structure exists in documentation.

Goals

  1. Reorganize the sidebar so the 96-item flat list becomes 22 collapsible category groups, collapsed by default; Redocly auto-expands the group of the active page.
  2. Introduce a single landing page agen-for-work/connectors/marketplace.md that renders a searchable, category-sectioned catalog of all connectors.
  3. Reuse the authoritative category taxonomy that exists in the dashboard codebase (ConnectorCategory enum and IntegrationCategoryMap in dashboard/app/v2/src/tools/stores/envStore/workforceApp/workforceApp.types.ts), so docs and product agree on category names without ad-hoc curation.
  4. Make the catalog data derive from per-file frontmatter (single source of truth in each connector's markdown), regenerated to a TypeScript data file by an explicit npm run generate:connectors step.
  5. Add CI validation so a connector cannot be added without a valid category.

Non-goals

  • No icon or logo assets per integration. The docs repo has none today (static/ contains only Frontegg logos and one stray slack.svg); sourcing and licensing 96 third-party brand assets is a separate project.
  • No per-connector description: on tiles. Names alone suffice for the "knows the name" path; descriptions can be added later as another optional frontmatter field.
  • No equivalent catalog for ciam/ or agen-for-saas/. If this pattern works, replicate it there afterwards.
  • No localization. Catalog UI strings ("Search 96 connectors…", "All categories", "No connectors match your search") are English.
  • No live sync with the dashboard repo. We copy the taxonomy into docs once (via bootstrap) and treat docs frontmatter as the source of truth thereafter.
  • No e2e or visual-regression tests. Smoke verification via npm run dev preview is enough for v1.

Design

1. Frontmatter schema

Today none of the 96 marketplace markdown files have any frontmatter — they start straight with ## Connector_name integration. The bootstrap (Phase 2) adds a frontmatter block to every file:

---
category: Communication                  # NEW — required, string from enum
displayName: Slack                       # NEW — optional, overrides humanized filename
---
  • category must be one of the 30 strings in ConnectorCategory (in dashboard enum) — values like Productivity, Communication, Project Management, CRM, Development, Storage, Finance, Customer Support, Marketing, E-commerce, Analytics, Monitoring, DevOps, HR, Video Conferencing, Scheduling, Identity, Design, Forms, IT Service Management, Documents, Automation, Data, Social Media, Feature Flags, Content Management, Sales Intelligence, Product Management, Infrastructure, AI. (Only 22 of these are populated by current docs.)
  • displayName overrides the filename-derived label (e.g. monday monday.com, bamboohrBambooHR, microsoft-todoMicrosoft To Do).

A one-time bootstrap script reads the dashboard's IntegrationCategoryMap and inserts category into all 96 files. After that, frontmatter is hand-maintained.

2. Source of truth — category enum

The 30 categories live in dashboard/app/v2/src/tools/stores/envStore/workforceApp/workforceApp.types.ts as the ConnectorCategory enum. We copy the enum verbatim (string values only) into a small file in this repo so the validator and component can reference it without depending on the dashboard package:

@theme/markdoc/components/ConnectorCatalog/categories.ts:

export const CONNECTOR_CATEGORIES = [
  'Productivity',
  'Communication',
  'Project Management',
  // ... 27 more, in the same order as the dashboard enum
] as const

export type ConnectorCategory = (typeof CONNECTOR_CATEGORIES)[number]

Order is the enum's source order; this is also the order used by sidebar groups and on the landing page.

3. Generator — scripts/generate-connectors.mjs

Inputs: agen-for-work/connectors/marketplace/*.md.

Outputs: @theme/markdoc/components/ConnectorCatalog/connectors.generated.ts, shape:

import type { ConnectorCategory } from './categories'

export interface ConnectorEntry {
  id: string            // filename without .md, e.g. 'slack'
  name: string          // displayName from frontmatter, or humanized id
  category: ConnectorCategory
  href: string          // 'connectors/marketplace/slack.md'
}

export const CONNECTORS: ConnectorEntry[] = [/* generated */]

Behavior:

  • Reads each markdown, extracts frontmatter via gray-matter (already available via Redocly toolchain — confirm during phase 0, otherwise add the dep).
  • Validates each file has category and that it is in CONNECTOR_CATEGORIES; fails the run otherwise.
  • Sorts entries by (category index in CONNECTOR_CATEGORIES, name).
  • Writes the data file with a // AUTO-GENERATED — do not edit header.
  • Idempotent: running twice produces identical output.

The script is exposed as npm run generate:connectors in package.json.

4. CI freshness check — scripts/check-marketplace-frontmatter.mjs

Exposed as npm run check:marketplace-frontmatter. Pattern matches the existing check:content-style script: a standalone Node script invoked separately (not bundled into npm run test, which is reserved for redocly lint). CI runs all three checks (test, check:content-style, check:marketplace-frontmatter) and fails on any.

Responsibilities:

  1. Re-run the generator into an in-memory string.
  2. Compare against the committed connectors.generated.ts. Fail with a clear message if they differ: "connectors.generated.ts is stale — run npm run generate:connectors and commit the result."
  3. Validate that every file in agen-for-work/connectors/marketplace/*.md:
    • has category in frontmatter;
    • category is in CONNECTOR_CATEGORIES;
    • is referenced exactly once in agen-for-work/sidebars.yaml.
  4. Validate that every entry under the Marketplace group in sidebars.yaml points to a file that exists on disk.

No new tooling beyond gray-matter and js-yaml (the latter likely already present for Redocly config parsing — verify in phase 0).

5. React component — @theme/markdoc/components/ConnectorCatalog/

Files:

ConnectorCatalog/
├── ConnectorCatalog.tsx         # React component (styled-components)
├── ConnectorCatalog.markdoc.ts  # Markdoc schema (no attributes)
├── categories.ts                # CONNECTOR_CATEGORIES, ConnectorCategory type
├── connectors.generated.ts      # generated, committed
└── index.ts                     # barrel export

Registered in @theme/markdoc/components.tsx and @theme/markdoc/schema.ts following the existing ProductCard pattern.

Component behavior:

  • Imports CONNECTORS from connectors.generated.ts and CONNECTOR_CATEGORIES from categories.ts. No props.
  • Internal state: query: string, category: 'all' | ConnectorCategory.
  • Top bar (sticky? no — keep simple): search input aria-label="Search connectors" and a <select> populated from the categories that are actually present in CONNECTORS (skip empty ones), in enum order, prefixed by an All categories option.
  • Body:
    • No filters active (default): render an <h3> section per category that has entries, followed by a grid of tiles. Tiles are plain <a href={connector.href}> with the connector name centered.
    • Search input non-empty: collapse all sections into one alphabetically sorted flat grid of matches. Match is case-insensitive substring on name.
    • Category dropdown set: render only that one category's section (still show the H3 for context).
    • Both active: filter by category, then by query, render as a flat grid under one H3.
    • Empty result: show <p>No connectors match your search.</p> followed by a Clear filters button that resets both state values.
  • Styling matches ProductCard conventions (styled-components, var(--icon-card-*) theme variables). Tile is a bordered rounded box with hover state; grid uses CSS grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)).
  • No animations beyond the standard 0.2s border-color transition on hover.

The Markdoc tag has zero attributes:

export const ConnectorCatalog: Schema & { tagName: string } = {
  attributes: {},
  render: 'ConnectorCatalog',
  tagName: 'ConnectorCatalog',
}

Usage in markdown: {% ConnectorCatalog /%}.

6. New landing page — agen-for-work/connectors/marketplace.md

---
title: Marketplace
---

## Marketplace

Browse all available connectors. Use the search field to find a connector by
name, or filter by category.

Heading rules from scripts/check-content-style.mjs are respected (first H2, no H1, no emojis).

7. Sidebar restructure — agen-for-work/sidebars.yaml

The current flat Marketplace group (one items: block with 96 entries) is replaced by a Marketplace group whose page: is the new landing and whose items: is a list of 22 category subgroups, in enum order:

- group: Marketplace
  page: connectors/marketplace.md
  expanded: false
  items:
    - group: Productivity
      expanded: false
      items:
        - label: Airtable
          page: connectors/marketplace/airtable.md
        - label: Confluence
          page: connectors/marketplace/confluence.md
        # … alphabetical within group
    - group: Communication
      expanded: false
      items:
        - label: Discord
          page: connectors/marketplace/discord.md
        # …
    # … 20 more category subgroups in enum order

Within each category, items are alphabetical by label. Category groups appear in CONNECTOR_CATEGORIES order. Categories with no docs entries are omitted.

Redocly Realm auto-expands the group containing the active page, so no custom behaviour is needed for the "collapsed-but-active-group-open" UX.

8. Updates to existing pages

  • agen-for-work/sources/overview.md — keep as-is. The Marketplace section there describes the product UI; the new landing page describes the docs catalog. They co-exist without overlap.
  • No changes to the 96 connector markdown files beyond the new frontmatter fields. Body content is untouched.

9. Execution phases (one PR, atomic commits)

  1. Phase 0 — verify deps. Confirm gray-matter and js-yaml are available in the toolchain; add them as devDependencies if not.
  2. Phase 1 — taxonomy + generator + validator. Add categories.ts, scripts/generate-connectors.mjs, scripts/check-marketplace-frontmatter.mjs. Add generate:connectors and check:marketplace-frontmatter to package.json scripts (each standalone, matching the existing check:content-style pattern).
  3. Phase 2 — bootstrap frontmatter. One-shot script that reads IntegrationCategoryMap from the dashboard repo (path supplied via env var) and writes category: into all 96 markdown files. The script lives in scripts/bootstrap-connector-categories.mjs and is deleted after the PR merges (one-time use).
  4. Phase 3 — component. Add ConnectorCatalog.tsx, ConnectorCatalog.markdoc.ts, index.ts; register in @theme/markdoc/components.tsx and schema.ts.
  5. Phase 4 — landing page. Add agen-for-work/connectors/marketplace.md.
  6. Phase 5 — sidebar restructure. Replace the flat list in agen-for-work/sidebars.yaml with category subgroups.
  7. Phase 6 — generate + commit data file. Run npm run generate:connectors, commit connectors.generated.ts.
  8. Phase 7 — validation. npm run test, npm run check:content-style, npm run dev smoke-check.

Validation

  • npm run test — Redocly lint. Must pass.
  • npm run check:content-style — unchanged rules; new marketplace.md and any other touched markdown must pass.
  • npm run check:marketplace-frontmatter — new validator. Must pass.
  • Local smoke (npm run dev):
    • /agen-for-work/connectors/marketplace renders the catalog with 96 tiles distributed across 22 H3 sections, in enum order.
    • Typing in the search input filters tiles live, with no flicker.
    • Selecting a category in the dropdown narrows to that section.
    • Empty result state appears for nonsense queries.
    • Sidebar shows Marketplace collapsed; expanding shows 22 collapsed subgroups; clicking any connector auto-expands its category group.

Open risks

  • gray-matter/js-yaml availability. Either is a small dep, but if Redocly's Markdoc pipeline strips them at runtime we may need to bundle frontmatter parsing into the generator only (which runs at dev time, so this is fine). Resolve in Phase 0.
  • Bootstrap reliability. The bootstrap script depends on a sibling dashboard/ checkout at a known relative path. Document the env var (DASHBOARD_REPO=/path/to/dashboard) in the script. If the path is wrong, fail loud.
  • Category drift. Once docs frontmatter is the source of truth, future dashboard renames are not auto-propagated. The validator only checks CONNECTOR_CATEGORIES, so an out-of-date categories.ts will silently diverge from product. Mitigation: include a comment in categories.ts pointing at the dashboard file and a date stamp; refresh manually when product changes are announced.
  • Sidebar diff size. Replacing 216 lines of flat YAML with a nested structure makes the PR diff noisy. Acceptable cost; review by reading the rendered preview rather than the YAML diff.
  • Search performance. 96 entries × substring match is trivial; no debouncing or indexing needed. If the catalog grows past ~500, revisit.