chore: mark all 59 tasks as completed in OpenSpec
This commit is contained in:
parent
23f18f405a
commit
9b5bc9b6b3
1 changed files with 59 additions and 59 deletions
|
|
@ -1,91 +1,91 @@
|
|||
## 1. Project Setup & Configuration
|
||||
|
||||
- [ ] 1.1 Initialize Next.js project with App Router, TypeScript, and Tailwind CSS using `npx create-next-app@latest` (with src/ directory, App Router, Tailwind CSS enabled)
|
||||
- [ ] 1.2 Install core dependencies: `lightweight-charts`, `papaparse`, `lucide-react`, and their type packages (`@types/papaparse`)
|
||||
- [ ] 1.3 Install database dependencies: `drizzle-orm`, `better-sqlite3`, `@types/better-sqlite3`, and `drizzle-kit` (dev dependency)
|
||||
- [ ] 1.4 Initialize shadcn/ui with dark theme defaults (`npx shadcn-ui@latest init` with slate color scheme)
|
||||
- [ ] 1.5 Add shadcn/ui components needed: Button, Tooltip (via `npx shadcn-ui@latest add button tooltip`)
|
||||
- [ ] 1.6 Configure `drizzle.config.ts` pointing to local SQLite database file (`./data/candles.db`)
|
||||
- [x] 1.1 Initialize Next.js project with App Router, TypeScript, and Tailwind CSS using `npx create-next-app@latest` (with src/ directory, App Router, Tailwind CSS enabled)
|
||||
- [x] 1.2 Install core dependencies: `lightweight-charts`, `papaparse`, `lucide-react`, and their type packages (`@types/papaparse`)
|
||||
- [x] 1.3 Install database dependencies: `drizzle-orm`, `better-sqlite3`, `@types/better-sqlite3`, and `drizzle-kit` (dev dependency)
|
||||
- [x] 1.4 Initialize shadcn/ui with dark theme defaults (`npx shadcn-ui@latest init` with slate color scheme)
|
||||
- [x] 1.5 Add shadcn/ui components needed: Button, Tooltip (via `npx shadcn-ui@latest add button tooltip`)
|
||||
- [x] 1.6 Configure `drizzle.config.ts` pointing to local SQLite database file (`./data/candles.db`)
|
||||
|
||||
## 2. Database Schema & ORM Setup
|
||||
|
||||
- [ ] 2.1 Create `src/lib/db/schema.ts` defining the `candles` table: id (integer PK auto-increment), time (integer, unique), open (real), high (real), low (real), close (real)
|
||||
- [ ] 2.2 Add `annotations` table to `src/lib/db/schema.ts`: id (integer PK auto-increment), timestamp (integer), label_type (text), geometry (text, nullable), created_at (integer)
|
||||
- [ ] 2.3 Create `src/lib/db/index.ts` with Drizzle client instance using better-sqlite3 driver, connecting to `./data/candles.db`
|
||||
- [ ] 2.4 Create `src/lib/db/migrate.ts` to run Drizzle migrations on app startup (ensure tables exist)
|
||||
- [ ] 2.5 Generate initial migration with `npx drizzle-kit generate` and verify schema creates correctly
|
||||
- [ ] 2.6 Test database connection by running the migration and confirming tables exist in SQLite
|
||||
- [x] 2.1 Create `src/lib/db/schema.ts` defining the `candles` table: id (integer PK auto-increment), time (integer, unique), open (real), high (real), low (real), close (real)
|
||||
- [x] 2.2 Add `annotations` table to `src/lib/db/schema.ts`: id (integer PK auto-increment), timestamp (integer), label_type (text), geometry (text, nullable), created_at (integer)
|
||||
- [x] 2.3 Create `src/lib/db/index.ts` with Drizzle client instance using better-sqlite3 driver, connecting to `./data/candles.db`
|
||||
- [x] 2.4 Create `src/lib/db/migrate.ts` to run Drizzle migrations on app startup (ensure tables exist)
|
||||
- [x] 2.5 Generate initial migration with `npx drizzle-kit generate` and verify schema creates correctly
|
||||
- [x] 2.6 Test database connection by running the migration and confirming tables exist in SQLite
|
||||
|
||||
## 3. Backend API — Data Ingestion
|
||||
|
||||
- [ ] 3.1 Create `src/app/api/upload/route.ts` with POST handler that accepts multipart form data containing a CSV file
|
||||
- [ ] 3.2 Implement CSV parsing using papaparse: validate required headers (time, open, high, low, close), handle both date strings and Unix timestamps for the time column
|
||||
- [ ] 3.3 Implement batch insert of parsed candle records into the `candles` table within a single SQLite transaction, with upsert behavior on duplicate timestamps
|
||||
- [ ] 3.4 Return JSON response: `{ success: true, count: N }` on success, `{ error: "message" }` with HTTP 400 on failure
|
||||
- [ ] 3.5 Test upload endpoint manually with a sample CSV file
|
||||
- [x] 3.1 Create `src/app/api/upload/route.ts` with POST handler that accepts multipart form data containing a CSV file
|
||||
- [x] 3.2 Implement CSV parsing using papaparse: validate required headers (time, open, high, low, close), handle both date strings and Unix timestamps for the time column
|
||||
- [x] 3.3 Implement batch insert of parsed candle records into the `candles` table within a single SQLite transaction, with upsert behavior on duplicate timestamps
|
||||
- [x] 3.4 Return JSON response: `{ success: true, count: N }` on success, `{ error: "message" }` with HTTP 400 on failure
|
||||
- [x] 3.5 Test upload endpoint manually with a sample CSV file
|
||||
|
||||
## 4. Backend API — Annotations CRUD
|
||||
|
||||
- [ ] 4.1 Create `src/app/api/annotations/route.ts` with GET handler returning all annotations as JSON array (parsing geometry from JSON string)
|
||||
- [ ] 4.2 Add POST handler to the annotations route: validate required fields (timestamp, label_type), serialize geometry to JSON string if present, insert into database, return created object with HTTP 201
|
||||
- [ ] 4.3 Create `src/app/api/annotations/[id]/route.ts` with DELETE handler: remove annotation by ID, return 200 on success, 404 if not found
|
||||
- [ ] 4.4 Create `src/app/api/candles/route.ts` with GET handler returning all candle records ordered by time ascending as JSON array
|
||||
- [x] 4.1 Create `src/app/api/annotations/route.ts` with GET handler returning all annotations as JSON array (parsing geometry from JSON string)
|
||||
- [x] 4.2 Add POST handler to the annotations route: validate required fields (timestamp, label_type), serialize geometry to JSON string if present, insert into database, return created object with HTTP 201
|
||||
- [x] 4.3 Create `src/app/api/annotations/[id]/route.ts` with DELETE handler: remove annotation by ID, return 200 on success, 404 if not found
|
||||
- [x] 4.4 Create `src/app/api/candles/route.ts` with GET handler returning all candle records ordered by time ascending as JSON array
|
||||
|
||||
## 5. Backend API — Export
|
||||
|
||||
- [ ] 5.1 Create `src/app/api/export/route.ts` with GET handler that queries all annotations and formats them as CSV (columns: timestamp, label_type, price)
|
||||
- [ ] 5.2 For marker annotations (break_up, break_down), look up the candle's close price by timestamp; for line annotations, use startPrice from geometry
|
||||
- [ ] 5.3 Set response headers: `Content-Type: text/csv` and `Content-Disposition: attachment; filename="annotations.csv"`
|
||||
- [ ] 5.4 Handle empty annotations case by returning CSV with header row only
|
||||
- [x] 5.1 Create `src/app/api/export/route.ts` with GET handler that queries all annotations and formats them as CSV (columns: timestamp, label_type, price)
|
||||
- [x] 5.2 For marker annotations (break_up, break_down), look up the candle's close price by timestamp; for line annotations, use startPrice from geometry
|
||||
- [x] 5.3 Set response headers: `Content-Type: text/csv` and `Content-Disposition: attachment; filename="annotations.csv"`
|
||||
- [x] 5.4 Handle empty annotations case by returning CSV with header row only
|
||||
|
||||
## 6. UI Shell & Layout
|
||||
|
||||
- [ ] 6.1 Update `src/app/layout.tsx` to set dark theme: Slate-900 background, light text, proper font and metadata
|
||||
- [ ] 6.2 Create `src/app/page.tsx` as the main page with a flexbox layout: fixed-width sidebar on the left, chart area filling remaining space
|
||||
- [ ] 6.3 Create `src/components/Toolbox.tsx` sidebar component with tool buttons using lucide-react icons: "Label: Break Up" (ArrowUpCircle), "Label: Break Down" (ArrowDownCircle), "Draw Line" (Minus/TrendingUp), "Delete" (Trash2)
|
||||
- [ ] 6.4 Implement active tool state management: clicking a tool highlights it, clicking again deactivates it, only one tool active at a time
|
||||
- [ ] 6.5 Add the Export button to the sidebar that triggers a download from GET /api/export (using an anchor tag or `window.location`)
|
||||
- [x] 6.1 Update `src/app/layout.tsx` to set dark theme: Slate-900 background, light text, proper font and metadata
|
||||
- [x] 6.2 Create `src/app/page.tsx` as the main page with a flexbox layout: fixed-width sidebar on the left, chart area filling remaining space
|
||||
- [x] 6.3 Create `src/components/Toolbox.tsx` sidebar component with tool buttons using lucide-react icons: "Label: Break Up" (ArrowUpCircle), "Label: Break Down" (ArrowDownCircle), "Draw Line" (Minus/TrendingUp), "Delete" (Trash2)
|
||||
- [x] 6.4 Implement active tool state management: clicking a tool highlights it, clicking again deactivates it, only one tool active at a time
|
||||
- [x] 6.5 Add the Export button to the sidebar that triggers a download from GET /api/export (using an anchor tag or `window.location`)
|
||||
|
||||
## 7. File Upload Component
|
||||
|
||||
- [ ] 7.1 Create `src/components/FileUpload.tsx` as a client component with a file input that accepts `.csv` files
|
||||
- [ ] 7.2 On file selection, send the file to POST /api/upload using FormData and fetch
|
||||
- [ ] 7.3 Display success message with row count or error message from the API response
|
||||
- [ ] 7.4 After successful upload, trigger a callback to refresh candle data on the chart
|
||||
- [ ] 7.5 Place the FileUpload component in the sidebar (inside Toolbox or above it)
|
||||
- [x] 7.1 Create `src/components/FileUpload.tsx` as a client component with a file input that accepts `.csv` files
|
||||
- [x] 7.2 On file selection, send the file to POST /api/upload using FormData and fetch
|
||||
- [x] 7.3 Display success message with row count or error message from the API response
|
||||
- [x] 7.4 After successful upload, trigger a callback to refresh candle data on the chart
|
||||
- [x] 7.5 Place the FileUpload component in the sidebar (inside Toolbox or above it)
|
||||
|
||||
## 8. Candlestick Chart Component
|
||||
|
||||
- [ ] 8.1 Create `src/components/CandleChart.tsx` as a client component ("use client") that initializes a lightweight-charts chart instance in a useEffect/useRef pattern
|
||||
- [ ] 8.2 Fetch candle data from GET /api/candles on mount and set it on a CandlestickSeries
|
||||
- [ ] 8.3 Apply dark theme options to the chart: dark background matching Slate-900, subtle grid lines, light crosshair and text colors
|
||||
- [ ] 8.4 Enable built-in interactivity: crosshair, zoom (mouse wheel), pan (drag on time axis)
|
||||
- [ ] 8.5 Implement chart resize handling: use ResizeObserver on the container div to call `chart.resize()` when dimensions change
|
||||
- [ ] 8.6 Fetch annotations from GET /api/annotations on mount and convert marker annotations (break_up, break_down) to lightweight-charts marker format, apply with `series.setMarkers()`
|
||||
- [ ] 8.7 Expose a `refreshData` method (via useImperativeHandle or callback prop) so parent can trigger re-fetching candles and annotations after upload or annotation changes
|
||||
- [x] 8.1 Create `src/components/CandleChart.tsx` as a client component ("use client") that initializes a lightweight-charts chart instance in a useEffect/useRef pattern
|
||||
- [x] 8.2 Fetch candle data from GET /api/candles on mount and set it on a CandlestickSeries
|
||||
- [x] 8.3 Apply dark theme options to the chart: dark background matching Slate-900, subtle grid lines, light crosshair and text colors
|
||||
- [x] 8.4 Enable built-in interactivity: crosshair, zoom (mouse wheel), pan (drag on time axis)
|
||||
- [x] 8.5 Implement chart resize handling: use ResizeObserver on the container div to call `chart.resize()` when dimensions change
|
||||
- [x] 8.6 Fetch annotations from GET /api/annotations on mount and convert marker annotations (break_up, break_down) to lightweight-charts marker format, apply with `series.setMarkers()`
|
||||
- [x] 8.7 Expose a `refreshData` method (via useImperativeHandle or callback prop) so parent can trigger re-fetching candles and annotations after upload or annotation changes
|
||||
|
||||
## 9. Annotation Click Handling
|
||||
|
||||
- [ ] 9.1 Subscribe to chart click events: on chart click, use `chart.timeScale().coordinateToTime(x)` and `series.coordinateToPrice(y)` to convert pixel coordinates to data coordinates
|
||||
- [ ] 9.2 For "break_up" and "break_down" active tools: snap clicked time to nearest candle timestamp, POST annotation to /api/annotations with label_type and timestamp, then refresh markers on the chart
|
||||
- [ ] 9.3 For "delete" active tool on marker click: identify if a marker annotation exists at the clicked candle timestamp, if so call DELETE /api/annotations/[id] and refresh markers
|
||||
- [ ] 9.4 Handle edge cases: clicking outside the data range (no valid time coordinate), clicking when no tool is active (do nothing)
|
||||
- [x] 9.1 Subscribe to chart click events: on chart click, use `chart.timeScale().coordinateToTime(x)` and `series.coordinateToPrice(y)` to convert pixel coordinates to data coordinates
|
||||
- [x] 9.2 For "break_up" and "break_down" active tools: snap clicked time to nearest candle timestamp, POST annotation to /api/annotations with label_type and timestamp, then refresh markers on the chart
|
||||
- [x] 9.3 For "delete" active tool on marker click: identify if a marker annotation exists at the clicked candle timestamp, if so call DELETE /api/annotations/[id] and refresh markers
|
||||
- [x] 9.4 Handle edge cases: clicking outside the data range (no valid time coordinate), clicking when no tool is active (do nothing)
|
||||
|
||||
## 10. Line Drawing — SVG Overlay
|
||||
|
||||
- [ ] 10.1 Create `src/components/SvgOverlay.tsx` as a transparent SVG element absolutely positioned over the chart container, matching the chart's dimensions
|
||||
- [ ] 10.2 Implement coordinate transformation functions: convert data coordinates (time, price) to SVG pixel coordinates using `chart.timeScale().timeToCoordinate()` and `series.priceToCoordinate()`
|
||||
- [ ] 10.3 Render existing line annotations by fetching from GET /api/annotations, filtering for label_type "line", and drawing SVG `<line>` elements using transformed coordinates
|
||||
- [ ] 10.4 Subscribe to chart visible range changes (`timeScale().subscribeVisibleTimeRangeChange()`) and re-render SVG lines when user zooms or pans
|
||||
- [ ] 10.5 Implement two-click line drawing: first click stores start point (time, price), mouse move draws preview line from start to cursor, second click saves the line via POST /api/annotations with geometry JSON
|
||||
- [ ] 10.6 Implement Escape key to cancel in-progress line drawing (clear preview, reset state)
|
||||
- [ ] 10.7 Implement line deletion: when "delete" tool is active, detect clicks near an existing SVG line (within a pixel tolerance), call DELETE /api/annotations/[id] for that line
|
||||
- [x] 10.1 Create `src/components/SvgOverlay.tsx` as a transparent SVG element absolutely positioned over the chart container, matching the chart's dimensions
|
||||
- [x] 10.2 Implement coordinate transformation functions: convert data coordinates (time, price) to SVG pixel coordinates using `chart.timeScale().timeToCoordinate()` and `series.priceToCoordinate()`
|
||||
- [x] 10.3 Render existing line annotations by fetching from GET /api/annotations, filtering for label_type "line", and drawing SVG `<line>` elements using transformed coordinates
|
||||
- [x] 10.4 Subscribe to chart visible range changes (`timeScale().subscribeVisibleTimeRangeChange()`) and re-render SVG lines when user zooms or pans
|
||||
- [x] 10.5 Implement two-click line drawing: first click stores start point (time, price), mouse move draws preview line from start to cursor, second click saves the line via POST /api/annotations with geometry JSON
|
||||
- [x] 10.6 Implement Escape key to cancel in-progress line drawing (clear preview, reset state)
|
||||
- [x] 10.7 Implement line deletion: when "delete" tool is active, detect clicks near an existing SVG line (within a pixel tolerance), call DELETE /api/annotations/[id] for that line
|
||||
|
||||
## 11. Integration & Final Wiring
|
||||
|
||||
- [ ] 11.1 Wire up state flow in page.tsx: active tool state passed from Toolbox to CandleChart and SvgOverlay, upload callback from FileUpload triggers chart refresh
|
||||
- [ ] 11.2 Ensure annotations refresh after every create/delete operation (markers update via setMarkers, lines update via SVG re-render)
|
||||
- [ ] 11.3 Add empty state handling: when no candles are loaded, display a message prompting the user to upload a CSV file
|
||||
- [ ] 11.4 Verify full workflow end-to-end: upload CSV → view candles → place Break Up marker → place Break Down marker → draw a line → delete an annotation → export CSV
|
||||
- [ ] 11.5 Create DEPLOYMENT.md with steps to run the app locally (npm install, database setup, npm run dev)
|
||||
- [ ] 11.6 Create README.md with project overview, tech stack, and usage instructions
|
||||
- [x] 11.1 Wire up state flow in page.tsx: active tool state passed from Toolbox to CandleChart and SvgOverlay, upload callback from FileUpload triggers chart refresh
|
||||
- [x] 11.2 Ensure annotations refresh after every create/delete operation (markers update via setMarkers, lines update via SVG re-render)
|
||||
- [x] 11.3 Add empty state handling: when no candles are loaded, display a message prompting the user to upload a CSV file
|
||||
- [x] 11.4 Verify full workflow end-to-end: upload CSV → view candles → place Break Up marker → place Break Down marker → draw a line → delete an annotation → export CSV
|
||||
- [x] 11.5 Create DEPLOYMENT.md with steps to run the app locally (npm install, database setup, npm run dev)
|
||||
- [x] 11.6 Create README.md with project overview, tech stack, and usage instructions
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue