@page "/deployment/flow-debugger"
@using ZB.MOM.WW.ScadaBridge.Security
@using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Deployment
@using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared
@using ZB.MOM.WW.ScadaBridge.Commons.Types.Debugging
@* arch-review F3: opening the page is a Replay-capable read (Deployer + the OperationalAudit
roles) so the Audit Log / Execution Chain drill-ins work for an auditor. Every Live-run
affordance below is additionally gated on _canLive (the Deployer role). *@
@attribute [Authorize(Policy = AuthorizationPolicies.RequireFlowDebugger)]
Flow Debugger
@* Wider than the site select beside it: the field carries a whole instance
name, not a short site name. The script-entry branch still sums to 8 of 12
(site 2 + instance 3 + script 3) alongside the auto-width toolbar columns. *@
@* A site can hold hundreds of instances, so this is the shared area-tree
combobox, not a flat list (design 2026-09-08). _areas/_instances are
loaded once per site — the picker treats a new list as a site change. *@
}
}
else
{
}
@* Without Deployer there is only Replay, so the toggle would be a single dead button. *@
@if (_canLive)
{
}
@if (_mode == FlowMode.Live)
{
}
else
{
}
@if (!_canLive)
{
Replay only — rebuilding a past execution from the audit log. Starting a live run
requires the Deployer role.
@if (_entry == EntryKind.Method)
{
@* Method entry (#37): the same typed ParameterValueForm the script pane uses —
per-item list editors, nested object groups, inline validation — keyed on
the method so a change of method remounts it; plus a whole-payload raw-JSON
escape hatch to paste the exact body an external caller sends. Both edit
one value tree, and Run stays disabled while it is invalid. *@
Inputs
@if (_methodRawMode)
{
@if (_methodRawError is { } rawError)
{
@rawError
}
else
{
The whole request body, validated against the method's parameter definitions.
}
}
else
{
}
}
else if (SelectedScript is { } script)
{
Inputs
@* A write kind is fired by writing its own trigger attribute — the operator
supplies only the value. Every other kind is called directly with the
parameters its definitions declare. *@
@if (script.IsWriteKind)
{
@script.TriggerType trigger — writing the attribute fires the script.
}
else if (string.IsNullOrWhiteSpace(script.ParameterDefinitions))
{
This script takes no parameters.
}
else
{
@* Keyed on the script so a change of script REMOUNTS the form: its raw
text and parse errors are per-instance state that must not survive
(and two scripts may even declare byte-identical definitions, which
would otherwise skip the reparse and its ValidChanged entirely). *@
@if (_graph == null && _predicted != null)
{
Predicted from script syntax — dashed nodes depend on inputs or live site state.
}
else if (SiteNotTraced)
{
Site not traced — the site returned no trace rows for the routed call (older site build or an untraced call).
}
@if (_listView)
{
@* Direction B: the same steps as a vertical list; a prediction-only graph has no real steps, so it shows in the swimlane only. *@
}
else
{
}