archive: multi-chart-management change complete
This commit is contained in:
parent
603b08209b
commit
3da4987f89
10 changed files with 0 additions and 0 deletions
|
|
@ -0,0 +1,80 @@
|
|||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Upload endpoint
|
||||
The system SHALL provide a `POST /api/upload` endpoint that accepts a CSV file via multipart form data. The endpoint SHALL create a new chart (named from the uploaded filename without extension), parse the CSV using papaparse, validate the format, and insert all candle records into the `candles` table with the new chart's `chart_id` within a single database transaction. On success, the endpoint SHALL return a JSON response with the chart `id`, chart `name`, and the count of inserted records. On failure, it SHALL return an appropriate error status and message without creating a chart.
|
||||
|
||||
#### Scenario: Successful upload
|
||||
- **WHEN** a valid CSV file named "BTC-1H.csv" is sent to POST /api/upload
|
||||
- **THEN** endpoint creates a chart named "BTC-1H", inserts candles with that chart_id, and returns `{ "success": true, "count": <number>, "chart": { "id": <number>, "name": "BTC-1H" } }` with HTTP 200
|
||||
|
||||
#### Scenario: Invalid CSV upload
|
||||
- **WHEN** a CSV with missing or invalid headers is sent to POST /api/upload
|
||||
- **THEN** endpoint returns `{ "error": "<description>" }` with HTTP 400 and does not create a chart
|
||||
|
||||
#### Scenario: No file provided
|
||||
- **WHEN** POST /api/upload is called without a file
|
||||
- **THEN** endpoint returns `{ "error": "No file provided" }` with HTTP 400
|
||||
|
||||
#### Scenario: Duplicate filename
|
||||
- **WHEN** a CSV named "BTC-1H.csv" is uploaded and a chart named "BTC-1H" already exists
|
||||
- **THEN** endpoint creates a chart named "BTC-1H-2" (or next available suffix) and inserts candles under that chart
|
||||
|
||||
### Requirement: Get annotations endpoint
|
||||
The system SHALL provide a `GET /api/annotations` endpoint that accepts an optional `chartId` query parameter. When `chartId` is provided, the endpoint SHALL return only annotations belonging to that chart. When `chartId` is omitted, the endpoint SHALL return annotations for the most recently created chart. Each annotation object SHALL include: `id`, `chart_id`, `timestamp`, `label_type`, `geometry` (parsed from JSON string or null), and `created_at`.
|
||||
|
||||
#### Scenario: Fetch annotations for specific chart
|
||||
- **WHEN** GET /api/annotations?chartId=3 is called
|
||||
- **THEN** endpoint returns a JSON array of annotations where chart_id equals 3, with HTTP 200
|
||||
|
||||
#### Scenario: Fetch annotations without chartId
|
||||
- **WHEN** GET /api/annotations is called without a chartId parameter
|
||||
- **THEN** endpoint returns annotations for the most recently created chart with HTTP 200
|
||||
|
||||
#### Scenario: No annotations exist for chart
|
||||
- **WHEN** GET /api/annotations?chartId=3 is called and no annotations exist for chart 3
|
||||
- **THEN** endpoint returns an empty JSON array `[]` with HTTP 200
|
||||
|
||||
### Requirement: Create annotation endpoint
|
||||
The system SHALL provide a `POST /api/annotations` endpoint that accepts a JSON body with fields: `timestamp` (required, integer), `label_type` (required, string), `chart_id` (required, integer), and `geometry` (optional, object). The endpoint SHALL validate the input, verify the chart exists, serialize geometry to JSON string if present, and insert the record into the `annotations` table. On success, it SHALL return the created annotation object with its assigned `id`.
|
||||
|
||||
#### Scenario: Create a marker annotation
|
||||
- **WHEN** POST /api/annotations is called with `{ "timestamp": 1700000000, "label_type": "break_up", "chart_id": 3 }`
|
||||
- **THEN** endpoint saves the annotation with chart_id 3 and returns the created object with `id` and HTTP 201
|
||||
|
||||
#### Scenario: Create a line annotation
|
||||
- **WHEN** POST /api/annotations is called with `{ "timestamp": 1700000000, "label_type": "line", "chart_id": 3, "geometry": { "startTime": 1700000000, "startPrice": 1.05, "endTime": 1700100000, "endPrice": 1.06 } }`
|
||||
- **THEN** endpoint saves the annotation with chart_id 3 and serialized geometry JSON, returns the created object with HTTP 201
|
||||
|
||||
#### Scenario: Invalid annotation data
|
||||
- **WHEN** POST /api/annotations is called with missing required fields (timestamp, label_type, or chart_id)
|
||||
- **THEN** endpoint returns `{ "error": "<description>" }` with HTTP 400
|
||||
|
||||
#### Scenario: Annotation for non-existent chart
|
||||
- **WHEN** POST /api/annotations is called with a chart_id that does not exist
|
||||
- **THEN** endpoint returns `{ "error": "Chart not found" }` with HTTP 404
|
||||
|
||||
### Requirement: Get candles endpoint
|
||||
The system SHALL provide a `GET /api/candles` endpoint that accepts an optional `chartId` query parameter. When `chartId` is provided, the endpoint SHALL return only candles belonging to that chart. When `chartId` is omitted, the endpoint SHALL return candles for the most recently created chart. Results SHALL be ordered by time ascending. Each object SHALL include: `time`, `open`, `high`, `low`, `close`.
|
||||
|
||||
#### Scenario: Fetch candles for specific chart
|
||||
- **WHEN** GET /api/candles?chartId=3 is called
|
||||
- **THEN** endpoint returns a JSON array of candle objects where chart_id equals 3, ordered by time ascending with HTTP 200
|
||||
|
||||
#### Scenario: Fetch candles without chartId
|
||||
- **WHEN** GET /api/candles is called without a chartId parameter
|
||||
- **THEN** endpoint returns candles for the most recently created chart with HTTP 200
|
||||
|
||||
#### Scenario: No candles exist for chart
|
||||
- **WHEN** GET /api/candles?chartId=3 is called and no candles exist for chart 3
|
||||
- **THEN** endpoint returns an empty JSON array `[]` with HTTP 200
|
||||
|
||||
### Requirement: Export annotations endpoint
|
||||
The system SHALL provide a `GET /api/export` endpoint that accepts an optional `chartId` query parameter. When `chartId` is provided, the endpoint SHALL export only annotations for that chart. When `chartId` is omitted, the endpoint SHALL export annotations for the most recently created chart. The CSV SHALL have columns: `timestamp`, `label_type`, `price`. The response SHALL set `Content-Type: text/csv` and `Content-Disposition: attachment; filename="annotations.csv"` headers.
|
||||
|
||||
#### Scenario: Export for specific chart
|
||||
- **WHEN** GET /api/export?chartId=3 is called and annotations exist for chart 3
|
||||
- **THEN** endpoint returns a CSV file download with only annotations belonging to chart 3
|
||||
|
||||
#### Scenario: Export without chartId
|
||||
- **WHEN** GET /api/export is called without a chartId parameter
|
||||
- **THEN** endpoint exports annotations for the most recently created chart
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Candlestick chart rendering
|
||||
The system SHALL render candle data as a candlestick chart using the `lightweight-charts` library (v4). The chart MUST display OHLC data with candlestick visuals using a black and white color scheme. Bullish candles SHALL have a white interior with black outline and black wicks. Bearish candles SHALL be completely black (black fill and black wicks). The chart SHALL be a client-side React component. The chart SHALL fetch and display candles scoped to the active chart by passing `chartId` to the `GET /api/candles` endpoint.
|
||||
|
||||
#### Scenario: Chart renders candle data for active chart
|
||||
- **WHEN** an active chart is selected and candle data exists for that chart
|
||||
- **THEN** the chart fetches candles via `GET /api/candles?chartId=<activeChartId>` and renders only that chart's candles
|
||||
|
||||
#### Scenario: Empty state
|
||||
- **WHEN** no charts exist or the active chart has no candle data
|
||||
- **THEN** the chart area displays an empty chart with a prompt to upload CSV data
|
||||
|
||||
#### Scenario: Bullish candle appearance
|
||||
- **WHEN** a candle's close price is higher than its open price (bullish)
|
||||
- **THEN** the candle displays with white interior, black border, and black wick
|
||||
|
||||
#### Scenario: Bearish candle appearance
|
||||
- **WHEN** a candle's close price is lower than its open price (bearish)
|
||||
- **THEN** the candle displays as completely black (black fill and black wick)
|
||||
|
||||
#### Scenario: Chart switches when active chart changes
|
||||
- **WHEN** user selects a different chart from the chart selector
|
||||
- **THEN** the chart clears existing data, fetches candles for the newly selected chart, and renders the new dataset
|
||||
|
||||
### Requirement: Annotation markers on chart
|
||||
The chart SHALL display visual markers for existing annotations using the `series.setMarkers()` API. Break Up annotations MUST appear as green upward arrows above the bar. Break Down annotations MUST appear as red downward arrows below the bar. Markers MUST update when annotations are added or deleted. Markers SHALL be scoped to the active chart by fetching annotations via `GET /api/annotations?chartId=<activeChartId>`.
|
||||
|
||||
#### Scenario: Break Up marker display
|
||||
- **WHEN** a Break Up annotation exists for a candle timestamp in the active chart
|
||||
- **THEN** a green upward arrow marker appears above that candle on the chart
|
||||
|
||||
#### Scenario: Break Down marker display
|
||||
- **WHEN** a Break Down annotation exists for a candle timestamp in the active chart
|
||||
- **THEN** a red downward arrow marker appears below that candle on the chart
|
||||
|
||||
#### Scenario: Marker updates on annotation change
|
||||
- **WHEN** user adds or deletes an annotation on the active chart
|
||||
- **THEN** chart markers update immediately without requiring a page reload
|
||||
|
||||
#### Scenario: Markers refresh on chart switch
|
||||
- **WHEN** user switches to a different chart
|
||||
- **THEN** markers from the previous chart are cleared and markers for the new chart's annotations are loaded
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Charts database table
|
||||
The system SHALL store chart datasets in a `charts` table with columns: `id` (integer primary key, auto-increment), `name` (text, unique), `created_at` (integer, Unix timestamp). Each chart represents a distinct uploaded CSV dataset.
|
||||
|
||||
#### Scenario: Schema structure
|
||||
- **WHEN** the database is initialized
|
||||
- **THEN** the `charts` table exists with all required columns and constraints
|
||||
|
||||
#### Scenario: Unique chart names
|
||||
- **WHEN** a chart is created with a name that already exists
|
||||
- **THEN** the system appends a numeric suffix (e.g., "btc-daily-2") to ensure uniqueness
|
||||
|
||||
### Requirement: Chart selector in sidebar
|
||||
The system SHALL display a chart selector component in the sidebar, positioned between the app title/header section and the file upload section. The selector SHALL show the name of the currently active chart and allow the user to switch between all available charts.
|
||||
|
||||
#### Scenario: Chart selector renders with charts
|
||||
- **WHEN** one or more charts exist in the database
|
||||
- **THEN** the sidebar displays a selector showing the active chart name, with a dropdown listing all available charts sorted by creation date (newest first)
|
||||
|
||||
#### Scenario: Chart selector with no charts
|
||||
- **WHEN** no charts exist in the database
|
||||
- **THEN** the selector displays a placeholder message "No charts — upload a CSV to get started"
|
||||
|
||||
#### Scenario: Switch active chart
|
||||
- **WHEN** user selects a different chart from the selector dropdown
|
||||
- **THEN** the system updates `activeChartId` state, fetches candles and annotations for the selected chart, and re-renders the chart and sidebar label list
|
||||
|
||||
#### Scenario: Active chart persistence on page load
|
||||
- **WHEN** the page loads and charts exist
|
||||
- **THEN** the system selects the most recently created chart as the active chart
|
||||
|
||||
### Requirement: Delete chart
|
||||
The system SHALL allow users to delete a chart and all its associated candles and annotations.
|
||||
|
||||
#### Scenario: Delete chart with confirmation
|
||||
- **WHEN** user clicks the delete button next to a chart in the selector
|
||||
- **THEN** the system displays a confirmation dialog: "Delete chart '{name}' and all its candles and annotations? This cannot be undone."
|
||||
|
||||
#### Scenario: Confirm chart deletion
|
||||
- **WHEN** user confirms chart deletion
|
||||
- **THEN** the system deletes the chart, all its candles, and all its annotations from the database, removes the chart from the selector, and switches to the next available chart (or shows empty state if none remain)
|
||||
|
||||
#### Scenario: Cancel chart deletion
|
||||
- **WHEN** user cancels chart deletion
|
||||
- **THEN** no data is deleted and the dialog closes
|
||||
|
||||
#### Scenario: Delete last chart
|
||||
- **WHEN** user deletes the only remaining chart
|
||||
- **THEN** the system shows the empty state with no chart selected and prompts the user to upload a CSV
|
||||
|
||||
### Requirement: List charts API
|
||||
The system SHALL provide a `GET /api/charts` endpoint that returns all charts as a JSON array, ordered by `created_at` descending. Each chart object SHALL include: `id`, `name`, `created_at`.
|
||||
|
||||
#### Scenario: Fetch all charts
|
||||
- **WHEN** GET /api/charts is called
|
||||
- **THEN** endpoint returns a JSON array of chart objects ordered by created_at descending with HTTP 200
|
||||
|
||||
#### Scenario: No charts exist
|
||||
- **WHEN** GET /api/charts is called and no charts exist
|
||||
- **THEN** endpoint returns an empty JSON array `[]` with HTTP 200
|
||||
|
||||
### Requirement: Delete chart API
|
||||
The system SHALL provide a `DELETE /api/charts/[id]` endpoint that deletes a chart and all its associated candles and annotations within a single transaction.
|
||||
|
||||
#### Scenario: Delete existing chart
|
||||
- **WHEN** DELETE /api/charts/5 is called and chart with id 5 exists
|
||||
- **THEN** endpoint deletes the chart, all candles with chart_id 5, and all annotations with chart_id 5, then returns `{ "success": true }` with HTTP 200
|
||||
|
||||
#### Scenario: Delete non-existent chart
|
||||
- **WHEN** DELETE /api/charts/999 is called and no chart with that id exists
|
||||
- **THEN** endpoint returns `{ "error": "Chart not found" }` with HTTP 404
|
||||
|
||||
### Requirement: Auto-select new chart after upload
|
||||
The system SHALL automatically switch to the newly created chart after a successful CSV upload.
|
||||
|
||||
#### Scenario: Upload creates and selects chart
|
||||
- **WHEN** user uploads a CSV and the upload succeeds
|
||||
- **THEN** the system creates a new chart, inserts candle data for that chart, adds the chart to the selector, and sets it as the active chart
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: CSV file upload
|
||||
The system SHALL provide a file upload component that accepts CSV files containing OHLC candle data. The CSV format MUST have columns: `time`, `open`, `high`, `low`, `close`. The `time` column SHALL accept both `YYYY-MM-DD` date strings and Unix timestamps (integer seconds). Uploading a CSV SHALL create a new chart (named from the filename without extension) and insert all candle rows associated with that chart, rather than replacing existing data.
|
||||
|
||||
#### Scenario: Valid CSV upload
|
||||
- **WHEN** user uploads a CSV file with valid headers (time, open, high, low, close) and valid data rows
|
||||
- **THEN** system creates a new chart named from the filename (without .csv extension), parses all rows, and stores them in the `candles` table with the new chart's `chart_id`
|
||||
|
||||
#### Scenario: CSV with Unix timestamps
|
||||
- **WHEN** user uploads a CSV where the `time` column contains Unix timestamps (e.g., 1700000000)
|
||||
- **THEN** system stores the timestamps as integers in the database with the correct `chart_id` and renders candles correctly on the chart
|
||||
|
||||
#### Scenario: CSV with date strings
|
||||
- **WHEN** user uploads a CSV where the `time` column contains date strings (e.g., "2024-01-15")
|
||||
- **THEN** system converts dates to Unix timestamps and stores them in the database with the correct `chart_id`
|
||||
|
||||
#### Scenario: Invalid CSV format
|
||||
- **WHEN** user uploads a CSV missing required headers or containing malformed data
|
||||
- **THEN** system displays an error message describing the issue, does not create a chart, and does not store any partial data
|
||||
|
||||
#### Scenario: Duplicate filename upload
|
||||
- **WHEN** user uploads a CSV whose filename (without extension) matches an existing chart name
|
||||
- **THEN** system appends a numeric suffix to the chart name (e.g., "btc-daily-2") and creates a new chart with the suffixed name
|
||||
|
||||
### Requirement: Candles database table
|
||||
The system SHALL store candle data in a `candles` table with columns: `id` (integer primary key, auto-increment), `chart_id` (integer, foreign key to `charts.id`, NOT NULL), `time` (integer, Unix timestamp), `open` (real), `high` (real), `low` (real), `close` (real). The table MUST have a composite unique constraint on `(chart_id, time)`.
|
||||
|
||||
#### Scenario: Schema structure
|
||||
- **WHEN** the database is initialized
|
||||
- **THEN** the `candles` table exists with all required columns including `chart_id` and the composite unique constraint on `(chart_id, time)`
|
||||
|
||||
#### Scenario: Same timestamp across different charts
|
||||
- **WHEN** two different charts have candles with the same Unix timestamp
|
||||
- **THEN** both records are stored successfully because the unique constraint is per-chart
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Label list in sidebar
|
||||
The Toolbox SHALL display a collapsible section showing all label annotations for the active chart with interactive controls.
|
||||
|
||||
#### Scenario: Display label list section
|
||||
- **WHEN** Toolbox renders with an active chart selected
|
||||
- **THEN** system displays "Label Annotations" section below the annotation tools with collapse/expand toggle button, showing only annotations belonging to the active chart
|
||||
|
||||
#### Scenario: Label list expanded
|
||||
- **WHEN** "Label Annotations" section is expanded
|
||||
- **THEN** system displays scrollable list of label annotations for the active chart sorted by timestamp (newest first), with each entry showing timestamp, label type badge, and delete button
|
||||
|
||||
#### Scenario: Label list collapsed
|
||||
- **WHEN** user clicks collapse button on "Label Annotations" section
|
||||
- **THEN** system hides the label list but shows count summary "Labels: X break_up, Y break_down" for the active chart
|
||||
|
||||
#### Scenario: Empty label list
|
||||
- **WHEN** no label annotations exist for the active chart
|
||||
- **THEN** section displays message "No labels yet. Click Break Up or Break Down tools to add labels."
|
||||
|
||||
#### Scenario: Label entry format
|
||||
- **WHEN** displaying a label in the list
|
||||
- **THEN** each entry shows formatted timestamp (e.g., "Feb 12, 14:30"), colored badge ("BREAK UP" in green or "BREAK DOWN" in red), and trash icon delete button
|
||||
|
||||
#### Scenario: Label list updates on chart switch
|
||||
- **WHEN** user switches to a different chart via the chart selector
|
||||
- **THEN** the label list clears and reloads with annotations belonging to the newly selected chart
|
||||
|
||||
### Requirement: Label count display
|
||||
The Toolbox SHALL display counts of each label type for the active chart.
|
||||
|
||||
#### Scenario: Display label counts
|
||||
- **WHEN** Toolbox renders and labels exist for the active chart
|
||||
- **THEN** system displays count summary "Break Up: X | Break Down: Y" at top of label section, reflecting only the active chart's annotations
|
||||
|
||||
#### Scenario: Zero labels
|
||||
- **WHEN** no labels exist for the active chart
|
||||
- **THEN** count summary displays "Break Up: 0 | Break Down: 0"
|
||||
|
||||
#### Scenario: Count updates after delete
|
||||
- **WHEN** user deletes a label from the active chart
|
||||
- **THEN** count summary updates immediately to reflect the new totals
|
||||
|
||||
#### Scenario: Counts update on chart switch
|
||||
- **WHEN** user switches to a different chart
|
||||
- **THEN** count summary updates to reflect the new chart's label counts
|
||||
|
||||
### Requirement: Delete all labels with confirmation
|
||||
The system SHALL provide a "Delete All Labels" button with confirmation dialog. The deletion SHALL only affect labels belonging to the active chart.
|
||||
|
||||
#### Scenario: Click Delete All Labels button
|
||||
- **WHEN** user clicks "Delete All Labels" button in Toolbox
|
||||
- **THEN** system displays confirmation dialog with message "Delete all label annotations for this chart (Break Up and Break Down)? This cannot be undone." and Cancel/Confirm buttons
|
||||
|
||||
#### Scenario: User confirms delete all labels
|
||||
- **WHEN** confirmation dialog is open and user clicks Confirm button
|
||||
- **THEN** system deletes only label annotations belonging to the active chart, removes all label markers from chart, clears label list, clears selection state, triggers annotation refresh, and closes dialog
|
||||
|
||||
#### Scenario: User cancels delete all labels
|
||||
- **WHEN** confirmation dialog is open and user clicks Cancel button
|
||||
- **THEN** system closes dialog without making any API calls or removing any labels
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: File upload interface
|
||||
The application SHALL provide a CSV file upload interface accessible from the sidebar. The upload component SHALL trigger the POST /api/upload endpoint. On success, the component SHALL add the newly created chart to the chart selector and set it as the active chart, triggering the chart and annotation data to refresh for the new chart.
|
||||
|
||||
#### Scenario: Upload via UI
|
||||
- **WHEN** user selects a CSV file through the upload interface
|
||||
- **THEN** the file is sent to the upload API, a new chart is created, the chart selector updates to include the new chart, and the new chart becomes active with its candles displayed
|
||||
|
||||
#### Scenario: Upload error display
|
||||
- **WHEN** the upload API returns an error
|
||||
- **THEN** the UI displays the error message to the user and no chart is created
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Theme-aware Manage Annotation Types link
|
||||
The "Manage Annotation Types" link in the sidebar header SHALL use theme-aware styling consistent with the rest of the application. The link SHALL NOT use hardcoded color values. It SHALL use `text-muted-foreground` and `hover:text-foreground` Tailwind classes to respect both light and dark themes.
|
||||
|
||||
#### Scenario: Link styling in dark mode
|
||||
- **WHEN** the application is in dark mode
|
||||
- **THEN** the "Manage Annotation Types" link uses muted foreground color and brightens on hover, consistent with the dark theme
|
||||
|
||||
#### Scenario: Link styling in light mode
|
||||
- **WHEN** the application is in light mode
|
||||
- **THEN** the "Manage Annotation Types" link uses muted foreground color and darkens on hover, consistent with the light theme
|
||||
|
||||
#### Scenario: Link visual consistency
|
||||
- **WHEN** the sidebar renders
|
||||
- **THEN** the "Manage Annotation Types" link does not use underline styling and visually matches other sidebar text elements
|
||||
Loading…
Add table
Add a link
Reference in a new issue