Commit Graph

46 Commits

Author SHA1 Message Date
Joseph Doherty a58151e99e feat(adminui): editable DB-backed LDAP role map (global, FleetAdmin-gated) 2026-05-29 09:55:07 -04:00
Joseph Doherty 7570df76d3 feat(adminui): editable OpcUaClient endpoint URL list via CollectionEditor 2026-05-29 09:41:09 -04:00
Joseph Doherty 244949caa3 feat(adminui): editable S7 tag list via CollectionEditor 2026-05-29 09:37:12 -04:00
Joseph Doherty a5a0d06dbe feat(adminui): editable FOCAS device + tag lists via CollectionEditor 2026-05-29 09:33:53 -04:00
Joseph Doherty 6882761f4c feat(adminui): editable TwinCAT device + tag lists via CollectionEditor 2026-05-29 09:29:57 -04:00
Joseph Doherty 15f3797f1e feat(adminui): editable AbLegacy device + tag lists via CollectionEditor 2026-05-29 09:26:25 -04:00
Joseph Doherty 534d670b21 feat(adminui): editable AbCip device + tag lists via CollectionEditor 2026-05-29 09:22:51 -04:00
Joseph Doherty b351a81c8f fix(adminui): preserve un-edited Modbus tag fields across edit (review)
Capture the original ModbusTagDefinition as _source in ModbusTagRow and
rewrite ToDefinition() to use 'with {}', so StringByteOrder, ArrayCount,
Deadband, UnitId, and CoalesceProhibited survive a load→edit→save cycle.
2026-05-29 09:18:36 -04:00
Joseph Doherty c4116e54c9 feat(adminui): editable Modbus tag list via CollectionEditor 2026-05-29 09:14:06 -04:00
Joseph Doherty 5622e51006 fix(adminui): clean up dev-migration note on Home page
Removed the F15 follow-up annotation that was visible to end users.
Replaced with a one-line orientation pointer to the nav.
2026-05-29 08:02:57 -04:00
Joseph Doherty 7c9621040e feat(adminui): wire Galaxy picker to live browser + attribute side-panel 2026-05-28 16:17:34 -04:00
Joseph Doherty 1b0baf7025 feat(adminui): wire OpcUaClient picker to live browser 2026-05-28 16:16:37 -04:00
Joseph Doherty 063005fefa feat(adminui): DriverTagPicker modal + 9 static address builders
- DriverTagPicker shell: modal chrome + per-driver picker body
  rendered as ChildContent.
- 9 picker bodies (Modbus/AbCip/AbLegacy/S7/TwinCat/FOCAS/
  OpcUaClient/Galaxy/Historian.Wonderware). 5 have computed
  builder logic + unit tests; 4 are free-text passthroughs
  (live browse for OPC UA + Galaxy is a documented follow-up).
- Each typed driver page gets a "Pick address" button that opens
  the modal with the matching body. Picked address surfaces in
  the modal footer for manual copy — no JS interop in v1.
2026-05-28 11:21:33 -04:00
Joseph Doherty 4b374fd177 feat(adminui): Test Connect button on every typed driver page
- AdminProbeService routes TestDriverConnect through
  IAdminOperationsClient with a 65s outer guard (actor side already
  clamps to [1,60]).
- Added generic AskAsync<T> to IAdminOperationsClient interface and
  AdminOperationsClient impl, delegating straight to the Akka proxy.
- DriverTestConnectButton renders the button + inline result chip,
  auto-clears after 30s, disables during in-flight.
- Wired into all 9 typed driver pages directly under the
  identity section. Sources timeout from the form's
  ProbeTimeoutSeconds; sources config JSON from the form's
  current Options (operator can test BEFORE saving).
2026-05-28 11:02:49 -04:00
Joseph Doherty 4584612a1a feat(adminui): DriverStatusPanel + wire into 9 typed pages
Live panel subscribed to the /hubs/driverstatus SignalR feed —
renders state chip, last-success age, 5-min error count, last
error message. Auto-reconnect; dimmed when no push arrives for 30s.
Hidden for new instances (nothing deployed yet); shown read-only
on every edit-mode page. Reconnect/Restart buttons land in Phase 8.
2026-05-28 10:29:43 -04:00
Joseph Doherty c4086c243c fix(adminui): S7 typed page no longer wipes Tags on save
- S7DriverPage.FormModel now preserves Tags through Form ↔ Options
  translation (was hard-coding Tags = [] on every save, silently
  destroying any tag list that operators had configured).
- Add FormModel_RoundTrip tests for OpcUaClient and Historian
  mirror classes — both were translating Options ↔ form-model
  entirely untested.
- Surface S7 Tags in the round-trip test so this regression
  can't reach merge again.
2026-05-28 10:06:43 -04:00
Joseph Doherty a971db3ee5 refactor(adminui): retire generic DriverEdit.razor
All 9 driver types now have typed pages; DriverEditRouter dispatches
to them directly. Unknown DriverType strings (e.g. legacy rows) render
an explicit error notice instead of falling through to a generic
editor — the failure mode is now visible, not silent.
2026-05-28 09:59:25 -04:00
Joseph Doherty 5f8fa7004c feat(adminui): wire all 9 typed pages into DriverEditRouter map
DriverEditRouter now dispatches every known DriverType to its typed
page. The legacy DriverEdit fallback remains in ResolveComponentType
for forward-compatibility with as-yet-unknown driver types but is no
longer reached for any current driver.
2026-05-28 09:58:36 -04:00
Joseph Doherty 059a6218f7 feat(adminui): AbLegacy typed driver page 2026-05-28 09:57:07 -04:00
Joseph Doherty 8149739161 feat(adminui): FOCAS typed driver page
Adds FocasDriverPage.razor (route: /clusters/{id}/drivers/new/focas) with
typed sections for timeout, probe, AlarmProjection (enabled + poll interval),
HandleRecycle (enabled + interval in minutes), FixedTree (enabled + axis/
program/timer poll intervals), and read-only JSON views for Devices and Tags.
FormModel uses flat settable properties + FromOptions/ToOptions with
appropriate unit conversions (ms, minutes). Also adds
FocasDriverPageFormSerializationTests (3 tests: JSON round-trip, unknown-field
drop, FormModel round-trip covering all sub-options classes).
2026-05-28 09:56:53 -04:00
Joseph Doherty 2c16062457 feat(adminui): Historian.Wonderware typed driver page 2026-05-28 09:55:15 -04:00
Joseph Doherty dfbf6793de feat(adminui): TwinCat typed driver page
Adds TwinCATDriverPage.razor (route: /clusters/{id}/drivers/new/twincat)
with typed fields for timeout, UseNativeNotifications, EnableControllerBrowse,
NotificationMaxDelayMs, probe sub-options (enabled/interval/timeout/admin
timeout), and read-only JSON views for Devices and Tags collections.
FormModel uses flat settable properties + FromOptions/ToOptions. Also adds
TwinCATDriverPageFormSerializationTests (3 tests). Fixes pre-existing
placeholder syntax error in AbCipDriverPage.razor (@raw_cpu_type in
attribute caused RZ9986).
2026-05-28 09:54:49 -04:00
Joseph Doherty a243cfd126 feat(adminui): Galaxy typed driver page 2026-05-28 09:52:31 -04:00
Joseph Doherty 5cad9b260e feat(adminui): S7 typed driver page
Adds S7DriverPage.razor (route: /clusters/{id}/drivers/new/s7) with
typed fields for host, port, CpuType InputSelect, rack, slot, timeout,
probe sub-options, and read-only JSON tag view. FormModel uses flat
settable properties and FromOptions/ToOptions round-trip; no
init-only bindings in Razor. Also adds
S7DriverPageFormSerializationTests (3 tests: JSON round-trip,
unknown-field drop, FormModel round-trip).
2026-05-28 09:52:10 -04:00
Joseph Doherty a3073d16bf feat(adminui): Modbus typed driver page 2026-05-28 09:52:01 -04:00
Joseph Doherty efcc2311e6 feat(adminui): OpcUaClient typed driver page 2026-05-28 09:50:34 -04:00
Joseph Doherty 27b3a014da refactor(adminui): hand /drivers routes to DriverTypePicker + DriverEditRouter
Removes both @page directives from DriverEdit.razor. The picker owns
/drivers/new; the router owns /drivers/{id} and dispatches via
DynamicComponent (currently falls back to DriverEdit for every driver
type — Phase 4 populates the type map one driver at a time).
2026-05-28 09:39:49 -04:00
Joseph Doherty 55e8bf70d9 feat(adminui): add DriverEditRouter dispatch page
Falls back to legacy DriverEdit until Phase 4 populates the type-map.
2026-05-28 09:38:35 -04:00
Joseph Doherty c0ce5d02bd feat(adminui): add DriverTypePicker landing page
Adds /clusters/{ClusterId}/drivers/new picker page (Task 3.1). Renders
a 9-card Bootstrap grid — one card per driver type — each linking to
/clusters/{ClusterId}/drivers/new/{slug}. No data fetch; type list is
hardcoded. Route collides with DriverEdit.razor's same directive; Task
3.3 removes the duplicate to resolve the runtime ambiguity.
2026-05-28 09:36:54 -04:00
Joseph Doherty a28f4cdd25 refactor(adminui): drive DriverEdit.razor through shared section components
No functional change — the identity, resilience, and save-bar are now
each in their own reusable component so the typed driver pages (Phase 4)
can share them. The middle "Driver config (JSON)" panel stays inlined
for now — it's replaced wholesale by typed forms in Phase 4.
2026-05-28 09:33:06 -04:00
Joseph Doherty 6884de9774 revert(adminui): restore 'OtOpcUa Admin' login title
v2-ci / build (push) Failing after 44s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
User chose to revert the MxAccess Gateway rebrand on the login card. Keep
the layout fix from c064ec1 (no panel-head top strip; inline h1.login-title)
and just put the original product name back.
2026-05-26 14:50:06 -04:00
Joseph Doherty c064ec16cf fix(security,adminui): logout redirects to /login + restyle login card
v2-ci / build (push) Failing after 41s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Two small UX fixes:

- AuthEndpoints.LogoutAsync now redirects browser callers to /login after
  SignOutAsync instead of returning 204 NoContent. 204 was correct for the
  REST contract but left browsers stuck on the page they came from (the
  cookie was cleared but no navigation happened, so "Sign out" appeared
  to do nothing). API callers can still opt into the status-only behavior
  by sending `Accept: application/json`.

- Login.razor drops the .panel-head top strip; the sign-in card now reads
  as a self-contained form with an inline title "MxAccess Gateway Admin —
  sign in". Added a .login-title CSS class to site.css that matches the
  panel-head's typographic weight without the bar.
2026-05-26 14:47:53 -04:00
Joseph Doherty a1a7646b33 fix(adminui): refresh stale F9 stub copy on /alerts page
ScriptedAlarmActor (Runtime/ScriptedAlarms) shipped a while back — the
"Engine wiring (F9 ScriptedAlarmActor) is pending" stub message was
misleading. Also drop the matching "(F9)" / "(future)" parentheticals
in the intro panel and frame the empty state as a current-window
condition, not a missing feature.
2026-05-26 13:53:09 -04:00
Joseph Doherty e4d0d82f7f feat(adminui): collapsible nav sidebar with cookie state + LoginLayout
Port the ScadaLink CentralUI sidebar pattern into the OtOpcUa AdminUI:

- Drop the top app-bar. Brand moves into the side rail's header — same
  visual rhythm as ScadaLink's NavMenu.
- New NavSection.razor: collapsible eyebrow toggle (rail-eyebrow-toggle CSS)
  with a chevron + label. Mirrors ScadaLink/Components/Layout/NavSection.
- New NavSidebar.razor: interactive island carrying the three section
  groups (Navigation / Scripting / Live) + session block. Marked
  @rendermode InteractiveServer; MainLayout itself stays static-rendered
  because layouts can't take a RenderFragment Body across an interactive
  boundary.
- New wwwroot/js/nav-state.js: window.navState.get/.set persists the
  expanded-section list to the otopcua_nav cookie (one-year lifetime,
  SameSite=Lax). Same shape as ScadaLink's scadabridge_nav.
- New LoginLayout.razor + @layout LoginLayout on Login.razor: the login
  page now renders without the side rail — clean centred card.
- MainLayout.razor: slimmed down to the d-flex shell + hamburger toggle +
  <NavSidebar/> + @Body.
- Login.razor: also drops the trailing "LDAP bind against the configured
  directory..." footer that the user asked to remove.
- site.css: adds .side-rail .brand styles (mirrored from ScadaLink) and
  the .rail-eyebrow-toggle / .rail-eyebrow-chevron / .rail-section-body
  styles for the new collapsible UI.

Auto-expand on page load: NavSidebar seeds the expanded set from the
current URL's first path segment (in OnInitialized so it works even on
the very first server render) and from the cookie (in OnAfterRenderAsync
once JS interop is available). LocationChanged hooks keep the expanded
state in sync as the user navigates between sections.
2026-05-26 13:48:35 -04:00
Joseph Doherty 59858129cb feat(adminui): F15.3 closes F15 — live alerts/script-log, CSV import, Monaco editor
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been cancelled
v2-ci / build (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been cancelled
v2-ci / integration (push) Has been cancelled
Final F15 batch wires up the SignalR-backed live pages, ports the bulk
equipment importer, and progressively enhances the Script source editor
with Monaco.

Message contracts:
- Commons.Messages.Alerts.AlarmTransitionEvent — fires on every alarm
  state transition; published on the `alerts` DPS topic by future
  ScriptedAlarmActor (F9) emits.
- Commons.Messages.Logging.ScriptLogEntry — one log line emitted by a
  hosted script; published on the `script-logs` DPS topic by future
  VirtualTagActor (F8) + ScriptedAlarmActor (F9) emits.
  (Folder named "Logging" to dodge .gitignore's "logs/" rule.)

SignalR plumbing:
- AlertHub gains MethodName + bridge actor (AlertSignalRBridge)
- ScriptLogHub introduced; ScriptLogSignalRBridge follows the same
  DPS-subscribe → IHubContext fan-out pattern as FleetStatusSignalRBridge
- WithOtOpcUaSignalRBridges now spawns all three bridges
- MapOtOpcUaHubs maps /hubs/script-log alongside the existing hubs

Pages:
- /alerts                      live alarm tail, 200-row capacity
- /script-log                  live script-log tail with level + script
                               filter, 500-row capacity
- /clusters/{id}/equipment/import — CSV bulk Equipment add with preview
                                    (Name/MachineCode/UnsLineId/Driver +
                                    optional ZTag/SAPID/Manufacturer/Model;
                                    skips rows whose MachineCode already
                                    exists in the fleet)
- ScriptEdit progressively enhanced with Monaco editor via JSInterop —
  the textarea remains Blazor's source of truth and Monaco syncs into it
  on every keystroke so @bind keeps working; falls back gracefully if
  the CDN is unreachable.

MainLayout nav gains a "Live" section (Deployments, Alerts, Alarms
historian) and a "Scripts" link under Scripting. ClusterEquipment
surfaces the new Import CSV button.

Tally: F15 ships ~42 razor pages + 3 SignalR hubs + 3 bridge actors.
Microsoft.AspNetCore.SignalR.Client added (was already in central PM).

All 104 v2 tests remain green.
2026-05-26 08:39:17 -04:00
Joseph Doherty ae980aef5d feat(adminui): F15.2 batch 4 — closes live-edit forms (Acl/VirtualTag/ScriptedAlarm/Script)
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been cancelled
v2-ci / build (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been cancelled
v2-ci / integration (push) Has been cancelled
Final batch of F15.2. After this commit every entity surfaced by the
Phase A-D read views has a matching new/edit/delete form.

- AclEdit.razor                /clusters/{id}/acls/{new|aclId}
  - NodePermissions [Flags] enum surfaced as per-bit checkboxes plus
    one-click bundle buttons (ReadOnly / Operator / Engineer / Admin)
  - ScopeKind select + ScopeId free-text target (null = cluster-wide)
- VirtualTagEdit.razor         /virtual-tags/{new|virtualTagId}
  - Trigger validation: enforces at least one of ChangeTriggered or
    TimerIntervalMs is set
- ScriptedAlarmEdit.razor      /scripted-alarms/{new|scriptedAlarmId}
  - AlarmType select with OPC UA Part 9 subtypes
  - MessageTemplate is a textarea (template tokens are server-resolved)
- ScriptEdit.razor             /scripts/{new|scriptId}
  - SHA-256 hash computed from SourceCode on save (operator never sees
    or edits SourceHash directly)
  - InputTextArea now; Monaco syntax editor is a future enhancement

List pages (ClusterAcls / VirtualTags / ScriptedAlarms / Scripts) all
gain New + per-row Edit affordances.

Tally: F15.2 shipped CRUD for 11 entities — Cluster, ClusterNode,
UnsArea, UnsLine, Namespace, DriverInstance, Equipment, Tag, NodeAcl,
VirtualTag, ScriptedAlarm, Script.

All 9 integration tests still green.
2026-05-26 08:27:56 -04:00
Joseph Doherty 2662ac08e4 feat(adminui): F15.2 batch 3 — Equipment + Tag CRUD (operator surfaces)
v2-ci / build (push) Failing after 44s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (push) Has been skipped
The two most-edited entities for daily operator workflows. Both follow the
same single-page edit-or-create pattern from batches 1 + 2 with RowVersion
optimistic concurrency.

- EquipmentEdit.razor   /clusters/{id}/equipment/{new|EquipmentId}
  - EquipmentId is system-generated on create (decision #125): EQ-{first
    12 hex chars of a new EquipmentUuid}.
  - UNS line + driver instance selects are scoped to the cluster.
  - All 9 OPC 40010 identification fields surfaced as an optional panel.
  - MachineCode uniqueness checked client-side before EF unique index
    enforces it server-side.
- TagEdit.razor         /clusters/{id}/tags/{new|TagId}
  - Equipment vs FolderPath input switches based on the selected
    driver's namespace kind — Equipment-kind requires EquipmentId,
    SystemPlatform-kind requires FolderPath (decision #110 invariant
    enforced client-side; sp_ValidateDraft re-enforces server-side at
    deploy).
  - DataType select uses the OPC UA built-in primitive type names.
  - TagConfig validated as JSON pre-flight.

ClusterEquipment + ClusterTags list pages get New / Edit affordances.

All 9 integration tests still green.
2026-05-26 08:22:51 -04:00
Joseph Doherty 45740578c9 feat(adminui): F15.2 batch 2 — topology entity CRUD
v2-ci / build (push) Failing after 52s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (push) Has been skipped
Same single-page edit-or-create pattern as batch 1, applied to the
foundational topology entities. After this batch the whole hierarchy
(cluster → nodes → UNS areas → UNS lines → namespaces → drivers) is
fully editable through the UI.

- ClusterEdit.razor                  /clusters/{id}/edit
  Update + delete for an existing cluster. NodeCount stays coupled to
  RedundancyMode (None→1, Warm/Hot→2). ModifiedBy taken from
  AuthenticationStateProvider.
- NodeEdit.razor                     /clusters/{id}/nodes/{new|nodeId}
  Full ClusterNode CRUD. ApplicationUri uniqueness is enforced by EF
  index; ServiceLevelBase defaults to 200 (primary preference) on
  create; per-node DriverConfigOverridesJson validated as JSON.
- UnsAreaEdit.razor                  /clusters/{id}/uns/areas/{new|id}
- UnsLineEdit.razor                  /clusters/{id}/uns/lines/{new|id}
  UNS structure CRUD; Lines pick their parent Area from a select that
  loads the cluster's areas.

List pages updated:
- ClusterOverview now shows an "Edit cluster" button + a "New node"
  action on the nodes panel + per-row Edit buttons.
- ClusterUns gains New/Edit affordances for both Areas and Lines.

All 9 integration tests still green; no regressions.
2026-05-26 08:18:49 -04:00
Joseph Doherty 5ae67a48ba feat(adminui): F15.2 batch 1 — Namespace + DriverInstance live-edit CRUD
v2-ci / build (push) Failing after 34s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (push) Has been skipped
Pattern proof for the live-edit forms gated by Phases A–D's read views.
Each entity gets a single edit page handling both create (route param
omitted) and update (route param present) modes, with RowVersion-based
optimistic concurrency checked against EF Core's
DbUpdateConcurrencyException.

Pattern:
- @page "/clusters/{id}/<thing>/new"
- @page "/clusters/{id}/<thing>/{rowId}"
- IsNew computed from rowId presence
- EditForm + DataAnnotations validation
- byte[] RowVersion stashed on FormModel; assigned to
  Entry(e).Property(e => e.RowVersion).OriginalValue before SaveChanges
- Delete button (edit mode only) flows through the same RowVersion check
- Concurrency conflict surfaces as an inline error panel; user reloads

This batch:
- NamespaceEdit.razor          — small entity, validates the pattern
- DriverEdit.razor             — keystone for everything downstream
                                 (Equipment/Tag/VirtualTag/ScriptedAlarm),
                                 JSON config editor per Q1 with reformat
                                 on save and validation pre-flight
- ClusterNamespaces row gains an Edit button + New action
- ClusterDrivers expanded view gains an Edit button + New action

Equipment/UnsArea/UnsLine/Tag/ACL/VirtualTag/ScriptedAlarm/Script forms
follow this same template in subsequent F15.2 batches.

All 9 integration tests still green; no v2 test regressions.
2026-05-26 08:14:36 -04:00
Joseph Doherty 74161f9460 feat(adminui): F15 Phase D — logic + ops pages
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been cancelled
v2-ci / integration (push) Has been cancelled
v2-ci / build (push) Has been cancelled
- ClusterAudit (/clusters/{id}/audit) — reads ConfigAuditLog with the
  EventId/CorrelationId columns added in F3; shown as a Cluster tab
- VirtualTags (/virtual-tags)            — fleet-wide read view
- ScriptedAlarms (/scripted-alarms)      — fleet-wide read view
- Scripts (/scripts)                     — fleet-wide; expandable code preview
- RoleGrants (/role-grants)              — per Q4, surfaces the fleet-wide
                                           LDAP-group → role mapping from
                                           Authentication:Ldap:GroupToRole
                                           (read-only; reload via host restart)
- Certificates (/certificates)           — own/trusted/issuer/rejected store
                                           contents resolved against
                                           OpcUa:PkiStoreRoot config (F13a)
- Reservations (/reservations)           — ExternalIdReservation table
- AlarmsHistorian (/alarms-historian)    — live HistorianAdapterActor sink
                                           status via the F11 GetStatus query;
                                           5s polling

ScriptLog deferred (needs the F16-deferred ScriptLogHub bridge).
ClusterNav extended with the Audit tab.

Adds an AdminUI → Runtime project reference so the historian status page can
inject IRequiredActor<HistorianAdapterActorKey>. NuGet audit suppression for
the transitive Opc.Ua.Core advisory mirrored from the Runtime project.

All 104 v2 tests still green.
2026-05-26 08:01:23 -04:00
Joseph Doherty 396052a126 feat(adminui): F15 Phase C — config-tab read views (Equipment/UNS/Namespaces/Drivers/Tags/ACLs)
v2-ci / build (push) Failing after 38s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (push) Has been skipped
Per Q3 of the rebuild plan, each v1 ClusterDetail tab becomes a separate
route under /clusters/{id}/<tab>. This batch adds read-only table views
for the six core config entity types; live-edit forms with RowVersion
concurrency land in Phase C.2 once the read-view shape is reviewed.

- ClusterEquipment    /clusters/{id}/equipment   — joins via DriverInstance
                                                   so the cluster scope works
- ClusterUns          /clusters/{id}/uns         — Areas + Lines tables
- ClusterNamespaces   /clusters/{id}/namespaces  — Kind + URI + Enabled chip
- ClusterDrivers      /clusters/{id}/drivers     — collapsed list with JSON
                                                   config expandable per Q1
                                                   (typed editors deferred)
- ClusterTags         /clusters/{id}/tags        — first 200 by name + filter
- ClusterAcls         /clusters/{id}/acls        — LDAP group + scope +
                                                   NodePermissions bits

Shared ClusterNav.razor extracted; ClusterOverview + ClusterRedundancy
updated to use it. _Imports.razor adds Components.Shared so the shared
nav is in scope across pages.
2026-05-26 07:56:39 -04:00
Joseph Doherty fd0cc4dfdb feat(adminui): F15 Phase B — cluster CRUD + Overview/Redundancy routes
v2-ci / build (push) Failing after 38s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (push) Has been skipped
- ClustersList (/clusters) — table view, row-click opens detail
- NewCluster (/clusters/new) — EditForm with DataAnnotations; redundancy
  mode + node-count coupling enforced client-side (None→1, Warm/Hot→2);
  CreatedBy taken from AuthenticationStateProvider
- ClusterOverview (/clusters/{id}) — cluster details + last-deployment
  badge + node list. Per Q3, the legacy 10-tab monolith is split into
  separate routes; this page hosts the Overview "tab" as its primary slot
- ClusterRedundancy (/clusters/{id}/redundancy) — static ServiceLevelBase
  config view; live ServiceLevel comes via RedundancyStateActor DPS topic
  (deferred to its own follow-up once the SignalR bridge lands)

The other 8 v1 cluster tabs (Equipment, UNS, Namespaces, Drivers, Tags,
ACLs, ScriptedAlarms, Scripts, Audit) land in Phase C/D.
2026-05-26 07:52:41 -04:00
Joseph Doherty 850d6774ea feat(adminui): F15 Phase A — shell + auth + fleet + hosts pages
v2-ci / build (push) Failing after 38s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (push) Has been skipped
Implements Phase A of the F15 rebuild plan: minimum-viable Admin surface
with a working sign-in path and a fleet-state landing page. Decisions Q1–Q5
of docs/v2/AdminUI-rebuild-plan.md were taken as recommended.

- App.razor (moved into AdminUI library from the Host stub; vendored
  Bootstrap from RCL wwwroot — no public CDN, air-gap safe)
- Routes.razor (AuthorizeRouteView enforces page-level [Authorize])
- RedirectToLogin.razor (preserves returnUrl through the auth hop)
- Login.razor (static SSR, posts to /auth/login; Q5 wording about
  generic-vs-specific LDAP errors)
- Account.razor (identity + fleet roles + raw LDAP groups; Q4 — no
  per-cluster grants; fleet-wide LDAP-group → role mapping only)
- Fleet.razor (per-node deployment status: reads NodeDeploymentState
  + unions with IClusterRoleInfo.MembersWithRole("driver") so freshly-
  joined nodes appear as "waiting"; 10s auto-refresh)
- Hosts.razor (Akka cluster topology: members, status, roles, role-
  leader; 5s auto-refresh)

Host's stub App.razor deleted; Program.cs now points at
AdminUI.Components.App via an added using.

All 104 v2 tests remain green.
2026-05-26 07:49:35 -04:00
Joseph Doherty b266f63cd7 feat(adminui): thread User.Identity.Name into Deployments createdBy (F18)
Injects AuthenticationStateProvider and reads the current user's identity
name on Deploy click, replacing the "(current user)" placeholder.
Anonymous case falls back to "(anonymous)" — should never hit in practice
since the page requires FleetAdmin/ConfigEditor.
2026-05-26 06:17:53 -04:00
Joseph Doherty 76310b8829 chore(cleanup): delete OtOpcUa.Server, OtOpcUa.Admin, and obsolete v1 tests
Task 56: removes the legacy in-process Server + Admin Web project + their test
projects (Server.Tests, Admin.Tests, Admin.E2ETests). The fused OtOpcUa.Host
binary built across Phases 1-9 is now the sole production entry point.

What happened to the 47 legacy Admin Blazor pages: per follow-up F15, the
v1 architecture's draft/publish UX is replaced by v2's live-edit + snapshot-
deploy model, so a 1:1 migration is not meaningful. The mechanical move via
git mv preserves the history; service classes + page bodies that referenced
removed v1 types (ConfigGeneration, RedundancyRole, GenerationId) were
deleted. AdminUI now ships a minimal Home page + the v2 Deployments page.

Per-page rebuild against the v2 surface is tracked as F15. The v2 Deployments
page (Task 52) is the only first-party UI shipping in this PR.

Task 57: solution build green; 84+ tests green across active v2 + legacy
driver test projects.
2026-05-26 05:38:31 -04:00
Joseph Doherty f167808a2c feat(adminui): Deployments page with drift indicator and Deploy button 2026-05-26 05:18:00 -04:00