Summary
The “New Project” button on the Sparki dashboard navigated to/dashboard/projects/new, but that URL was caught by the [id] dynamic route segment, which treated "new" as a project ID. The API returned HTML (404 page) instead of JSON, causing the "Unexpected token '<', '<!DOCTYPE'..." parse error. Additionally, the “New Project” button on the projects list page (/dashboard/projects) was a dead <button> with no click handler or link.
Root cause
1. Missing static route for /projects/new
The file system routing structure was:
[id] dynamic segment matches every path segment. Without a static new/ directory, "new" is treated as a project ID. The useProject("new") hook fires, getProject("new") calls GET /api/projects/new, the server returns its HTML fallback, and response.json() throws the parse error.
2. Unwired button on projects list page
/dashboard/projects/page.tsx line 132 rendered a <button> element with the correct visual styling but zero interactivity — no href, no onClick, no Link wrapper. The dashboard page (/dashboard/page.tsx line 268) correctly used <Link href="/dashboard/projects/new">, but the projects page didn’t match.
Fix applied
Static route created
ProjectSetupWizard component (from @/components/forms/Wizards) and useCreateProject hook (from @/hooks). On successful creation, it redirects to /dashboard/projects/{id}. On cancel, it returns to the projects list.
Dead button replaced with Link
The<button> on the projects list page was replaced with <Link href="/dashboard/projects/new">, keeping the same visual styling.
Key takeaways
-
Static routes before dynamic routes — In Next.js App Router, always create explicit static route directories for known path segments (
new,edit,settings) before relying on[param]catch-alls. This is a common footgun when adding CRUD routes. -
Test the full navigation flow — The dashboard test (
page.test.tsx:85-99) correctly asserted that the “New Project” link pointed to/dashboard/projects/new, but there was no corresponding test or page to handle that URL. Integration tests should cover the destination, not just the source. -
Buttons vs Links — Interactive elements that navigate should always be
<Link>(or<a>), not<button>. Buttons without handlers are invisible failures — no error, no feedback, just nothing happens. -
Existing components were ready — The
ProjectSetupWizardanduseCreateProjecthook were already built and waiting. The fix was purely a routing/wiring issue, not a missing feature.