Repository navigation
Add filtering and unsimulated action pins to overview diagram - #105
Merged
Merged
Conversation
…ed pins Category toggles (solves / low margin / still overloaded / divergent- islanded) plus All / None bulk toggles, a Max-loading slider (default 150%), and a "Show unsimulated" switch now live in the Remedial Actions overview header. The filters drive both the pin layer on the overview diagram and the Selected / Suggested / Rejected card lists in the sidebar ActionFeed via a shared ActionOverviewFilters state owned by App.tsx, so the two views stay in lock-step. When "Show unsimulated" is on, every scored-but-not-yet-simulated action (from result.action_scores) renders as a dimmed, dashed pin on the N-1 map. Double-clicking a dimmed pin streams a manual simulation via /api/simulate-and-variant-diagram — the same code path the Manual Selection dropdown uses — and promotes the action into the normal Simulated Actions list. Three replay-log event types were added (overview_filter_changed, overview_unsimulated_toggled, overview_unsimulated_pin_simulated) and registered in both the TS and Python spec-conformance SPEC tables.
Two follow-ups on the action-overview filters feature: 1. Popover placement in detached windows: the above/below + clamp heuristics used to fall back to `window.innerWidth/innerHeight`, which in a detached popup still returned the MAIN window's dimensions — so a pin clicked near the popup's bottom edge produced a popover that hid the pin. We now read `containerRef.current?.ownerDocument?.defaultView` and capture the popup's own viewport at click time so `decidePopoverPlacement` / `computePopoverStyle` reflect where the pin actually sits on screen. 2. Combined-action pin protection: filtering out an underlying unitary action would previously leave a combined-pin curve dangling without its endpoint. We now keep any unitary pin referenced by a passing combined pin visible with a new `dimmedByFilter` flag (rendered at opacity 0.4 with the washed- out severity palette), so the operator still sees the combined pin's two constituent actions as context. When both the combined pin AND the unitary fail the filter, the unitary is dropped as before. Adds 3 popoverPlacement viewport tests + 2 detached-window popover placement tests + 3 combined-action protection tests. Full suite: 973/973.
…-double-click After double-clicking an unsimulated pin, `handleSimulateUnsimulatedAction` in App.tsx already mirrors the Manual Selection flow: it streams the simulation, primes the action-variant diagram, and calls `wrappedManualActionAdded` which inserts the new entry into `result.actions` and adds the id to `selectedActionIds` — same state machine as the search-dropdown Add path. Three new tests assert the user-observable outcome: - the dimmed dashed "unsimulated" pin disappears, - a fully-coloured pin (no `dimmedByFilter`, no `data-unsimulated`, no opacity override) replaces it at the same anchor, - and, when the parent also starred the new entry (as Manual Selection does), the pin picks up the selected styling (gold star). Also hardens the stream reader in App.tsx to flush any trailing bytes after the final read — belt-and-braces in case a future backend change omits the final \n.
…adata Hovering a dimmed "un-simulated" pin now shows the same triage data the Manual Selection dropdown's score table exposes: action type, score, rank inside the type bucket (e.g. "rank 3 of 12"), max score in the bucket, plus MW start for load-shedding / renewable-curtailment or tap start (with the allowed range) for PST actions. App.tsx computes `unsimulatedActionInfo` alongside `unsimulatedActionIds` from `result.action_scores`, ranking each bucket by descending score so rank 1 is the top candidate. The resulting `Record<id, UnsimulatedActionScoreInfo>` is threaded through VisualizationPanel to ActionOverviewDiagram and handed to `buildUnsimulatedActionPins`, which formats the multi-line SVG `<title>`. When score info is absent (older session reloads, tests that stub only the id list), the original generic tooltip still applies. Tests: 6 new svgUtils unit tests covering each tooltip branch (generic fallback, scored, MW start, tap start with range, missing lookup), plus 1 component-level test verifying the rendered `<title>` carries the enriched text. Full suite: 982/982.
…elper New single-select chip row (ALL / DISCO / RECO / LS / RC / OPEN / CLOSE / PST) above the overview diagram — same affordance as the Explore Pairs filter, now extracted into a shared component used by both. Choosing a chip hides every pin (unitary, combined, un-simulated) AND every card in the sidebar ActionFeed that doesn't classify into the matching bucket. Filter semantics mirror the existing category + threshold filters: a combined-action pin stays visible when EITHER of its constituents matches the chip, so filtering by DISCO doesn't hide a disco-plus- reco combined action. Wiring: - new `utils/actionTypes.ts` — `classifyActionType` + `matchesActionTypeFilter` + `ACTION_TYPE_FILTER_TOKENS`. Classifier now correctly distinguishes "Ouverture du poste" (open coupling) from "Ouverture de la ligne" (disco), fixing a desc-based mis-classification ActionFeed had too. - new `components/ActionTypeFilterChips.tsx` — presentational chip row, test-id-friendly, reused by ExplorePairsTab and ActionOverviewDiagram. - `types.ts` — `actionType: ActionTypeFilterToken` added to `ActionOverviewFilters`. - ActionOverviewDiagram reads / writes the filter through `onFiltersChange`, logs an `overview_filter_changed` event with `kind: 'action_type'`, and filters its three pin layers. - ActionFeed honours the shared filter for Selected / Suggested / Rejected card lists (combined-action cards use the same either-constituent-matches rule as the overview pins). - App.tsx seeds the initial filter with `actionType: 'all'`. - ExplorePairsTab now renders the same chip component. - Spec tables (TS + Python) accept `action_type` inside `overview_filter_changed.details`. Tests: new classifier unit tests (incl. the coupling-vs-disco regression), chip component tests, and 5 overview-level tests covering chip render + filter-by-type + un-simulated pin filtering by score-info type. Full suite: 1009/1009.
…ng classifier Two small follow-ups on the filter work: 1. Header layout: the "📍 Remedial actions overview — N pins on the N-1 network" title and the dedicated action-type chip sub-banner were each taking ~30 px of vertical space on the visualisation panel. Merged into a single row: a compact 📍<count> counter (with the full phrasing as a tooltip), then the severity chips, All/None, threshold slider, Show-unsimulated toggle, a vertical separator, and the DISCO/RECO/LS/RC/OPEN/CLOSE/PST chip row — all inline. Claims back one row of diagram area. 2. Classifier: `node_merging_*` actions (which the recommender emits as close-coupling operations) were classified 'unknown' and disappeared when the operator picked the CLOSE chip — "node merging" contains neither "close_coupling" nor "fermeture", so none of the existing heuristics matched. The classifier now maps `node_merging` → close coupling and `node_splitting` → open coupling, from either the id or the score-table type. Tests: updated the pin-counter assertion to read the new counter test-id / tooltip, and added four classifier cases covering node_merging / node_splitting via id and type. Full suite: 1012/1012.
…N coupling
The classifier used `(desc.includes('poste') && desc.includes('ouverture'))`
as a coupling signal. That pattern matches BOTH a coupling open
("Ouverture du poste 'X'") and a line breaker open
("Ouverture LINE DJ_OC dans le poste POSTE") — so line-disconnection
actions with a substation-qualified description were landing in the
OPEN bucket and, symmetrically, disappearing under the DISCO chip.
Two-step fix:
1. `utils/actionTypes.ts` now computes a separate `isCouplingSignal`
based on id tokens (`coupling`, `busbar`, `noeud`, `node_merging`,
`node_splitting`), score-table types, description markers (`coupl`
/ `busbar`) and the specific `"du poste 'X'"` phrasing where the
poste itself is the target. OPEN/CLOSE are gated by this signal;
DISCO/RECO only apply when the signal is absent.
2. ActionFeed and ExplorePairsTab had their own inline copies of the
bucket heuristics with the same bug. They now both delegate to
`classifyActionType` / `matchesActionTypeFilter`, so the three
chip filters (manual selection dropdown, score table in that
dropdown, Explore Pairs, action-overview, sidebar ActionFeed)
share a single source of truth.
Tests: 6 new classifier cases covering line-vs-coupling with the
"dans le poste" and "du poste 'X'" phrasings, plus a line-reco
mirror and a close-coupling reco case. Full suite: 1018/1018.
Reconciles the action-overview filter / un-simulated-pin feature branch with main's parallel svgUtils.ts → `./svg/*` decomposition. The only conflict was `frontend/src/utils/svgUtils.ts` (both sides rewrote it). Resolution: accept main's barrel file and port the branch's additions into the decomposed modules: - `svg/actionPinData.ts`: add optional `unsimulated` and `dimmedByFilter` fields on `ActionPinInfo`; export `actionPassesOverviewFilter`; extend `buildActionOverviewPins` with an optional `overviewFilter` param; add `buildUnsimulatedActionPins` + its internal tooltip helper. - `svg/actionPinRender.ts`: extend `ApplyPinsOptions` with `unsimulatedPins` + `onUnsimulatedPinDoubleClick`; teach `resolvePinFill` / `renderUnitaryPin` about `dimmedByFilter` (washed-out fill, 0.4 opacity, `data-dimmed-by-filter` attr); add `renderUnsimulatedPin` for dashed grey previews; wire them into `applyActionOverviewPins`. - `svgUtils.ts` barrel: re-export `actionPassesOverviewFilter` and `buildUnsimulatedActionPins`. All 1079 frontend tests pass; typecheck + lint clean. https://claude.ai/code/session_0176eivSfH1GiV9CPc1iGqMo
Extends the action-overview doc with the features landed in PR #105: - Overview paragraph + ASCII diagram updated to show the consolidated single-row header (counter + category chips + All/None + threshold slider + Show-unsimulated + action-type chips) and the un-simulated pin glyph. - New `Un-simulated action pin` sub-section under Pin anatomy: SVG structure, anchor fallback via minimal stub, grey palette, dashed stroke, single/double-click semantics, tooltip enrichment contract. - New `Filter-dimmed constituent pin` sub-section: `dimmedByFilter` flag, `data-dimmed-by-filter` attribute, 0.4 opacity rationale. - New top-level `Filtering` section: ActionOverviewFilters shape and defaults, severity categories, threshold slider semantics (including the null-max_rho bypass), action-type chip classifier rules (coupling signal, commits f356c2e + d479516 regressions), three-pass protected-constituent algorithm, shared predicate contract with ActionFeed, interaction-logging events. - Auto-fit dependency list updated to include un-simulated pins. - New interaction entry: double-click on un-simulated pin → manual simulation. - Files table refreshed: svgUtils.ts is now a barrel, new rows for actionPinData.ts, actionPinRender.ts, ActionTypeFilterChips.tsx, actionTypes.ts. - Test-coverage table refreshed with the new filter / un-simulated / action-type / protected-constituent / re-colour regression cases. - Performance notes: filter memo granularity + single-append ordering for un-simulated render. https://claude.ai/code/session_0176eivSfH1GiV9CPc1iGqMo
Refactor: Extract stateless helpers from mixins into focused modules
The committed `dist-standalone/standalone_v0.6.html` snapshot was taken on Apr 20 (v0.6 release prep) and predates the action-overview features that emit `overview_filter_changed`, `overview_unsimulated_pin_simulated`, and `overview_unsimulated_toggled`. The parity script defaulted to the (gitignored) unversioned `standalone.html`, falling back to the legacy mirror when absent, so CI reported the 3 events as missing against a stale artifact. - Regenerate `standalone_v0.6.html` from the current React source via `npm run build:standalone` so the tracked bundle picks up the new events and matches the 54-event frontend inventory. - Teach `check_standalone_parity.py` to pick the latest `dist-standalone/standalone_v*.html` when no unversioned build is present, so CI runs against the committed snapshot instead of the legacy file. - Apply the `_DEFERRED_TYPES` exemption symmetrically: the auto-gen bundle now uses the same `buildConfigInteractionDetails()` pattern as the React source, so `config_loaded` / `settings_applied` must be deferred on both sides (was FE-only, silently masked while the legacy mirror was the de-facto target).
Refactor action overview pins and add filtering/unsimulated actions
marota
pushed a commit
that referenced
this pull request
Apr 22, 2026
… dynamic fix Captures the post-0.6.0 work: svgPatch DOM-recycling (PR #108), Action Overview filters + unsimulated pins (PR #105, #107), code-quality gate + 5 decomposition passes (PR #104, #106), docs reorganisation (PR #103), App.tsx hook extraction (PR #109), and the dynamic reco_ reconnection-action fix on the current branch. https://claude.ai/code/session_01Tzp2fdUas3Y9vNZy6dxxuC
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.
Summary
This PR adds comprehensive filtering capabilities to the Remedial Action overview diagram and introduces support for displaying unsimulated (scored-but-not-yet-simulated) actions as dimmed, dashed pins. The filtering state is now shared between the overview pins and the sidebar action feed to keep both views in sync.
Key Changes
Filtering Infrastructure
ActionOverviewFilterstype containing category toggles (green/orange/red/grey), threshold cap, unsimulated visibility flag, and action-type chip selectionactionPassesOverviewFilter()utility so both the overview pins and action feed cards apply identical filtering logicactionTypes.tsmodule withclassifyActionType()andmatchesActionTypeFilter()to unify action categorization across three UI surfaces (overview, feed, explore pairs)Overview Diagram Enhancements
max_rhoexceeds the thresholdbuildUnsimulatedActionPins()function renders scored-but-not-simulated actions as dimmed, dashed pins with hover tooltips containing score metadataonSimulateUnsimulatedActioncallbackAction Feed Integration
classifyActionType()so the feed and overview stay in lock-stepSupporting Changes
ActionTypeFilterChips— reusable single-select chip row for action-type filtering (used by overview, feed, and explore pairs)overviewFiltersstate and computedunsimulatedActionIds/unsimulatedActionInfofromaction_scoresImplementation Details
nullmax_rho (divergent/islanded) bypass the threshold check so non-numeric outcomes remain visible when grey category is enabledhttps://claude.ai/code/session_01TJCZcgr9ZJ5WfqnpbEovNJ