first MVP
This commit is contained in:
115
DEVELOPMENT.md
Normal file
115
DEVELOPMENT.md
Normal file
@@ -0,0 +1,115 @@
|
||||
Boat — Local Development Runbook
|
||||
|
||||
This runbook explains how to run the Boat MVP locally, seed demo data, and troubleshoot common issues.
|
||||
|
||||
Project layout highlights
|
||||
- [app.page()](src/app/page.tsx:1) — Graph landing page that fetches /api/graph and renders the Cytoscape graph
|
||||
- [components/Graph.tsx](src/components/Graph.tsx:1) — Cytoscape wrapper with zoom/pan, fit, and selection details
|
||||
- [components/PersonForm.tsx](src/components/PersonForm.tsx:1) — Create person form
|
||||
- [components/PeopleSelect.tsx](src/components/PeopleSelect.tsx:1) — Typeahead selector against /api/people
|
||||
- [components/ConnectionForm.tsx](src/components/ConnectionForm.tsx:1) — Create connection form with ordered introducer chain
|
||||
- [app.people.new.page()](src/app/people/new/page.tsx:1) — Page for adding a person
|
||||
- [app.connections.new.page()](src/app/connections/new/page.tsx:1) — Page for adding a connection
|
||||
- [api.people.route()](src/app/api/people/route.ts:1), [api.people.id.route()](src/app/api/people/[id]/route.ts:1) — People API
|
||||
- [api.connections.route()](src/app/api/connections/route.ts:1), [api.connections.id.route()](src/app/api/connections/[id]/route.ts:1) — Connections API
|
||||
- [api.graph.route()](src/app/api/graph/route.ts:1) — Graph snapshot API
|
||||
- [lib.db()](src/lib/db.ts:1) — Prisma client singleton
|
||||
- [lib.validators()](src/lib/validators.ts:1) — Zod schemas
|
||||
- [prisma.schema()](prisma/schema.prisma:1) — Data model
|
||||
- [prisma.migration.sql](prisma/migrations/20251114191515_connection_pair_constraints/migration.sql:1) — Undirected uniqueness + self-edge constraints
|
||||
- [prisma.seed()](prisma/seed.ts:1) — Seed script for 20 demo people and ~30 connections
|
||||
|
||||
Prerequisites
|
||||
- Docker (to run local Postgres quickly)
|
||||
- Node 20+ and npm
|
||||
|
||||
1) Start Postgres (Docker)
|
||||
If you haven’t yet started the Docker Postgres container, run:
|
||||
- docker volume create boat-pgdata
|
||||
- docker run -d --name boat-postgres -p 5432:5432 -e POSTGRES_DB=boat -e POSTGRES_USER=boat -e POSTGRES_PASSWORD=boat -v boat-pgdata:/var/lib/postgresql/data postgres:16
|
||||
|
||||
Verify it’s running:
|
||||
- docker ps | grep boat-postgres
|
||||
|
||||
2) Environment variables
|
||||
This repo already includes [.env](.env:1) pointing to the Docker Postgres:
|
||||
DATABASE_URL="postgresql://boat:boat@localhost:5432/boat?schema=public"
|
||||
|
||||
3) Install deps and generate Prisma Client
|
||||
From the app directory:
|
||||
- npm install
|
||||
- npx prisma generate
|
||||
|
||||
4) Migrate the database
|
||||
- npx prisma migrate dev --name init
|
||||
- npx prisma migrate dev (if new migrations were added)
|
||||
|
||||
Note: Constraints for undirected uniqueness and self-edges are included in [prisma.migration.sql](prisma/migrations/20251114191515_connection_pair_constraints/migration.sql:1).
|
||||
|
||||
5) Seed demo data (20 people)
|
||||
- npm run db:seed
|
||||
|
||||
This executes [prisma.seed()](prisma/seed.ts:1) and inserts people and connections.
|
||||
|
||||
6) Run the Next.js dev server
|
||||
Important: run from the app directory, not workspace root.
|
||||
|
||||
- npm run dev
|
||||
|
||||
Access:
|
||||
- http://localhost:3000 (or the alternate port if 3000 is busy)
|
||||
|
||||
7) Using the app
|
||||
- Landing page shows the global graph
|
||||
- Toolbar buttons:
|
||||
- Add Person → [app.people.new.page()](src/app/people/new/page.tsx:1)
|
||||
- Add Connection → [app.connections.new.page()](src/app/connections/new/page.tsx:1)
|
||||
- Reload — refetches data for the graph
|
||||
- Click nodes or edges to show details in the side panel
|
||||
|
||||
8) API quick tests
|
||||
- List people:
|
||||
- curl "http://localhost:3000/api/people?limit=10"
|
||||
- Create person:
|
||||
- curl -X POST "http://localhost:3000/api/people" -H "Content-Type: application/json" -d '{"name":"Jane Demo","sectors":["agriculture"]}'
|
||||
- Create/update connection (undirected):
|
||||
- curl -X POST "http://localhost:3000/api/connections" -H "Content-Type: application/json" -d '{"personAId":"<idA>","personBId":"<idB>","introducedByChain":[],"eventLabels":["event:demo"]}'
|
||||
|
||||
Troubleshooting
|
||||
|
||||
A) “npm enoent Could not read package.json at /home/maxime/boat/package.json”
|
||||
- You ran npm in the workspace root. Use the app directory:
|
||||
- cd boat-web
|
||||
- npm run dev
|
||||
|
||||
B) “Unable to acquire lock … .next/dev/lock”
|
||||
- Another Next dev instance is running or a stale lock exists.
|
||||
- Kill dev: pkill -f "next dev" (Unix)
|
||||
- Remove lock: rm -f .next/dev/lock
|
||||
- Then: npm run dev
|
||||
|
||||
C) “Failed to load external module @prisma/client … cannot find module '.prisma/client/default'”
|
||||
- Prisma client must be generated after schema changes or misconfigured generator.
|
||||
- Ensure generator in [prisma.schema()](prisma/schema.prisma:7) is:
|
||||
generator client { provider = "prisma-client-js" }
|
||||
- Regenerate: npx prisma generate
|
||||
- If still failing, remove stale output and regenerate:
|
||||
- rm -rf node_modules/.prisma
|
||||
- npx prisma generate
|
||||
|
||||
D) Port 3000 already in use
|
||||
- Run on a different port:
|
||||
- npm run dev -- -p 3001
|
||||
|
||||
Tech notes
|
||||
- The undirected edge uniqueness is enforced via functional unique index on LEAST/GREATEST and a no-self-edge CHECK in [prisma.migration.sql](prisma/migrations/20251114191515_connection_pair_constraints/migration.sql:1).
|
||||
- Deleting a person cascades to connections (MVP behavior).
|
||||
- Sectors, interests, and eventLabels are free-text arrays (TEXT[]).
|
||||
- Introduced-by chain is an ordered list of person IDs (existing people only).
|
||||
- UI intentionally minimal and open as per MVP brief.
|
||||
|
||||
Acceptance checklist mapping
|
||||
- Create person: [api.people.route()](src/app/api/people/route.ts:1) + [PersonForm.tsx](src/components/PersonForm.tsx:1) ✔
|
||||
- Create connection: [api.connections.route()](src/app/api/connections/route.ts:1) + [ConnectionForm.tsx](src/components/ConnectionForm.tsx:1) ✔
|
||||
- Global graph view: [api.graph.route()](src/app/api/graph/route.ts:1) + [Graph.tsx](src/components/Graph.tsx:1) ✔
|
||||
- Persistence: Postgres via Prisma ✔
|
||||
Reference in New Issue
Block a user