feat(uns): equipment modal wired into the tree

This commit is contained in:
Joseph Doherty
2026-06-08 13:31:14 -04:00
parent 0abd1d8fc2
commit 2beaa43d60
5 changed files with 490 additions and 5 deletions
@@ -63,6 +63,15 @@
OnSaved="OnModalSavedAsync"
OnCancel="CloseModals" />
<EquipmentModal Visible="_equipmentModalVisible"
IsNew="_equipmentModalIsNew"
UnsLineId="_equipmentModalLineId"
Existing="_equipmentModalExisting"
Lines="_equipmentModalLineOptions"
Drivers="_equipmentModalDriverOptions"
OnSaved="OnModalSavedAsync"
OnCancel="CloseModals" />
@if (_confirmNode is not null)
{
<div class="modal-backdrop fade show" style="display:block"></div>
@@ -110,6 +119,14 @@
private LineEditDto? _lineModalExisting;
private IReadOnlyList<(string Id, string Display)> _lineModalAreaOptions = Array.Empty<(string, string)>();
// --- Equipment modal state ---
private bool _equipmentModalVisible;
private bool _equipmentModalIsNew;
private string? _equipmentModalLineId;
private EquipmentEditDto? _equipmentModalExisting;
private IReadOnlyList<(string Id, string Display)> _equipmentModalLineOptions = Array.Empty<(string, string)>();
private IReadOnlyList<(string Id, string Display)> _equipmentModalDriverOptions = Array.Empty<(string, string)>();
// --- Delete-confirm state ---
private UnsNode? _confirmNode;
private bool _confirmBusy;
@@ -139,6 +156,18 @@
.Select(a => (a.EntityId!, a.DisplayName))
.ToList();
/// <summary>Returns the <c>(Id, Display)</c> line options inside a single cluster, for the equipment picker.</summary>
private IReadOnlyList<(string Id, string Display)> LinesForCluster(string? clusterId) =>
_roots
.SelectMany(ent => ent.Children)
.Where(c => c.Kind == UnsNodeKind.Cluster && c.ClusterId == clusterId)
.SelectMany(c => c.Children)
.Where(a => a.Kind == UnsNodeKind.Area)
.SelectMany(a => a.Children)
.Where(l => l.Kind == UnsNodeKind.Line && l.EntityId is not null)
.Select(l => (l.EntityId!, l.DisplayName))
.ToList();
/// <summary>
/// Toggles a node's expansion. For equipment nodes whose children have not yet
/// been loaded, lazily fetches the tag/virtual-tag leaves on first expand.
@@ -175,9 +204,10 @@
/// <summary>
/// Opens the create modal for a node's primary child: a cluster gets a new area; an area gets a
/// new line scoped to its cluster. Equipment "+ Tag" is handled in a later task.
/// new line scoped to its cluster; a line gets a new equipment scoped to its cluster. Equipment
/// "+ Tag" is handled in a later task.
/// </summary>
private void HandleAddChild(UnsNode node)
private async Task HandleAddChild(UnsNode node)
{
CloseModals();
switch (node.Kind)
@@ -196,6 +226,15 @@
_lineModalAreaOptions = AreaOptionsForCluster(node.ClusterId);
_lineModalVisible = true;
break;
case UnsNodeKind.Line:
_equipmentModalIsNew = true;
_equipmentModalExisting = null;
_equipmentModalLineId = node.EntityId;
_equipmentModalLineOptions = LinesForCluster(node.ClusterId);
_equipmentModalDriverOptions = await Svc.LoadDriversForClusterAsync(node.ClusterId!);
_equipmentModalVisible = true;
break;
}
}
@@ -226,6 +265,17 @@
_lineModalAreaOptions = AreaOptionsForCluster(node.ClusterId);
_lineModalVisible = true;
break;
case UnsNodeKind.Equipment:
var equipment = await Svc.LoadEquipmentAsync(node.EntityId!);
if (equipment is null) { return; }
_equipmentModalIsNew = false;
_equipmentModalExisting = equipment;
_equipmentModalLineId = equipment.UnsLineId;
_equipmentModalLineOptions = LinesForCluster(node.ClusterId);
_equipmentModalDriverOptions = await Svc.LoadDriversForClusterAsync(node.ClusterId!);
_equipmentModalVisible = true;
break;
}
}
@@ -238,7 +288,7 @@
/// <summary>
/// Performs the pending delete. Loads the entity's RowVersion first, then dispatches on Kind.
/// Area/Line are handled here; other kinds are wired in later tasks.
/// Area/Line/Equipment are handled here; Tag/VirtualTag are wired in later tasks.
/// </summary>
private async Task ConfirmDeleteAsync()
{
@@ -264,8 +314,14 @@
result = await Svc.DeleteLineAsync(node.EntityId!, line.RowVersion);
break;
case UnsNodeKind.Equipment:
var equipment = await Svc.LoadEquipmentAsync(node.EntityId!);
if (equipment is null) { await ReloadAndCloseAsync(); return; }
result = await Svc.DeleteEquipmentAsync(node.EntityId!, equipment.RowVersion);
break;
default:
// Equipment/Tag/VirtualTag deletes are wired in later tasks.
// Tag/VirtualTag deletes are wired in later tasks.
result = new UnsMutationResult(false, "Delete for this node kind is not yet available.");
break;
}
@@ -309,6 +365,10 @@
_lineModalVisible = false;
_lineModalExisting = null;
_lineModalAreaOptions = Array.Empty<(string, string)>();
_equipmentModalVisible = false;
_equipmentModalExisting = null;
_equipmentModalLineOptions = Array.Empty<(string, string)>();
_equipmentModalDriverOptions = Array.Empty<(string, string)>();
_confirmNode = null;
_confirmError = null;
}