From 15d9ecf3193ef320d44125faf13340c2b6ac1279 Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Thu, 17 Sep 2026 00:53:12 -0400 Subject: [PATCH] Fetch col_meta in the member-list effect instead of reading a ref The effect decided which dim_groups to load from colMetaRef.current, but that ref is filled inside initViewer, which is async. The effect ran first, saw an empty array, found no groups, and fetched nothing -- and a ref changing does not re-run an effect, so it never recovered. dimMembers stayed {} for the whole session. Everything therefore fell back to the source lookup, which is precisely the query pf.dim_member exists to replace: XCP06500G18B112 has two attribute combinations across history, so DISTINCT ... LIMIT 2 returned two rows and the route answered null. The member row had the answer all along, picked as the most recent by order date. Co-Authored-By: Claude Opus 5 (1M context) --- ui/src/views/Forecast.jsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ui/src/views/Forecast.jsx b/ui/src/views/Forecast.jsx index d8f3745..e1718d5 100644 --- a/ui/src/views/Forecast.jsx +++ b/ui/src/views/Forecast.jsx @@ -669,7 +669,16 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio if (!sourceId) { setDimMembers({}); return } let cancelled = false ;(async () => { - const meta = colMetaRef.current + // col_meta is fetched here rather than read from colMetaRef: that ref is + // filled inside initViewer, which is async, so this effect ran first, saw an + // empty array, found no groups and never fetched a list -- and a ref changing + // does not re-run an effect, so it never recovered. It is a small query and + // the browser will serve it from cache anyway. + let meta = [] + try { + meta = await fetch(`/api/sources/${sourceId}/cols`).then(r => r.ok ? r.json() : []) + } catch { return } + if (cancelled) return const groups = [...new Set(meta.filter(c => c.dim_group && c.is_key).map(c => c.dim_group))] const loaded = {} for (const g of groups) {