# Taskpile A task manager with force-directed graph visualization. ## Architecture - **Frontend**: Next.js 14 (App Router) + React 18 + Tailwind CSS 3 + TypeScript - **Backend**: Rust (Axum 0.7) + SQLite (via SQLx) - **Graph**: `react-force-graph-2d` for force-directed visualization ## Project Structure ``` frontend/ src/app/page.tsx — Main page: tabs, panels, task state management src/app/layout.tsx — Root layout src/components/ GraphView.tsx — Force graph with node selection, drag-to-center TaskList.tsx — Pending/completed task list with selection TaskItem.tsx — Individual task card AddTaskForm.tsx — New task form TaskDetailPanel.tsx — Right panel: selected task details UserPanel.tsx — Left panel: user profile (example data) ForceGraphClient.tsx — ForceGraph2D ref wrapper for dynamic import src/lib/ api.ts — API client (fetch wrappers) types.ts — TypeScript interfaces src/__tests__/ unit/ — Jest unit tests (API, TaskItem) e2e/ — Jest integration tests (full user flows) backend/ src/main.rs — Axum server on port 3001 src/models.rs — Task, GraphNode, GraphEdge structs src/db.rs — SQLite pool + migrations src/graph.rs — Deterministic edge generation (~30% pairs) src/routes/tasks.rs — CRUD: GET/POST /api/tasks, PATCH/DELETE /api/tasks/:id src/routes/graph.rs — GET /api/graph tests/integration_test.rs — Axum integration tests ``` ## Running ```bash # Backend (port 3001) cd backend && cargo run # Frontend (port 3003, proxies /api to backend) cd frontend && npm run dev -- -p 3003 ``` Port 3000 is used by Gitea on this machine — use 3003 for the frontend. ## Testing ```bash # Frontend tests cd frontend && npx jest # Backend tests cd backend && cargo test ``` ## Key Design Decisions - Task IDs are UUIDs (TEXT in SQLite, string from backend). Frontend `Task.id` is typed as `number` but actually receives strings — selection uses string comparison throughout. - Graph tab and task list are switched via tabs in the center area. Left panel (user info) and right panel (task details) are independently foldable. - Selecting a task (from list or graph node click) animates the node to the center of the viewport (drag-style, not camera pan). The node stays pinned until a different task is selected. - `ForceGraph2D` canvas dimensions are driven by a `ResizeObserver` on a container div that always renders. The canvas is only mounted after the first measurement to avoid a 300x300 default. - Panel transitions are 300ms CSS; graph re-fit/re-center delays 350ms to wait for the transition. ## API All endpoints under `/api`: | Method | Path | Description | |--------|------|-------------| | GET | /tasks | List all tasks | | POST | /tasks | Create task `{title, description?}` | | PATCH | /tasks/:id | Update task `{title?, description?, completed?}` | | DELETE | /tasks/:id | Delete task | | GET | /graph | Get nodes + weighted edges | ## Proxy Do not use system proxy env vars when testing the app locally — `curl --noproxy '*'` or equivalent.