Commit Graph

67 Commits

Author SHA1 Message Date
Joseph Doherty
22d91c858a feat(ui): Layer E2 OpcUaEndpointEditor gains Authentication / Advanced / Deadband sections
Three new sections inserted into <OpcUaEndpointEditor>:

1. Authentication (between the existing Connection row and Timing)
   - 'Enable Authentication' button when Config.UserIdentity is null
   - TokenType select (Anonymous / UsernamePassword / X509Certificate)
   - Conditional Username + Password inputs for UsernamePassword
   - Conditional Certificate path + Certificate password for X509Certificate
   - 'Remove Authentication' button

2. Advanced subscription (after the existing Subscription row)
   - Subscription display name (text)
   - Subscription priority (number 0-255)
   - Timestamps to return (Source / Server / Both select)
   - Discard oldest (checkbox)

3. Deadband filter (after Advanced subscription)
   - 'Enable Deadband' button when Config.Deadband is null
   - Type select (Absolute / Percent), Value number input
   - 'Remove Deadband' button

EnableAuthentication and EnableDeadband helpers complement EnableHeartbeat.
All new fields use the existing RenderFieldError helper for validator errors.

82/82 CentralUI tests pass (the 10 new editor tests drove the design).
2026-05-12 02:30:06 -04:00
Joseph Doherty
cfb90d2078 fix(ui/admin): always clear _loading in DataConnectionForm.OnInitializedAsync 2026-05-12 01:14:18 -04:00
Joseph Doherty
9916aeaa47 refactor(ui/admin): DataConnectionForm uses OpcUaEndpointEditor and typed model 2026-05-12 01:11:49 -04:00
Joseph Doherty
1c71d3342a feat(ui): OpcUaEndpointEditor Blazor component 2026-05-12 01:05:32 -04:00
Joseph Doherty
da5fdf0e63 feat(ui/admin): Topology-style refresh of Data Connections page
Brings the Data Connections admin page up to the same UX standard as the
Topology page:
- Search box with dim non-matches (opacity 0.4, shape preserved)
- Toolbar: + Connection (disabled until a site is selected), Refresh,
  Expand, Collapse
- Site context menu gains "Add Connection here" that navigates with
  ?siteId= so the form preselects + locks the Site field
- Form gains "Primary Endpoint" / "Backup Endpoint" h6 subsection
  headers matching the SiteForm convention; Failover Retry Count moved
  inside the Backup subsection
- URL renamed: /admin/connections (primary) + /admin/data-connections
  (legacy secondary @page). Same dual-route treatment on the form
- Nav label: "Data Connections" -> "Connections"
- Adds DataConnectionsPageTests bUnit suite (6 tests)
2026-05-11 22:42:48 -04:00
Joseph Doherty
f3386d0278 feat(ui/deployment): consolidate sites/areas/instances into Topology page
Single /deployment/topology page replaces /deployment/instances (legacy URL
preserved as a secondary @page directive) and the /admin/areas* CRUD pages.
TreeView with Site → Area → Instance, V1–V7 visual guide (bi-building /
bi-diagram-3 / bi-box), always-visible empty containers, search dim, F2
inline area rename, and right-click context menus per node kind (Add Area,
Move to Area…, lifecycle actions, etc.).

Adds AreaService.MoveAreaAsync with cycle prevention, same-site enforcement,
and name-collision check at the new parent. Instance rename intentionally
out of scope — UniqueName is the site-side actor identity, requires its own
design pass.
2026-05-11 22:03:55 -04:00
Joseph Doherty
b2eddd9713 feat(ui/templates): derived-template action and slimmer composition row
Right-click a template now offers "New Derived Template" — opens
TemplateCreate with the parent pre-selected via a new ?parentId query
parameter. Composition rows in the tree drop the trailing
"→ TargetName" muted text; the kind glyph plus the instance name carry
enough meaning, and the composed template is one click away from the
row's right-click menu.
2026-05-11 21:29:32 -04:00
Joseph Doherty
b4cb7e6f5f feat(templates): lock ParentTemplateId after creation
Template inheritance is set once at create time and immutable on update.
UpdateTemplateAsync now returns "Parent template cannot be changed after
creation." when the caller sends a parent that differs from the stored
value — server-side enforcement covers UI, ManagementService, and CLI.
TemplateEdit renders the parent as static plaintext rather than an
editable dropdown; TemplateCreate's parent picker is unchanged.
2026-05-11 21:29:21 -04:00
Joseph Doherty
8e388a89c5 feat(ui/templates): adopt TreeView design guide; split editor to /design/templates/{id}
Templates page is now a tree-only browser; editing happens on a dedicated
TemplateEdit page. Drag-drop is replaced by context-menu Move-to-Folder.
TreeView gains Bootstrap Icons (chevron + per-kind glyphs), ancestor guide
lines, defined hover/selected/focus tokens, and Escape-dismisses-menu per
the new Visual Design Guide (V1-V7) in Component-TreeView.md.
2026-05-11 20:52:34 -04:00
Joseph Doherty
f3b33e7e1d fix(ui/treeview): union sessionStorage keys instead of overwriting
The previous fix tried to defer page-side RevealNode to the second
render so TreeView's async sessionStorage load could finish first. In
practice Blazor Server didn't always fire a second OnAfterRenderAsync
on the page after the deep-link load, so the reveal never ran.

Real fix: change TreeView's storage-load to UNION the restored keys
with whatever's already in _expandedKeys, instead of REPLACING. That
way the page can call RevealNode whenever it wants and the storage
restore can't clobber the reveal regardless of completion order. The
page-side guard simplifies back to a one-shot reveal on first render.

Semantic note: if a deep-link reveal expands an ancestor that the user
had previously collapsed, the deep link wins. Intentional — the URL
expresses the navigation intent.
2026-05-11 12:42:38 -04:00
Joseph Doherty
d8e6f44616 fix(ui/templates): defer deep-link reveal until TreeView restores sessionStorage
Both page.OnAfterRenderAsync(firstRender=true) and
TreeView.OnAfterRenderAsync(firstRender=true) ran concurrently:
- Page called RevealNode → added ancestor keys to _expandedKeys
- TreeView awaited treeviewStorage.load → replaced _expandedKeys with
  the persisted set (often empty if user collapsed before navigating)

Whichever JS interop completed second won. When TreeView won, the deep-link
reveal silently lost. Gate the reveal on firstRender==false so it runs
strictly after TreeView's restore is done.
2026-05-11 12:39:21 -04:00
Joseph Doherty
ca164dca03 fix(ui/templates): stop drop propagation on folder nodes
Without stopPropagation, dropping a template onto a folder fires both
OnDrop(folder) and OnDropOnRoot via event bubbling. The two async handlers
race on the same scoped DbContext, which is not thread-safe — the second
throws ObjectDisposedException and tears down the Blazor circuit. Surfaced
during browser smoke testing via JS-dispatched DragEvent sequence.
2026-05-11 12:28:05 -04:00
Joseph Doherty
acead212b2 fix(ui/templates): dereference string params with @ and stack toolbar below title
Smoke testing revealed two issues introduced by the modal extraction commit:

1. ErrorMessage / InitialName / TemplateName parameters on the dialog
   components were passed as bare strings (e.g. ErrorMessage="_newFolderError")
   instead of dereferenced C# expressions (ErrorMessage="@_newFolderError").
   Razor treats unquoted-but-not-@-prefixed values to string parameters as
   string literals — so the error block rendered the literal field name in
   red whenever the modal opened. Non-string parameters (int/IEnumerable)
   were fine since Razor treats those as C# expressions by default.

2. The Templates header + 4-button toolbar shared one flex row, but at
   col-md-4 / col-lg-3 width the buttons overflowed into the right-column
   empty-state area. Stack title above a full-width btn-group instead.
2026-05-11 12:20:40 -04:00
Joseph Doherty
3587ab4fcb refactor(ui/templates): extract dialog modals into shared components 2026-05-11 12:03:35 -04:00
Joseph Doherty
17e690f6ef test(ui/templates): cover drag-template-to-root via bUnit DragEventArgs 2026-05-11 12:00:07 -04:00
Joseph Doherty
ca3b34223d feat(ui/templates): reveal deep-linked template on initial render 2026-05-11 11:21:53 -04:00
Joseph Doherty
c60aad9df4 feat(ui/templates): native HTML5 drag-drop reorganization 2026-05-11 11:20:42 -04:00
Joseph Doherty
fc105acd7c feat(ui/templates): new-folder, new-template, move-template dialogs 2026-05-11 11:18:36 -04:00
Joseph Doherty
39e6e0a525 feat(ui/templates): per-kind context menus + folder rename/delete 2026-05-11 11:15:25 -04:00
Joseph Doherty
4977f99a74 feat(ui/templates): split-pane layout with folder + composition tree 2026-05-11 11:12:40 -04:00
Joseph Doherty
78165b3d99 feat(ui/templates): replace flat tree model with TmplNode discriminated by kind 2026-05-11 11:10:39 -04:00
Joseph Doherty
20f60c88f9 feat(ui/templates): load folders alongside templates 2026-05-11 11:09:16 -04:00
Joseph Doherty
9dccf8e72f deprecate(lmxproxy): move all LmxProxy code, tests, and docs to deprecated/
LmxProxy is no longer needed. Moved the entire lmxproxy/ workspace, DCL
adapter files, and related docs to deprecated/. Removed LmxProxy registration
from DataConnectionFactory, project reference from DCL, protocol option from
UI, and cleaned up all requirement docs.
2026-04-08 15:56:23 -04:00
Joseph Doherty
02a7e8abc6 feat(health): show all cluster nodes (online/offline, primary/standby) in health dashboard
Add NodeStatus record, IClusterNodeProvider interface, and AkkaClusterNodeProvider
that queries Akka cluster membership for all site-role nodes. HealthReportSender
populates ClusterNodes before each report. UI shows a row per node with
hostname, Online/Offline badge, and Primary/Standby badge. Falls back to
single-node display if ClusterNodes is not populated.
2026-03-24 16:19:39 -04:00
Joseph Doherty
e84a831a02 feat(health): redesign health dashboard with 4-column layout and new metrics
New fields in SiteHealthReport: NodeHostname, DataConnectionEndpoints
(primary/secondary), DataConnectionTagQuality (good/bad/uncertain),
ParkedMessageCount. New collector methods to populate them.

Health dashboard redesigned to match mockup: Nodes | Data Connections
(with per-connection tag quality) | Instances + S&F Buffers | Error
Counts + Parked Messages. Site names resolved from repository.
2026-03-24 16:19:39 -04:00
Joseph Doherty
5e2a4c9080 fix(ui): align TreeView node text by giving toggle and spacer equal fixed width 2026-03-24 16:19:39 -04:00
Joseph Doherty
0abaa47de2 fix(ui): normalize TreeView expanded keys to strings for sessionStorage compatibility
Keys from KeySelector (e.g. boxed int) were compared against string keys
restored from sessionStorage, causing expansion state to be lost on
navigation. All keys are now normalized to strings internally.
2026-03-24 16:19:39 -04:00
Joseph Doherty
a0a6bb4986 refactor(ui): replace manual template inheritance tree with TreeView component 2026-03-24 16:19:39 -04:00
Joseph Doherty
2b5dabb336 refactor(ui): redesign Areas page with TreeView and dedicated Add/Edit/Delete pages
Areas page now shows a single TreeView with sites as roots and areas as
children. Context menus: sites get "Add Area", areas get "Add Child Area",
"Edit Area", "Delete Area" — each navigating to a dedicated page.

The Delete Area page shows a TreeView of the area and all recursive children
with assigned instances. Deletion is blocked if any instances are assigned
to the area or its descendants.
2026-03-24 16:19:39 -04:00
Joseph Doherty
968fc4adc7 fix(ui): disable site and instance dropdowns while debug view is connected 2026-03-24 16:19:39 -04:00
Joseph Doherty
4c7fa03c07 fix(ui): remove default list-style bullets from TreeView ul elements 2026-03-24 16:19:39 -04:00
Joseph Doherty
addbb6ffeb fix(ui): move treeview-storage.js to Host wwwroot where static files are served 2026-03-24 16:19:39 -04:00
Joseph Doherty
f1537b62ca refactor(ui): replace instances table with hierarchical TreeView (Site → Area → Instance) 2026-03-24 16:19:39 -04:00
Joseph Doherty
71894f4ba9 refactor(ui): replace manual area tree rendering with TreeView component 2026-03-24 16:19:39 -04:00
Joseph Doherty
4426f3e928 refactor(ui): replace data connections table with TreeView grouped by site 2026-03-24 16:19:39 -04:00
Joseph Doherty
08d511f609 test(ui): add external filtering tests for TreeView (R8) 2026-03-24 16:19:39 -04:00
Joseph Doherty
4e5b5facec feat(ui): add right-click context menu to TreeView (R15) 2026-03-24 16:19:39 -04:00
Joseph Doherty
f127efe6ea feat(ui): add ExpandAll, CollapseAll, RevealNode to TreeView (R12, R13) 2026-03-24 16:19:39 -04:00
Joseph Doherty
d3a6ed5f68 feat(ui): add sessionStorage persistence for TreeView expansion state (R11) 2026-03-24 16:19:39 -04:00
Joseph Doherty
da4f29f6ee feat(ui): add selection support to TreeView (R5) 2026-03-24 16:19:39 -04:00
Joseph Doherty
75648c0c76 feat(ui): add TreeView<TItem> component with core rendering, expand/collapse, ARIA (R1-R4, R14) 2026-03-24 16:19:39 -04:00
Joseph Doherty
bc4fc97652 refactor(ui): extract instance bindings and overrides to dedicated Configure page
Move connection bindings, attribute overrides, and area assignment from
inline expandable rows on the Instances table to a separate page at
/deployment/instances/{id}/configure for a cleaner, less cramped UX.
2026-03-22 15:58:32 -04:00
Joseph Doherty
dcdf79afdc fix(dcl): format ArrayValue objects as comma-separated strings for display
ArrayValue from LmxProxy client was showing as type name in debug views.
Added ValueFormatter utility and NormalizeValue in LmxProxyDataConnection
to convert arrays at the adapter boundary. DateTime arrays remain as
"System.DateTime[]" due to server-side v1 string serialization.
2026-03-22 14:46:15 -04:00
Joseph Doherty
ab4e88f17f feat(ui): add primary/backup endpoint fields to data connection form 2026-03-22 08:36:18 -04:00
Joseph Doherty
04af03980e feat(dcl): rename Configuration to PrimaryConfiguration, add BackupConfiguration and FailoverRetryCount 2026-03-22 08:18:31 -04:00
Joseph Doherty
970d0a5cb3 refactor: simplify data connections from many-to-many site assignment to direct site ownership
Replace SiteDataConnectionAssignment join table with a direct SiteId FK on DataConnection,
simplifying the data model, repositories, UI, CLI, and deployment service.
2026-03-21 21:07:10 -04:00
Joseph Doherty
d3194e3634 feat: separate create/edit form pages, Playwright test infrastructure, /auth/token endpoint
Move all CRUD create/edit forms from inline on list pages to dedicated form pages
with back-button navigation and post-save redirect. Add Playwright Docker container
(browser server on port 3000) with 25 passing E2E tests covering login, navigation,
and site CRUD workflows. Add POST /auth/token endpoint for clean JWT retrieval.
2026-03-21 15:17:24 -04:00
Joseph Doherty
9b0a80dcbd feat: add GrpcNodeAAddress/GrpcNodeBAddress to Site entity, CLI, and UI 2026-03-21 11:45:22 -04:00
Joseph Doherty
fd2e96fea2 feat: replace debug view polling with real-time SignalR streaming
The debug view polled every 2s by re-subscribing for full snapshots. Now a
persistent DebugStreamBridgeActor on central subscribes once and receives
incremental Akka stream events from the site, forwarding them to the Blazor
component via callbacks and to the CLI via a new SignalR hub at
/hubs/debug-stream. Adds `debug stream` CLI command with auto-reconnect.
2026-03-21 01:34:53 -04:00
Joseph Doherty
7740a3bcf9 feat: add JoeAppEngine OPC UA nodes, fix DCL auto-reconnect and quality push
- Add JoeAppEngine folder to OPC UA nodes.json (BTCS, AlarmCntsBySeverity, Scheduler/ScanTime)
- Fix DataConnectionActor: capture Self in PreStart for use from non-actor threads,
  preventing Self.Tell failure in Disconnected event handler
- Implement InstanceActor.HandleConnectionQualityChanged to mark attributes Bad on disconnect
- Fix LmxFakeProxy TagMapper to serialize arrays as JSON instead of "System.Int32[]"
- Allow DataType and DataSourceReference updates in TemplateService.UpdateAttributeAsync
- Update test_infra_opcua.md with JoeAppEngine documentation
2026-03-19 13:27:54 -04:00