# 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: ```yaml --- 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`, `bamboohr` → `BambooHR`, `microsoft-todo` → `Microsoft 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`: ```typescript 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: ```typescript 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 `