From 2acea08ced7bb04ad335867010d8164a89db1cbd Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 19 Apr 2026 21:41:13 -0400 Subject: [PATCH] =?UTF-8?q?Admin=20Equipment=20editor=20=E2=80=94=20Identi?= =?UTF-8?q?ficationFields=20component=20+=20edit=20mode=20+=20three=20miss?= =?UTF-8?q?ing=20OPC=2040010=20fields.=20Closes=20the=20UI-editor=20slice?= =?UTF-8?q?=20of=20task=20#159=20(Phase=206.4=20Stream=20D=20remaining);?= =?UTF-8?q?=20the=20DriverNodeManager=20wire-in=20+=20ACL=20integration=20?= =?UTF-8?q?test=20are=20split=20into=20a=20new=20follow-up=20task=20#195?= =?UTF-8?q?=20because=20they're=20blocked=20on=20a=20prerequisite=20that?= =?UTF-8?q?=20hasn't=20shipped=20=E2=80=94=20the=20DriverNodeManager=20doe?= =?UTF-8?q?s=20not=20currently=20materialize=20Equipment=20nodes=20at=20al?= =?UTF-8?q?l=20(NodeScopeResolver=20has=20an=20explicit=20"A=20future=20re?= =?UTF-8?q?solver=20will..."=20TODO=20in=20its=20decomposition=20docstring?= =?UTF-8?q?).=20Shipping=20the=20IdentificationFolderBuilder=20call=20befo?= =?UTF-8?q?re=20the=20parent=20walker=20exists=20would=20wire=20a=20call?= =?UTF-8?q?=20that=20no=20code=20path=20hits,=20so=20the=20wire-in=20is=20?= =?UTF-8?q?deferred=20until=20the=20Equipment=20node=20walker=20lands=20fi?= =?UTF-8?q?rst.=20New=20IdentificationFields.razor=20reusable=20component?= =?UTF-8?q?=20renders=20the=209-field=20decision=20#139=20grid=20in=20a=20?= =?UTF-8?q?Bootstrap=203-column=20layout=20=E2=80=94=20Manufacturer,=20Mod?= =?UTF-8?q?el,=20SerialNumber,=20HardwareRevision,=20SoftwareRevision,=20Y?= =?UTF-8?q?earOfConstruction=20(InputNumber),=20AssetLocation,=20Manufactu?= =?UTF-8?q?rerUri=20(placeholder=20https://=E2=80=A6),=20DeviceManualUri?= =?UTF-8?q?=20(placeholder=20https://=E2=80=A6).=20Takes=20a=20required=20?= =?UTF-8?q?Equipment=20parameter=20+=202-way=20binds=20every=20field;=20no?= =?UTF-8?q?=20state=20of=20its=20own.=20Three=20fields=20that=20were=20mis?= =?UTF-8?q?sing=20from=20the=20old=20inline=20form=20=E2=80=94=20AssetLoca?= =?UTF-8?q?tion,=20ManufacturerUri,=20DeviceManualUri=20=E2=80=94=20now=20?= =?UTF-8?q?present,=20matching=20IdentificationFolderBuilder.FieldNames=20?= =?UTF-8?q?exactly.=20EquipmentTab.razor=20refactored=20to=20consume=20the?= =?UTF-8?q?=20component=20in=20both=20create=20+=20edit=20flows.=20Each=20?= =?UTF-8?q?table=20row=20gains=20an=20Edit=20button=20next=20to=20Remove.?= =?UTF-8?q?=20StartEdit=20clones=20the=20row=20into=20=5Fdraft=20so=20Canc?= =?UTF-8?q?el=20doesn't=20mutate=20the=20displayed=20list=20row=20with=20i?= =?UTF-8?q?n-flight=20edits;=20on=20Save,=20UpdateAsync=20persists=20throu?= =?UTF-8?q?gh=20EquipmentService's=20existing=20update=20path=20which=20al?= =?UTF-8?q?ready=20handles=20all=209=20Identification=20fields.=20SaveAsyn?= =?UTF-8?q?c=20branches=20on=20=5FeditMode=20=E2=80=94=20create=20still=20?= =?UTF-8?q?derives=20EquipmentId=20from=20a=20fresh=20Uuid=20via=20DraftVa?= =?UTF-8?q?lidator=20per=20decision=20#125,=20edit=20keeps=20the=20origina?= =?UTF-8?q?l=20EquipmentId=20+=20EquipmentUuid=20(immutable=20once=20set).?= =?UTF-8?q?=20FormName=20renamed=20equipment-form=20(was=20new-equipment)?= =?UTF-8?q?=20to=20work=20for=20both=20flows.=20Admin=20project=20builds?= =?UTF-8?q?=200=20errors;=20Admin.Tests=2072/72=20passing.=20No=20new=20te?= =?UTF-8?q?sts=20shipped=20=E2=80=94=20this=20PR=20is=20strictly=20a=20Raz?= =?UTF-8?q?or-component=20refactor=20+=20two=20new=20bound=20fields=20+=20?= =?UTF-8?q?an=20Edit=20branch;=20the=20existing=20EquipmentService=20tests?= =?UTF-8?q?=20cover=20both=20the=20create=20+=20update=20paths.=20Task=20#?= =?UTF-8?q?195=20created=20to=20track=20the=20blocked=20server-side=20work?= =?UTF-8?q?:=20call=20IdentificationFolderBuilder.Build=20from=20DriverNod?= =?UTF-8?q?eManager=20once=20the=20Equipment=20walker=20exists,=20plus=20a?= =?UTF-8?q?n=20integration=20test=20browsing=20Equipment/Identification=20?= =?UTF-8?q?as=20an=20unauthorized=20user=20asserting=20BadUserAccessDenied?= =?UTF-8?q?=20per=20the=20builder's=20cross-reference=20note=20in=20docs/v?= =?UTF-8?q?2/acl-design.md=20=C2=A7Identification.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Pages/Clusters/EquipmentTab.razor | 81 ++++++++++++++----- .../Pages/Clusters/IdentificationFields.razor | 49 +++++++++++ 2 files changed, 110 insertions(+), 20 deletions(-) create mode 100644 src/ZB.MOM.WW.OtOpcUa.Admin/Components/Pages/Clusters/IdentificationFields.razor diff --git a/src/ZB.MOM.WW.OtOpcUa.Admin/Components/Pages/Clusters/EquipmentTab.razor b/src/ZB.MOM.WW.OtOpcUa.Admin/Components/Pages/Clusters/EquipmentTab.razor index fdc28f1..4bc3d4f 100644 --- a/src/ZB.MOM.WW.OtOpcUa.Admin/Components/Pages/Clusters/EquipmentTab.razor +++ b/src/ZB.MOM.WW.OtOpcUa.Admin/Components/Pages/Clusters/EquipmentTab.razor @@ -36,7 +36,10 @@ else if (_equipment.Count > 0) @e.SAPID @e.Manufacturer / @e.Model @e.SerialNumber - + + + + } @@ -47,8 +50,8 @@ else if (_equipment.Count > 0) {
-
New equipment
- +
@(_editMode ? "Edit equipment" : "New equipment")
+
@@ -78,24 +81,13 @@ else if (_equipment.Count > 0)
-
OPC 40010 Identification
-
-
-
-
-
-
-
- - -
-
+ @if (_error is not null) {
@_error
}
- +
@@ -106,6 +98,7 @@ else if (_equipment.Count > 0) [Parameter] public long GenerationId { get; set; } private List? _equipment; private bool _showForm; + private bool _editMode; private Equipment _draft = NewBlankDraft(); private string? _error; @@ -125,20 +118,68 @@ else if (_equipment.Count > 0) private void StartAdd() { _draft = NewBlankDraft(); + _editMode = false; _error = null; _showForm = true; } + private void StartEdit(Equipment row) + { + // Shallow-clone so Cancel doesn't mutate the list-displayed row with in-flight form edits. + _draft = new Equipment + { + EquipmentRowId = row.EquipmentRowId, + GenerationId = row.GenerationId, + EquipmentId = row.EquipmentId, + EquipmentUuid = row.EquipmentUuid, + DriverInstanceId = row.DriverInstanceId, + DeviceId = row.DeviceId, + UnsLineId = row.UnsLineId, + Name = row.Name, + MachineCode = row.MachineCode, + ZTag = row.ZTag, + SAPID = row.SAPID, + Manufacturer = row.Manufacturer, + Model = row.Model, + SerialNumber = row.SerialNumber, + HardwareRevision = row.HardwareRevision, + SoftwareRevision = row.SoftwareRevision, + YearOfConstruction = row.YearOfConstruction, + AssetLocation = row.AssetLocation, + ManufacturerUri = row.ManufacturerUri, + DeviceManualUri = row.DeviceManualUri, + EquipmentClassRef = row.EquipmentClassRef, + Enabled = row.Enabled, + }; + _editMode = true; + _error = null; + _showForm = true; + } + + private void Cancel() + { + _showForm = false; + _editMode = false; + } + private async Task SaveAsync() { _error = null; - _draft.EquipmentUuid = Guid.NewGuid(); - _draft.EquipmentId = DraftValidator.DeriveEquipmentId(_draft.EquipmentUuid); - _draft.GenerationId = GenerationId; try { - await EquipmentSvc.CreateAsync(GenerationId, _draft, CancellationToken.None); + if (_editMode) + { + await EquipmentSvc.UpdateAsync(_draft, CancellationToken.None); + } + else + { + _draft.EquipmentUuid = Guid.NewGuid(); + _draft.EquipmentId = DraftValidator.DeriveEquipmentId(_draft.EquipmentUuid); + _draft.GenerationId = GenerationId; + await EquipmentSvc.CreateAsync(GenerationId, _draft, CancellationToken.None); + } _showForm = false; + _editMode = false; await ReloadAsync(); } catch (Exception ex) { _error = ex.Message; } diff --git a/src/ZB.MOM.WW.OtOpcUa.Admin/Components/Pages/Clusters/IdentificationFields.razor b/src/ZB.MOM.WW.OtOpcUa.Admin/Components/Pages/Clusters/IdentificationFields.razor new file mode 100644 index 0000000..582bfde --- /dev/null +++ b/src/ZB.MOM.WW.OtOpcUa.Admin/Components/Pages/Clusters/IdentificationFields.razor @@ -0,0 +1,49 @@ +@using ZB.MOM.WW.OtOpcUa.Configuration.Entities + +@* Reusable OPC 40010 Machinery Identification editor. Binds to an Equipment row and renders the + nine decision #139 fields in a consistent 3-column Bootstrap grid. Used by EquipmentTab's + create + edit forms so the same UI renders regardless of which flow opened it. *@ + +
OPC 40010 Identification
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +@code { + [Parameter, EditorRequired] public Equipment? Equipment { get; set; } +} -- 2.49.1