CtrlK
BlogDocsLog inGet started
Tessl Logo
PluginPublic

ai-unified-process/aiup-nextjs

AI Unified Process UI layer - Next.js App Router pages on an API layer, and their component tests

1.00x
Quality

94%

Does it follow best practices?

Impact

96%

1.00x

1 of 3 eval scenarios. Add 2 more for a full score.

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files

worked-example.mdskills/nextjs-ui/references/

Worked example: products

The UI layer of UC-010 "Browse Product Catalog": a list with a category filter and an empty state.

No route indirection

Where the detection found no route indirection, the route file holds the page:

// src/app/products/page.tsx
'use client';

import { useEffect, useState } from 'react';
import type { Product } from '../../types/product';

/** UC-010: Browse Product Catalog */
export default function ProductsPage() {
  const [products, setProducts] = useState<Product[]>([]);
  const [category, setCategory] = useState('');
  const [errorMessage, setErrorMessage] = useState<string | null>(null);

  useEffect(() => {
    const query = category ? `?category=${encodeURIComponent(category)}` : '';
    fetch(`/api/products${query}`)
      .then((res) => {
        if (!res.ok) throw new Error(`GET /api/products answered ${res.status}`);
        return res.json();
      })
      .then(setProducts)
      .catch(() => setErrorMessage('Products could not be loaded.'));
  }, [category]);

  return (
    <main>
      <h1>Products</h1>
      {errorMessage && <p role="alert">{errorMessage}</p>}
      <label htmlFor="category">Category</label>
      <select id="category" value={category} onChange={(e) => setCategory(e.target.value)}>
        <option value="">All</option>
        <option value="tools">Tools</option>
      </select>
      {products.length === 0 ? (
        <p>No products found.</p>
      ) : (
        <ul>
          {products.map((product) => (
            <li key={product.id}>
              {product.name} — {product.price}
            </li>
          ))}
        </ul>
      )}
    </main>
  );
}

The texts stand in for what the specification states; a message it does not state is a gap, not a choice.

Route indirection

Where the detection found route indirection, the route file is a thin wrapper and the body above lives in the view component, which also carries the /** UC-010: … */ comment:

// src/app/products/page.tsx
'use client';
import { ProductsPage } from '../../views/ProductsPage';

export default function Page() {
  return <ProductsPage />;
}

A page that reads the URL

useSearchParams must sit under a <Suspense> boundary in the route file, or static generation fails at build time with an error that names the hook but not the fix:

// src/app/products/page.tsx
'use client';
import { Suspense } from 'react';
import { ProductsPage } from '../../views/ProductsPage';

export default function Page() {
  return (
    <Suspense>
      <ProductsPage />
    </Suspense>
  );
}

.mcp.json

LICENSE

README.md

tile.json