Portfolio (Case Studies)
Routes
| Route | Purpose |
|---|---|
/work | Full project grid with filter chips |
/work/[category] | Pre-filtered grid for one category |
/work/[category]/[slug] | Cinematic case study detail page |
Key rule: filtered pages must pre-filter
/work/[category]/page.tsx must filter projects to the active category AND pass defaultCategory to ProjectGrid. Rendering the grid without pre-filtering produces a blank or all-projects page.
Components
ProjectGrid
Client Component. Accepts projects + optional defaultCategory. Renders FilterChips + stagger-animated Framer Motion grid.
ProjectCard
Server Component. Links to /work/[category]/[slug]. Featured cards get a cyan glow border (featured === true).
Case study page components
| Component | Type | What it renders |
|---|---|---|
ProjectHero | Client | 60vh hero — iframe for video, gradient for image |
ProjectInfo | Server | Two-column: title/description/deliverables + meta |
ProjectGallery | Client | Lightbox image grid (only if gallery has items) |
ProjectProcess | Server | Numbered step list from processSteps |
ProjectResults | Client | Stat card row from results + outcome prose |
ProjectNav | Client | Previous / Next project cards |
Static generation
export async function generateStaticParams() {
const projects = await getAllProjects({ status: 'published' });
return projects.map((p) => ({ category: p.category, slug: p.slug }));
}
Add export const dynamicParams = false once project data is stable — unknown slugs 404 at the routing layer.
Admin: creating a case study from a lead
On any lead detail page → QuickActions card → "Create Case Study" maps the lead data into a draft project and redirects to /admin/projects/[id] to finish filling it out.