// Copyright (c) 2025-2026, RTE (https://www.rte-france.com) // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, // you can obtain one at http://mozilla.org/MPL/2.0/. // SPDX-License-Identifier: MPL-2.0 // Game-Mode beginner-assistance wiring, extracted from App.tsx to keep the // hub under its size ceiling. Registers a handler on `gameBridge` that drives // the workspace from a lever hint (the "most-used levers" panel): // - single-click ('inspect'): fill the Inspect field, center the NAD on the // element (resolving an injection / coupling switch to its home VL), and // open that substation's SLD so the beginner can inspect it; // - double-click ('simulate'): run the mapped action directly — a catalogue // branch disco/reco, or a coupling maneuver at the resolved VL. Magnitude- // free injection / PST levers carry no action, so they degrade to inspect // with a hint to set the amount in the SLD. // // App keeps ownership of the collaborators (diagrams + the two simulate // entry points + the SLD opener) and passes them in, so this stays a thin // orchestrator and the bare (non-game) app never touches the bridge. import { useCallback, useEffect } from 'react'; import { api } from '../api'; import { gameBridge, type LeverInteractionMode } from '../game/gameBridge'; import { notifyError, notifyInfo } from '../utils/notifications'; import type { LeverInteraction } from '../types'; import type { DiagramsState } from './useDiagrams'; export interface LeverInteractionParams { diagrams: DiagramsState; /** Simulate a catalogue action (branch disco/reco) by id. */ handleSimulateUnsimulatedAction: (actionId: string) => Promise; /** Simulate a coupling maneuver (switch toggle) at a resolved VL. */ handleSimulateLever: (spec: { voltageLevelId: string; switches: Record }) => Promise; /** Open the SLD overlay for a voltage level. */ handleVlOpen: (vlName: string) => void; } export function useLeverInteraction(params: LeverInteractionParams): void { const { diagrams, handleSimulateUnsimulatedAction, handleSimulateLever, handleVlOpen } = params; const handleLeverInteraction = useCallback( async (interaction: LeverInteraction, mode: LeverInteractionMode) => { const { inspectQuery, category, simulate } = interaction; // The overflow tab isn't a NAD — center on the contingency tab instead. const tab = diagrams.activeTab === 'overflow' ? 'contingency' : diagrams.activeTab; // Fill the Inspect field with the clicked element (both modes). We // center explicitly below, so use the plain setter that doesn't pin // a focus tab (an injection / switch name never matches a NAD id, // which would otherwise leave that pin dangling). diagrams.setInspectQuery(inspectQuery); // A catalogue action (branch disco/reco) simulates directly by id. if (mode === 'simulate' && simulate?.actionId) { await handleSimulateUnsimulatedAction(simulate.actionId); return; } // Resolve the element's home VL for everything that isn't a branch // (a branch spans two VLs and is centered directly by its own id). let homeVl: string | null = null; if (category !== 'branch') { try { const { voltage_level_ids } = await api.getElementVoltageLevels(inspectQuery); if (voltage_level_ids.length === 1) homeVl = voltage_level_ids[0]; } catch { // Resolution is best-effort — fall through to centering on the id. } } // A coupling maneuver simulates directly once we know its VL. if (mode === 'simulate' && simulate?.switches) { if (homeVl) await handleSimulateLever({ voltageLevelId: homeVl, switches: simulate.switches }); else notifyError('Could not locate the substation for this maneuver.'); return; } // Magnitude-free lever double-clicked: it can't be simulated as-is, // so guide the operator to set the amount in the SLD, then fall // through to inspect (locate + open the substation). if (mode === 'simulate' && !simulate) { notifyInfo('Set the amount in the substation diagram, then Simulate.'); } // Inspect: center the NAD, and open the SLD when the lever lives in // one substation (an injection or a coupling switch → a single VL). if (homeVl) { diagrams.zoomToElement(homeVl, tab); handleVlOpen(homeVl); } else { diagrams.zoomToElement(inspectQuery, tab); } }, [diagrams, handleSimulateUnsimulatedAction, handleSimulateLever, handleVlOpen], ); useEffect(() => { if (!gameBridge.isGameMode()) return; gameBridge.registerLeverHandler(handleLeverInteraction); }, [handleLeverInteraction]); }