Next.js frontend showcase for the FeatureFusion lab. This directory is not in FeatureFusion.sln — the solution stays .NET-only.
The lab API, Aspire AppHost, and BuildingBlocks packages live under src/. This directory is the TypeScript UI that calls the Demo Commerce HTTP surfaces and the pagination keyset route. It is the evidence made visible — every feature map back to an experiment or package the lab proved.
| Layer | Choice |
|---|---|
| Framework | Next.js (App Router) |
| Styling | Tailwind CSS + shadcn/ui |
| Data | fetch via lib/fetcher.ts |
| State | React hooks + context (sidebar provider) |
| Package manager | Yarn |
Five features, ordered by lab value. Each one maps directly to a BuildingBlock or experiment the lab proved on real infrastructure.
The flagship. An infinite-scroll product catalog driven by opaque keyset cursors.
- Endpoint:
GET /api/v1/products-page?limit=20&sortBy=Price&sortDirection=Ascending - Sort switcher: Id · Name · Price · CreatedAt, Ascending / Descending
- Bidirectional navigation: forward via
NextCursor, backward viaPreviousCursor - Cursors are opaque — pass them back unchanged; do not construct them
This is exactly what Experiments 1–2 stress-tested (careless cursor clients). A UI that passes cursors back unchanged is the evidence made visible.
Mapped to: BuildingBlocks.Pagination.EntityFrameworkCore 1.1.0 — typed SortKey, composite keyset order, Npgsql row comparison, NULLS FIRST/LAST.
A retry-safe order creation flow that demonstrates HTTP idempotency as a visible UX state machine.
- Endpoint:
POST /api/v1/Order/orderwith a client-generatedIdempotency-Key(UUID) - Retry on network failure: the UI resends the same key and shows the 2xx envelope replay — no double order
- Visible statuses:
200— created202— AdmissionPending (polling state; the lab's Allow/Defer/Deny admission gate becomes a visible state)409— conflict
Mapped to: BuildingBlocks.Idempotency 1.0.1 — 2xx envelope replay, ProblemDetails, optional Redis lock. Proven by Experiments 3, 4, 12.
A chat-like panel where a user "asks" the app and the UI renders MCP tool calls + results. The most distinctive idea — it turns the lab's MCP research into a live demo rather than a static screenshot.
- Endpoint:
/mcpwith toolsproducts.list,orders.create,catalog.*,customers.* - UX: user types a request → UI calls the MCP server → renders tool name, parameters, and result inline
- Fits the repo's MCP/MAF research thread: the Cursor screenshot in the root README is a static proof; this is a live one
Mapped to: BuildingBlocks.Mcp 1.1.0 — [McpTool] / MapTool → MCP tools at /mcp.
Login with VIP credentials to see conditional components render based on claim filters. The VIP greeting demo becomes end-user-visible.
- Login:
POST /api/v1/Auth/loginwithvipuser/vippassword - Gated component: VIP-only greeting rendered when the JWT contains the right claim
- Feature filter preview:
GET /api/v1/lab/feature-filter-preview
Mapped to: Lab Feature Management + custom claims filters. Not a BuildingBlock — this is lab-only evidence.
Propagate W3C traceparent from the browser fetch → API → SigNoz, then show "view this request in SigNoz" links. Experiment 18 proved traces don't cross RabbitMQ — a UI that shows the HTTP-leg correlation fills a real documentation gap.
- Propagation:
fetchincludestraceparentheader → API picks it up → SigNoz link in the UI - SigNoz UI:
http://localhost:8080(Aspire-provisioned)
Mapped to: BuildingBlocks.Telemetry 1.0.2 + BuildingBlocks.Aspire.Hosting.SigNoz 1.0.0.
web/
├── app/
│ ├── layout.tsx # Root layout (sidebar + providers)
│ ├── page.tsx # Home page
│ ├── catalog/
│ │ ├── page.tsx # Catalog page (server component)
│ │ └── _components/
│ │ └── catalog.tsx # Catalog client component
│ └── globals.css
├── components/
│ ├── sidebar/ # App sidebar navigation
│ └── ui/ # shadcn/ui primitives
├── hooks/ # Custom React hooks
├── lib/
│ ├── fetcher.ts # Typed fetch wrapper
│ ├── constants/ # Route constants
│ └── utils.ts # cn() and helpers
├── models/ # TypeScript type definitions
├── providers/ # React context providers
├── public/ # Static assets
├── package.json
├── tsconfig.json
├── next.config.ts
└── postcss.config.mjs
cd web
yarn install
yarn devThe UI expects the FeatureFusion API to be running (via Aspire AppHost or Docker Compose). Point it at the API's HTTP endpoint — the Aspire dashboard shows the dynamic port.
- Evidence-driven: every feature maps to a proven experiment or package. No cargo-cult UI.
- Consume, don't duplicate: the UI calls the public HTTP/MCP APIs. It does not re-implement BuildingBlocks contracts in the client.
- Out of the .sln: the .NET solution stays .NET-only unless there is a deliberate reason to bind them.
- Opaque cursors: pagination cursors are passed back unchanged. The UI never decodes or constructs them.