fix: show TA-lib pattern spans on chart and add delete all annotations button

- Fix SpanAnnotationManager price range calculation: use direct candle
  filtering by time range instead of dummy Candle objects that fell back
  to 0/0 high/low, causing invisible rectangles
- Add handleDeleteAllAnnotations in page.tsx (deletes all span annotations
  and regular annotations for current chart)
- Add 'Delete All Annotations' button in sidebar below TA-Lib panel
This commit is contained in:
Marko Djordjevic 2026-02-17 20:12:23 +01:00
parent 7901a1a257
commit d416aa409c
3 changed files with 50 additions and 5 deletions

View file

@ -405,6 +405,21 @@ export default function Home() {
}
};
const handleDeleteAllAnnotations = async () => {
if (!activeChartId) return;
await Promise.all([
fetch(`/api/span-annotations?chartId=${activeChartId}`, { method: 'DELETE' }),
fetch(`/api/annotations?all=true&chartId=${activeChartId}`, { method: 'DELETE' }),
]);
await Promise.all([
fetchSpanAnnotations(activeChartId),
fetchAnnotations(activeChartId),
chartRef.current?.refreshData(),
]);
setSelectedSpanId(null);
setSelectedLabelId(null);
};
const handleLabelDelete = async (id: number) => {
setAnnotations(annotations.filter((a) => a.id !== id));
if (selectedLabelId === id) {
@ -778,6 +793,17 @@ export default function Home() {
/>
</div>
{/* Delete all annotations */}
<div className="px-3 py-2 border-t border-sidebar-border">
<button
onClick={handleDeleteAllAnnotations}
disabled={!activeChartId}
className="w-full px-2 py-1 text-[10px] text-destructive border border-destructive/30 rounded hover:bg-destructive/10 transition-colors disabled:opacity-50"
>
Delete All Annotations
</button>
</div>
{/* Training Panel */}
<div className="px-3 py-2 border-t border-sidebar-border">
<TrainingPanel />

View file

@ -112,12 +112,31 @@ export default function SpanAnnotationManager({
// Create primitives for each span annotation
spanAnnotations.forEach((span) => {
const { max_high, min_low } = calculatePriceRange(
{ time: span.start_time, open: 0, high: 0, low: 0, close: 0 },
{ time: span.end_time, open: 0, high: 0, low: 0, close: 0 }
// Find candles within span time range for price calculation
const spanCandles = candles.filter(
(c) => c.time >= span.start_time && c.time <= span.end_time
);
let max_high: number;
let min_low: number;
if (spanCandles.length > 0) {
max_high = Math.max(...spanCandles.map((c) => c.high));
min_low = Math.min(...spanCandles.map((c) => c.low));
} else {
// Fallback: find nearest candles to span boundaries
const nearest = candles.reduce(
(acc, c) => {
const distStart = Math.abs(c.time - span.start_time);
const distEnd = Math.abs(c.time - span.end_time);
if (distStart < acc.startDist) acc = { ...acc, startCandle: c, startDist: distStart };
if (distEnd < acc.endDist) acc = { ...acc, endCandle: c, endDist: distEnd };
return acc;
},
{ startCandle: candles[0], startDist: Infinity, endCandle: candles[0], endDist: Infinity }
);
max_high = Math.max(nearest.startCandle?.high ?? 0, nearest.endCandle?.high ?? 0);
min_low = Math.min(nearest.startCandle?.low ?? 0, nearest.endCandle?.low ?? 0);
}
// If we can't calculate price range from candles, use sensible defaults
const spanData: SpanData = {
id: span.id,
start_time: span.start_time,

File diff suppressed because one or more lines are too long