fix: evaluated field on event list + abort race condition
- Backend: add evaluated subquery column to list SELECT so each event returns evaluated=1/0 (was missing, causing all events to appear as unevaluated regardless of filter) - Frontend: AbortController cancels the previous in-flight fetch when a new load fires, preventing stale results from overwriting current filter state Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -171,7 +171,13 @@ def list_events(
|
|||||||
# When joining with instrument_impacts an event can appear multiple times — use GROUP BY
|
# When joining with instrument_impacts an event can appear multiple times — use GROUP BY
|
||||||
group_sql = "GROUP BY me.id" if instrument else ""
|
group_sql = "GROUP BY me.id" if instrument else ""
|
||||||
|
|
||||||
# Extra column: best instrument score for display
|
# Extra columns: evaluated flag + optional instrument score
|
||||||
|
evaluated_col = (
|
||||||
|
", (CASE WHEN EXISTS ("
|
||||||
|
"SELECT 1 FROM instrument_impacts _ei "
|
||||||
|
"WHERE _ei.source_type='event' AND _ei.source_id=me.id"
|
||||||
|
") THEN 1 ELSE 0 END) as evaluated"
|
||||||
|
)
|
||||||
inst_col = (
|
inst_col = (
|
||||||
", MAX(COALESCE(ii.adjusted_score, ii.impact_score)) as inst_score"
|
", MAX(COALESCE(ii.adjusted_score, ii.impact_score)) as inst_score"
|
||||||
", ii.direction as inst_direction"
|
", ii.direction as inst_direction"
|
||||||
@@ -181,7 +187,7 @@ def list_events(
|
|||||||
SELECT COUNT(DISTINCT me.id) FROM market_events me {join_clause} {where_sql}
|
SELECT COUNT(DISTINCT me.id) FROM market_events me {join_clause} {where_sql}
|
||||||
"""
|
"""
|
||||||
data_sql = f"""
|
data_sql = f"""
|
||||||
SELECT me.* {inst_col}
|
SELECT me.* {evaluated_col} {inst_col}
|
||||||
FROM market_events me {join_clause}
|
FROM market_events me {join_clause}
|
||||||
{where_sql} {group_sql} {order_sql}
|
{where_sql} {group_sql} {order_sql}
|
||||||
LIMIT ? OFFSET ?
|
LIMIT ? OFFSET ?
|
||||||
|
|||||||
@@ -1173,9 +1173,15 @@ export default function MarketEvents() {
|
|||||||
setDateTo(to)
|
setDateTo(to)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||||
|
const abortRef = useRef<AbortController | null>(null)
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
|
// Cancel any in-flight fetch
|
||||||
|
abortRef.current?.abort()
|
||||||
|
abortRef.current = new AbortController()
|
||||||
|
const signal = abortRef.current.signal
|
||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
if (search) params.set('search', search)
|
if (search) params.set('search', search)
|
||||||
@@ -1193,11 +1199,15 @@ export default function MarketEvents() {
|
|||||||
params.set('sort_by', instTicker ? 'instrument_score' : sortBy)
|
params.set('sort_by', instTicker ? 'instrument_score' : sortBy)
|
||||||
params.set('sort_dir', sortDir)
|
params.set('sort_dir', sortDir)
|
||||||
params.set('limit', '500')
|
params.set('limit', '500')
|
||||||
const r = await fetch(`/api/market-events?${params}`)
|
try {
|
||||||
if (r.ok) {
|
const r = await fetch(`/api/market-events?${params}`, { signal })
|
||||||
const d = await r.json()
|
if (r.ok) {
|
||||||
setEvents(d.events)
|
const d = await r.json()
|
||||||
setTotal(d.total)
|
setEvents(d.events)
|
||||||
|
setTotal(d.total)
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
if (e.name === 'AbortError') return // superseded by a newer load
|
||||||
}
|
}
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}, [search, category, level, minScore, dateFrom, dateTo, evaluated,
|
}, [search, category, level, minScore, dateFrom, dateTo, evaluated,
|
||||||
|
|||||||
Reference in New Issue
Block a user