Feature/favorites view#10
Merged
cayossarian merged 97 commits intomainfrom Apr 17, 2026
Merged
Conversation
Adds a synthetic "Favorites" entry to the dashboard panel dropdown that aggregates favorited circuits and sub-devices (BESS, EVSE) from every configured SPAN panel into a single workspace. - Heart toggles in the Graph Settings side panel and per-circuit / per-sub-device side panels (dashboard mode only — never in standalone card). Per-row hearts in panel-mode for both circuits and sub-devices. - Favorites view shows By Activity / By Area / Monitoring tabs (no By Panel) with circuit names prefixed by panel name when more than one panel contributes. Sub-devices render as tiles above the circuit list. Monitoring stacks per-panel blocks. - Persistent panel-stats header lifted out of the By-Panel grid so it stays visible across all tabs (real panels). Favorites pseudo-panel shows a summary strip + W/A unit toggle instead. - Stateful Favorites view: active tab, expanded composite ids, and search query persist via localStorage and restore exactly on return. - Side-panel domain service calls thread an optional config_entry_id so cross-panel favorites edits target the originating panel. DashboardController bypasses single-entry caches in favorites view and fetches per-entry settings on demand. - Heart UI uses the entity_id-based service API — UUIDs and sub-device device_ids are an internal storage concern. - Skip rendering ha-menu-button until hass is set; it reads this.hass.kioskMode in willUpdate. Bumps version to 0.9.3.
The sub-device-mode side panel's horizon segment buttons (BESS, EVSE) called set_subdevice_graph_horizon / clear_subdevice_graph_horizon without config_entry_id, so the backend's _get_horizon_manager fell back to the FIRST loaded SPAN entry's manager. On multi-panel installs the override was written to the wrong panel's manager and the chart stayed at the global default. Panel-mode (Graph Settings list) and the circuit-mode side panel already threaded it; the sub-device side panel now does too, sourced from cfg.configEntryId set by DashboardController. Also documents the 0.9.3 release in CHANGELOG.
In the Favorites view, un-favoriting a circuit or sub-device used to leave the row/tile in the rendered list until the next panel or tab switch. _refreshFavorites was deliberately skipping _scheduleTabRender to keep the open Graph Settings side panel alive on real panels. Branch the behavior by context: re-render when in the Favorites view (the user just removed what they were inspecting, so tearing down the side panel as a side effect is acceptable), keep the no-re-render behavior on real panels so the Graph Settings side panel stays open while toggling multiple hearts in a row.
…usion The per-circuit and per-sub-device side panels rendered the Favorite section with an ha-switch that sat directly under the breaker relay switch. Visually similar; accidentally hitting the wrong one could trip a breaker. Replaced both with the same filled/outlined heart icon used in the Graph Settings list. The two render paths now share a single _appendFavoriteHeartSection helper.
Addresses findings from the deep code review of the favorites branch.
Critical fixes:
- Per-entry horizon resolution in Favorites view. ``onGraphSettingsChanged``
and ``fetchAndBuildHorizonMaps`` now branch on ``_favRefs``; in favorites
mode they fetch graph settings per contributing config entry in
parallel and route each composite circuit/sub-device id through its
``FavoriteRef`` to the originating entry's settings. Without this, a
per-target horizon override on a non-primary panel was masked by the
primary entry's settings cache.
- Refresh-token guard in ``_refreshFavorites``: a monotonic ``_refreshSeq``
causes superseded callbacks to bail out, preventing rapid heart toggles
from interleaving renders or scheduling duplicate tab rebuilds.
- ``_renderFavoritesMonitoring`` builds tabs into a local map and only
commits to the instance field after the loop, with per-panel try/catch
so a single failing entry can't orphan tabs from the cleanup loop.
UX / a11y / cleanup:
- Heart buttons are now ``role=switch`` with ``aria-pressed`` and
``aria-label``; ``_toggleFavoriteEntity`` syncs ``aria-pressed``
alongside the optimistic class flip. Single ``_buildHeartButton``
helper used by both circuit/sub-device panel rows and the per-target
side-panel Favorite section.
- Favorites summary uses explicit ``t("panel.favorites_summary_one")``/
``t("panel.favorites_summary_many")`` so the i18n validator no longer
reports them unused; messages dropped the ungrammatical
"across N panels" suffix and now read "1 favorite" / "{circuits} favorites".
- ``_renderFavoritesMonitoring`` heading uses ``h2`` to fit the
document outline.
- Comment on ``PanelModeConfig.favoriteCircuitUuids`` /
``favoriteSubDeviceIds`` documents that they are open-time snapshots,
not live — gear re-clicks rebuild from ``_panelFavorites``.
- ``_buildFavoriteHeart`` warns to console when a circuit has no
current/power sensor (heart silently suppressed otherwise).
Dead code removed:
- ``parseCompositeId`` (exported but unused).
- ``FavoritesBuildResult.panelTopologies`` (built but not consumed).
updateCollapsedRows refreshed each row's power/current in place but never re-evaluated sort order, so 'By Activity' (and within-area sort) froze to the initial render order. Partition .list-view into groups bounded by .area-header and reorder rows (keeping expanded-content siblings glued to their parents) after updating values.
Widen the favorites-mode sidebar's per-panel section from 'favorited circuits only' to 'every circuit in that panel's topology'. Heart state derives from section.favoriteCircuitUuids, so users can toggle existing favorites off and add new ones for any circuit on a contributing panel without leaving the Favorites view -- consistent with the real-panel gear sidebar's full circuit listing and the Favorites Monitoring tab's full per-panel data. Extracts the row-selection step as sortedCircuitsForSection() in favorites-sections.ts so the alphabetical-sort + every-circuit contract is unit-testable without a DOM (5 new tests; 209 total).
Cache races: - favorites-store, monitoring-status: add generation counter so invalidate() supersedes in-flight fetches instead of letting a stale pre-invalidate result commit to cache for up to 30s. - monitoring-status: coalesce concurrent fetches onto a single promise instead of returning the possibly-null prior state to racing callers. Lifecycle leaks: - span-panel-card, span-panel: guard subscribeAreaUpdates with an _areaSubscribing flag so disconnect-before-resolve unsubscribes immediately instead of storing the handle on a detached element. - side-panel: add disconnectedCallback; clear _debounceTimers on close() so pending threshold/horizon writes cannot fire against a torn-down config. Null-safety: - tab-monitoring: replace 8 querySelector<T>(...)!.value assertions with a null-checked readGlobalFields() helper; surface failures via the status element instead of crashing the save handler. - tab-monitoring, tab-settings: coerce unvalidated WS responses via dedicated narrowing functions instead of unsafe `as` casts. - card-discovery: harden identifier-pair destructuring against malformed shapes. - retry-manager: replace `throw lastError!` with nullish fallback. - span-panel, error-store: narrow err with instanceof Error before reading .message; isolate subscriber exceptions in _notify(). Escaping: - grid-renderer, header-renderer, list-renderer, sub-device-renderer: escape every i18n-sourced string interpolated into title=, data-, and inline style attributes so future translations containing `"` cannot break markup. Refactor: - span-panel-card, span-panel: centralise shadow-root access via a private _root getter; removes 24 `this.shadowRoot!` assertions. State pruning: - span-panel: only prune favorites view-state expansion ids when a populated topology is available, so a rapid search keystroke during a tab re-render doesn't drop every expanded row.
- favorites-cache: assert invalidate() supersedes in-flight fetches and that concurrent callers coalesce. - monitoring-cache: same cache-race coverage plus a null-not-cached regression so a transient fetch error cannot mask subsequent success. - grid-renderer: renderCircuitSlot escapes user-controllable names, uuids, and i18n shedding labels inside title= attributes.
Two fixes to the Favorites view's BESS sub-device charts: 1. Line visibility: loadHistory() now runs before renderActivityView / renderAreaView, so the single updateDOM pass happens with real history. Previously the view rendered with an empty history first and updated after, which left residual ECharts state that hid the line stroke on SOE/POWER charts in Favorites. 2. Resize: wire setupResizeObserver into the Favorites render path so BESS and EVSE charts shrink on viewport changes without a refresh. Also flatten the chart area gradient to a uniform alpha, so the per- chart fill no longer varies with the filled polygon height.
- RetryManager offline short-circuit: add ErrorStore.hasAnyPanelOffline() so retries skip correctly under Favorites multi-panel watch (per-entity panel-offline keys), not just the legacy single-unnamed key. - FavoritesCache / MonitoringStatusCache: track inflight generation so invalidate() supersedes pending requests; fetch() after invalidate() issues a fresh call instead of awaiting a stale in-flight promise. - header-renderer buildSheddingLegendHTML: escape label / icon / color / textLabel via escapeHtml to match the escaping used elsewhere in the renderer and guard against stray markup in i18n strings. - CHANGELOG: correct 'Persisted per device' wording — list-columns storage is a single localStorage key, global to the browser.
The real panel Monitoring tab is rendered as a pure configuration view with no panel-stats header (gear icon, slide-to-enable, shedding legend, W/A toggle). The Favorites pseudo-panel's Monitoring view was inserting that same summary strip on top of its per-panel MonitoringTab stack, which doesn't match real panels and clutters a screen that is entirely threshold / notification config. Remove the insertAdjacentHTML call so the Favorites Monitoring view renders just the per-panel headings and MonitoringTab bodies, matching the real-panel behavior in `_renderTab` case "monitoring".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Grid view changes for multiple columns, code review changes