deckhearth/.cursor/skills/add-page/SKILL.md

110 lines
3.7 KiB
Markdown
Raw Permalink Normal View History

bootstrap: agent pipeline v0.5.0 + ship-readiness review Installs the three-layer agent-pipeline scaffold (https://github.com/varutasu/agent-pipeline @ v0.5.0): L1 — Context (curated brain) - AGENTS.md: orientation, conventions, 8 explicit gotchas - .cursor/rules/: no-go-zones, api-routes, auth-and-permissions, db-and-schema, ui-and-theming, schema-map - .cursor/skills/: add-api-route, add-page recipes - docs/agent-context/README.md: layer explainer - docs/SCHEMA_MAP.md: hand-curated Neon Postgres reference (replaces Prisma schema map since stack is raw SQL) L2 — Subagent roles (copied verbatim from upstream templates) - 9 .cursor/agents/role-*.md files: Conductor, IA-Architect, UX-Reviewer, Architect, Implementer, Reviewer, Design-System-Auditor, A11y-Auditor, Doc-Writer L3 — Pipeline scaffolding (Vercel variant) - CI: lint + schema-map-drift only (no duplicate build — Vercel handles it). Test job commented out until vitest lands. - preview-smoke + visual-diff via wait-for-vercel-preview - pr-health-rollup sticky comment aggregator - agent-context-drift weekly cron - PULL_REQUEST_TEMPLATE, CODEOWNERS (auth/admin paths tagged) - .convoys/ folder + seed ship-readiness.md review - lib/flags/index.js (JS — converted from TS template) - scripts/wt.sh (Cursor 3.2 deprecation stub), scripts/log-convoy-event.sh - tests/smoke/app.smoke.spec.ts (Playwright skeleton) Manifest - .agent-context-manifest.yml: tracks 31 artifacts by sha256 for future sync-agent-context drift detection Review - .convoys/ship-readiness.md: 16 findings (7 P0 ship-blockers, 5 P1 quality-bar, 4 P2 refactor, P3 UX/IA/a11y/docs) with proposed 13-convoy launch sequence. No production code changed in this commit. All findings in the ship-readiness review will be addressed in follow-up convoys starting with fix-auth-bypass. Structural brain: user-code-review-graph MCP has indexed the codebase (122 files, 628 nodes, 5602 edges, 11 communities, 84 flows). Per-developer; not committed. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
---
name: add-page
description: >-
Add a new Next.js page under pages/. Use when you need a new route, a new
view for an existing resource, or an admin-only screen. Covers Layout
wiring, auth, theme tokens, and the public/authenticated split pattern.
---
# Add a page
`pages/<file>.js` becomes a route. Pages router conventions:
| File | Route |
| --- | --- |
| `pages/about.js` | `/about` |
| `pages/cards/[id].js` | `/cards/:id` (but use `pages/card/[id].js` per existing naming) |
| `pages/admin/index.js` | `/admin` |
## Step 1: Decide auth shape
| Mode | Template |
| --- | --- |
| Public-only | Render without `ProtectedRoute`; no auth check |
| Auth-required | Wrap top-level export with `<ProtectedRoute>` |
| Admin-only | Wrap with `<AdminProtected>` from `components/AdminProtected.js` |
| Public + auth-enhanced (e.g. `/cards`) | Inline split: render `<PublicView>` if not logged in, `<AuthedView>` if logged in. Copy the pattern from `pages/cards.js`. |
## Step 2: Skeleton
```js
import { useState, useEffect } from 'react';
import Layout from '../components/Layout';
import ProtectedRoute from '../components/ProtectedRoute';
import { useAuth } from '../lib/use-auth';
export default function MyPage() {
return (
<ProtectedRoute>
<MyPageInner />
</ProtectedRoute>
);
}
function MyPageInner() {
const { user, loading } = useAuth();
const [items, setItems] = useState([]);
const [fetching, setFetching] = useState(false);
useEffect(() => {
if (loading || !user) return;
const token = localStorage.getItem('auth_token');
setFetching(true);
fetch('/api/my-resource', {
headers: { Authorization: `Bearer ${token}` },
})
.then((r) => r.json())
.then((data) => setItems(data.items || []))
.catch((err) => console.error('fetch failed', err))
.finally(() => setFetching(false));
}, [user, loading]);
return (
<Layout user={user} showSearch={false}>
<div className="p-6" style={{ backgroundColor: 'var(--bg-primary)' }}>
<h1 className="text-2xl font-bold" style={{ color: 'var(--text-primary)' }}>
My Page
</h1>
{fetching ? <p>Loading…</p> : items.map((i) => <div key={i.id}>{i.name}</div>)}
</div>
</Layout>
);
}
```
## Step 3: Theme tokens (not hex)
- Backgrounds → `var(--bg-primary)`, `var(--bg-secondary)`, `var(--bg-tertiary)`
- Text → `var(--text-primary)`, `var(--text-secondary)`
- Accents → `var(--accent-ember)`, `var(--accent-flame)`
- Borders → `var(--border)`
Use Tailwind for layout, spacing, sizing, hover/focus states. Use CSS vars (inline `style={{ ... }}`) for colors that need to switch with theme.
## Step 4: Always pass `user` to Layout
`<Layout user={user}>` — never let the default kick in (it's a hardcoded maintainer email; see `AGENTS.md` Gotcha #8).
## Step 5: Mobile
`components/Layout.js` already handles the mobile drawer + bottom nav. To add the page to nav, edit `NavigationContent` in `Layout.js`. Use existing icon names from the `getIcon` registry; add new ones to that registry before referencing.
## Step 6: Check
- [ ] Auth wrapper chosen (ProtectedRoute / AdminProtected / public).
- [ ] `useAuth()` from `lib/use-auth.js` (not the legacy `lib/auth-context.js`).
- [ ] `user` passed to Layout explicitly.
- [ ] Colors come from theme tokens, not hex.
- [ ] All interactive elements have `aria-label` or visible text.
- [ ] Mobile: confirm the page renders in the mobile drawer.
## Anti-patterns
| Don't | Do |
| --- | --- |
| Hardcode hex colors | Use CSS variables |
| Default `user = { … }` to a real email | Default to `null` |
| Pull from `lib/auth-context` for new code | Use `lib/use-auth` |
| Render Layout twice on the same page | Single `<Layout>` at the top |