Skip to content

Dashboard: silent-empty-state hardening (5 UX findings from EDH v2.2 regression review) #24

Description

@ducroq

Context

Today's EDH 3dfc7fb ("strategic envelope wrap v2.2") wrapped energy_price_forecast.json and wind_forecast.json under a {metadata, data: {...}} envelope. Two dashboard consumers had not been migrated, so the Prices-tab wholesale lines and the Forecast-tab wind chart silently rendered zero traces — fix shipped in 4a557c8 (defensive obj.data ?? obj shim).

User-interaction reviewer flagged that the fix is correct for today's regression but the underlying silent-failure pattern remains. Five findings, ranked by RPN, none individually blocker (no FAIL), but together they form a hardening backlog so the next schema bump doesn't repeat today's "stale dashboard, no signal" experience.

Findings

1. (RPN 288) No zero-trace guard in Prices chart → next schema bump silently shows partial data

updateChart in static/js/dashboard.js:251-274 calls renderChart with however many traces survive processEnergyDataForChart. If a future schema rename breaks the per-source loop (entsoe/epex/elspot), Plotly draws an EZ live line + Augur forecast band only — which looks partially inhabited and is more confusing than empty. Fix: after processEnergyDataForChart, count traces matching forecast sources; if zero and energyData is non-null, call showErrorNotification (already imported pattern in api-client.js:217).

2. (RPN 252) Solar/weather/grid processors lack the same defensive ?? shim

tab-charts.js getSolarLocations, processSolar, getWeatherLocations, processWeatherTemp, processWeatherCloud, processImbalance, processFlows, processLoad, processNedProduction, processNuclear, buildMarketCards, processGasChart all read .data directly. EDH says the other 14 feeds were already wrapped pre-v2.2 — so this is hypothetical-future hardening, not a current bug. May or may not be worth adding given the project's "don't design for hypothetical requirements" stance.

3. (RPN 210) DataLoader.loadFile silently returns null on fetch failure

static/js/modules/data-loader.js:27-36 does console.warn and returns null on non-200 or network error. No showErrorNotification call (unlike ApiClient.loadEnergyData at line 217). Tab-charts receive null silently → blank charts, no message. Fix: import showErrorNotification from error-handler.js, call it on the warn path. Alternatively consolidate at the onTabChange level.

4. (RPN 168) Empty <select> after a load failure has no placeholder

populateSelect in dashboard.js:381-405 does select.innerHTML = '' and iterates an empty array → completely empty dropdown widget. Combined with the blank chart div (since renderPlotlyChart is never called when locations are empty), it looks like a layout bug or perpetual loading state. Fix: append a disabled placeholder option when locations is empty; inject the "No data available." paragraph into the chart div mirroring renderPlotlyChart:455.

5. (RPN 120) lastUpdate shows wall-clock time when entsoe absent

dashboard.js:264 falls back to new Date().toISOString() if the optional chain to entsoe.metadata.start_time resolves undefined. For an energy price tool where freshness informs decisions (EV charging, heat pump timing), claiming "Last updated: now" when the data is actually stale is a trust issue. Pre-existing — the misleading fallback predates today's edit; today's commit only extended the optional-chain path to handle v2.2 envelope. Fix: replace || new Date().toISOString() with || null; render "Last updated: unknown" in chart-renderer when null.

Summary

# Title RPN Type
1 Zero-trace guard on Prices chart 288 New
2 Defensive shim on remaining processors 252 Hypothetical
3 DataLoader silent null → notification 210 Pre-existing
4 Empty select placeholder + chart fallback 168 Pre-existing
5 lastUpdate new Date() fallback lies 120 Pre-existing

Recommend tackling #1, #3, #4, #5 (real existing silent-failure modes). #2 is the only one that's purely defensive against a hypothetical future schema bump and the project's CLAUDE.md guidance pushes against that kind of work.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions