AI Unified Process UI layer - Next.js App Router pages on an API layer, and their component tests
94%
Does it follow best practices?
Impact
96%
1.00x1 of 3 eval scenarios. Add 2 more for a full score.
Passed
No findings from the security scan
The UI layer of UC-010 "Browse Product Catalog": a list with a category filter and an empty state.
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.
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 />;
}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>
);
}