From 6151a3dfdb1df881a0b2f235dcdb6852e93fe791 Mon Sep 17 00:00:00 2001
From: Alexander van Delft
Date: Fri, 20 Dec 2024 12:33:46 +0100
Subject: [PATCH 01/23] [Add] MultiModelEditor
---
COMET.Web.Common/Utilities/CopyCreator.cs | 96 +++++
.../Utilities/CopyElementDefinitionCreator.cs | 185 ++++++++++
COMET.Web.Common/Utilities/ThingCreator.cs | 334 ++++++++++++++++++
.../Components/OpenModelViewModel.cs | 5 +
COMET.Web.Common/wwwroot/css/styles.css | 11 +-
.../ElementDefinitionTree.razor | 92 +++++
.../ElementDefinitionTree.razor.cs | 256 ++++++++++++++
.../ElementDefinitionTree.razor.css | 5 +
.../MultiModelEditor/MultiModelEditor.razor | 96 +++++
.../MultiModelEditor.razor.cs | 244 +++++++++++++
.../MultiModelEditor.razor.css | 3 +
.../Extensions/ServiceCollectionExtensions.cs | 3 +
COMETwebapp/Model/Applications.cs | 11 +
COMETwebapp/Utilities/WebAppConstantValues.cs | 5 +
.../ElementDefinitionTreeViewModel.cs | 254 +++++++++++++
.../IElementDefinitionTreeViewModel.cs | 71 ++++
.../IMultiModelEditorViewModel.cs | 110 ++++++
.../MultiModelEditorViewModel.cs | 330 +++++++++++++++++
.../Rows/ElementBaseTreeRowViewModel.cs | 93 +++++
.../ElementDefinitionTreeTreeRowViewModel.cs | 103 ++++++
.../Rows/ElementUsageTreeTreeRowViewModel.cs | 60 ++++
21 files changed, 2366 insertions(+), 1 deletion(-)
create mode 100644 COMET.Web.Common/Utilities/CopyCreator.cs
create mode 100644 COMET.Web.Common/Utilities/CopyElementDefinitionCreator.cs
create mode 100644 COMET.Web.Common/Utilities/ThingCreator.cs
create mode 100644 COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor
create mode 100644 COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
create mode 100644 COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.css
create mode 100644 COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
create mode 100644 COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor.cs
create mode 100644 COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor.css
create mode 100644 COMETwebapp/ViewModels/Components/MultiModelEditor/ElementDefinitionTreeViewModel.cs
create mode 100644 COMETwebapp/ViewModels/Components/MultiModelEditor/IElementDefinitionTreeViewModel.cs
create mode 100644 COMETwebapp/ViewModels/Components/MultiModelEditor/IMultiModelEditorViewModel.cs
create mode 100644 COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs
create mode 100644 COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementBaseTreeRowViewModel.cs
create mode 100644 COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementDefinitionTreeTreeRowViewModel.cs
create mode 100644 COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementUsageTreeTreeRowViewModel.cs
diff --git a/COMET.Web.Common/Utilities/CopyCreator.cs b/COMET.Web.Common/Utilities/CopyCreator.cs
new file mode 100644
index 00000000..bba23e52
--- /dev/null
+++ b/COMET.Web.Common/Utilities/CopyCreator.cs
@@ -0,0 +1,96 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2023-2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine
+//
+// This file is part of CDP4-COMET WEB Community Edition
+// The CDP4-COMET WEB Community Edition is the Starion Web Application implementation of ECSS-E-TM-10-25
+// Annex A and Annex C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMET.Web.Common.Utilities
+{
+ using System.Linq;
+ using System.Threading.Tasks;
+
+ using CDP4Common.CommonData;
+ using CDP4Common.EngineeringModelData;
+
+ using CDP4Dal;
+ using CDP4Dal.Operations;
+ using CDP4Dal.Permission;
+
+ ///
+ /// The class responsible for copy operations
+ ///
+ public class CopyCreator
+ {
+ ///
+ /// The in which the copy is performed
+ ///
+ private readonly ISession session;
+
+ ///
+ /// Initializes a new instance of the class
+ ///
+ /// The associated
+ public CopyCreator(ISession session)
+ {
+ this.session = session;
+ }
+
+ ///
+ /// Perform the copy operation of an
+ ///
+ /// The to copy
+ /// The target container
+ public async Task Copy(ElementDefinition elementDefinition, Iteration targetIteration)
+ {
+ // copy the payload to this iteration
+ var copyOperationHelper = new CopyPermissionHelper(this.session, false);
+ var copyPermissionResult = await copyOperationHelper.ComputeCopyPermissionAsync(elementDefinition, targetIteration);
+
+ if (copyPermissionResult.ErrorList.Any())
+ {
+ await this.WriteCopyOperation(elementDefinition, targetIteration, OperationKind.CopyKeepValuesChangeOwner);
+ }
+ else if (copyPermissionResult.CopyableThings.Any())
+ {
+ await this.WriteCopyOperation(elementDefinition, targetIteration, OperationKind.CopyKeepValuesChangeOwner);
+ }
+ }
+
+ ///
+ /// Create and write the copy operation
+ ///
+ /// The to copy
+ /// The target container
+ /// The
+ private async Task WriteCopyOperation(Thing thingToCopy, Thing targetContainer, OperationKind operationKind)
+ {
+ var clone = thingToCopy.Clone(false);
+ var containerClone = targetContainer.Clone(false);
+
+ var transactionContext = TransactionContextResolver.ResolveContext(targetContainer);
+ var transaction = new ThingTransaction(transactionContext, containerClone);
+ transaction.Copy(clone, containerClone, operationKind);
+
+ await this.session.Write(transaction.FinalizeTransaction());
+ }
+ }
+}
\ No newline at end of file
diff --git a/COMET.Web.Common/Utilities/CopyElementDefinitionCreator.cs b/COMET.Web.Common/Utilities/CopyElementDefinitionCreator.cs
new file mode 100644
index 00000000..a30fd89d
--- /dev/null
+++ b/COMET.Web.Common/Utilities/CopyElementDefinitionCreator.cs
@@ -0,0 +1,185 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2023-2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine
+//
+// This file is part of CDP4-COMET WEB Community Edition
+// The CDP4-COMET WEB Community Edition is the Starion Web Application implementation of ECSS-E-TM-10-25
+// Annex A and Annex C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMET.Web.Common.Utilities
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Threading.Tasks;
+
+ using CDP4Common.EngineeringModelData;
+
+ using CDP4Dal;
+ using CDP4Dal.Operations;
+
+ ///
+ /// The class responsible for copying Element Definition
+ ///
+ public class CopyElementDefinitionCreator
+ {
+ ///
+ /// The string added to the element definition name
+ ///
+ private const string CopyAffix = " - Copy";
+
+ ///
+ /// The in which the copy is performed
+ ///
+ private readonly ISession session;
+
+ ///
+ /// The original-clone map
+ ///
+ private readonly Dictionary groupMap = new Dictionary();
+
+ ///
+ /// The original-clone map
+ ///
+ private readonly Dictionary valueSetMap = new Dictionary();
+
+ ///
+ /// Initializes a new instance of the class
+ ///
+ /// The associated
+ public CopyElementDefinitionCreator(ISession session)
+ {
+ this.session = session;
+ }
+
+ ///
+ /// Perform the copy operation of an
+ ///
+ /// The to copy
+ /// Do we need to copy the ElementUsages also?
+ public async Task Copy(ElementDefinition elementDefinition, bool areUsagesCopied)
+ {
+ var iterationClone = (Iteration)elementDefinition.Container.Clone(false);
+ var transactionContext = TransactionContextResolver.ResolveContext(iterationClone);
+ var transaction = new ThingTransaction(transactionContext, iterationClone);
+
+ var clone = elementDefinition.Clone(true);
+ clone.Iid = Guid.NewGuid();
+ clone.Name += CopyAffix;
+
+ if (!areUsagesCopied)
+ {
+ clone.ContainedElement.Clear();
+ }
+
+ this.ResolveReferences(elementDefinition, clone);
+ iterationClone.Element.Add(clone);
+
+ transaction.CopyDeep(clone);
+ await this.session.Write(transaction.FinalizeTransaction());
+ }
+
+ ///
+ /// Resolve the references of the copy
+ ///
+ /// The original
+ /// The clone
+ private void ResolveReferences(ElementDefinition original, ElementDefinition deepClone)
+ {
+ // Order of the item in a list is should be kept when cloning
+ // register mapping between original and copy
+ for (var i = 0; i < original.ParameterGroup.Count; i++)
+ {
+ this.groupMap.Add(original.ParameterGroup[i], deepClone.ParameterGroup[i]);
+ }
+
+ for (var i = 0; i < deepClone.Parameter.Count; i++)
+ {
+ var originalParameter = original.Parameter[i];
+ var cloneParameter = deepClone.Parameter[i];
+
+ for (var j = 0; j < originalParameter.ValueSet.Count; j++)
+ {
+ this.valueSetMap.Add(originalParameter.ValueSet[j], cloneParameter.ValueSet[j]);
+ }
+ }
+
+ for (var i = 0; i < deepClone.ContainedElement.Count; i++)
+ {
+ var originalUsage = original.ContainedElement[i];
+ var cloneUsage = deepClone.ContainedElement[i];
+
+ for (var j = 0; j < originalUsage.ParameterOverride.Count; j++)
+ {
+ var originalOverride = originalUsage.ParameterOverride[j];
+ var cloneOverride = cloneUsage.ParameterOverride[j];
+
+ for (var k = 0; k < originalOverride.ValueSet.Count; k++)
+ {
+ this.valueSetMap.Add(originalOverride.ValueSet[k], cloneOverride.ValueSet[k]);
+ }
+ }
+ }
+
+ // Resolve references
+ foreach (var group in this.groupMap.Values)
+ {
+ if (group.ContainingGroup != null)
+ {
+ // use the mapped group
+ group.ContainingGroup = this.groupMap[group.ContainingGroup];
+ }
+ }
+
+ // fix the group of the cloned parameters
+ foreach (var parameter in deepClone.Parameter)
+ {
+ if (parameter.Group != null)
+ {
+ parameter.Group = this.groupMap[parameter.Group];
+ }
+
+ foreach (var parameterSubscription in parameter.ParameterSubscription)
+ {
+ foreach (var parameterSubscriptionValueSet in parameterSubscription.ValueSet)
+ {
+ parameterSubscriptionValueSet.SubscribedValueSet =
+ this.valueSetMap[parameterSubscriptionValueSet.SubscribedValueSet];
+ }
+ }
+ }
+
+ // fix the references of the subscription value set
+ foreach (var elementUsage in deepClone.ContainedElement)
+ {
+ foreach (var parameterOverride in elementUsage.ParameterOverride)
+ {
+ foreach (var parameterSubscription in parameterOverride.ParameterSubscription)
+ {
+ foreach (var parameterSubscriptionValueSet in parameterSubscription.ValueSet)
+ {
+ parameterSubscriptionValueSet.SubscribedValueSet =
+ this.valueSetMap[parameterSubscriptionValueSet.SubscribedValueSet];
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/COMET.Web.Common/Utilities/ThingCreator.cs b/COMET.Web.Common/Utilities/ThingCreator.cs
new file mode 100644
index 00000000..95f1f814
--- /dev/null
+++ b/COMET.Web.Common/Utilities/ThingCreator.cs
@@ -0,0 +1,334 @@
+// ---------------// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2023-2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine
+//
+// This file is part of CDP4-COMET WEB Community Edition
+// The CDP4-COMET WEB Community Edition is the Starion Web Application implementation of ECSS-E-TM-10-25
+// Annex A and Annex C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMET.Web.Common.Utilities
+{
+ using System;
+ using System.Linq;
+ using System.Threading.Tasks;
+
+ using CDP4Common.EngineeringModelData;
+ using CDP4Common.SiteDirectoryData;
+
+ using CDP4Dal;
+ using CDP4Dal.Operations;
+
+ ///
+ /// The purpose of the is to encapsulate create logic for different things
+ ///
+ public class ThingCreator
+ {
+ ///
+ /// Create a new
+ ///
+ ///
+ /// The container of the that is to be created.
+ ///
+ ///
+ /// The that the is to be grouped in.
+ ///
+ ///
+ /// The that the new references
+ ///
+ ///
+ /// The that the references in case the is a
+ ///
+ ///
+ /// The that is the owner of the that is to be created.
+ ///
+ ///
+ /// The in which the current is to be added
+ ///
+ public async Task CreateParameter(ElementDefinition elementDefinition, ParameterGroup group, ParameterType parameterType, MeasurementScale measurementScale, DomainOfExpertise owner, ISession session)
+ {
+ if (elementDefinition == null)
+ {
+ throw new ArgumentNullException(nameof(elementDefinition), "The container ElementDefinition may not be null");
+ }
+
+ if (parameterType == null)
+ {
+ throw new ArgumentNullException(nameof(parameterType), "The ParameterType may not be null");
+ }
+
+ if (owner == null)
+ {
+ throw new ArgumentNullException(nameof(owner), "The owner DomainOfExpertise may not be null");
+ }
+
+ if (session == null)
+ {
+ throw new ArgumentNullException(nameof(session), "The session may not be null");
+ }
+
+ var parameter = new Parameter(Guid.NewGuid(), null, null)
+ {
+ Owner = owner,
+ ParameterType = parameterType,
+ Scale = measurementScale,
+ Group = group
+ };
+
+ var clone = elementDefinition.Clone(false);
+ clone.Parameter.Add(parameter);
+
+ var transactionContext = TransactionContextResolver.ResolveContext(elementDefinition);
+ var transaction = new ThingTransaction(transactionContext, clone);
+ transaction.Create(parameter);
+
+ try
+ {
+ var operationContainer = transaction.FinalizeTransaction();
+ await session.Write(operationContainer);
+ }
+ catch (Exception ex)
+ {
+ throw;
+ }
+ }
+
+ ///
+ /// Create a new
+ ///
+ ///
+ /// The container of the that is to be created.
+ ///
+ ///
+ /// The that the new references.
+ ///
+ ///
+ /// The in which the new is to be added
+ ///
+ public async Task CreateUserRuleVerification(RuleVerificationList ruleVerificationList, Rule rule, ISession session)
+ {
+ if (ruleVerificationList == null)
+ {
+ throw new ArgumentNullException(nameof(ruleVerificationList), "The ruleVerificationList must not be null");
+ }
+
+ if (rule == null)
+ {
+ throw new ArgumentNullException(nameof(rule), "The rule must not be null");
+ }
+
+ if (session == null)
+ {
+ throw new ArgumentNullException(nameof(session), "The session may not be null");
+ }
+
+ var userRuleVerification = new UserRuleVerification(Guid.NewGuid(), null, null)
+ {
+ Rule = rule,
+ IsActive = false,
+ Status = RuleVerificationStatusKind.NONE
+ };
+
+ var clone = ruleVerificationList.Clone(false);
+ clone.RuleVerification.Add(userRuleVerification);
+
+ var transactionContext = TransactionContextResolver.ResolveContext(ruleVerificationList);
+ var transaction = new ThingTransaction(transactionContext, clone);
+ transaction.Create(userRuleVerification);
+
+ try
+ {
+ var operationContainer = transaction.FinalizeTransaction();
+ await session.Write(operationContainer);
+ }
+ catch (Exception ex)
+ {
+ throw;
+ }
+ }
+
+ ///
+ /// Create a new
+ ///
+ ///
+ /// The container of the that is to be created.
+ ///
+ ///
+ /// The name for the
+ ///
+ ///
+ /// The in which the new is to be added
+ ///
+ public async Task CreateBuiltInRuleVerification(RuleVerificationList ruleVerificationList, string name, ISession session)
+ {
+ if (ruleVerificationList == null)
+ {
+ throw new ArgumentNullException(nameof(ruleVerificationList), "The ruleVerificationList must not be null");
+ }
+
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentException("The name may not be null or empty");
+ }
+
+ if (session == null)
+ {
+ throw new ArgumentNullException(nameof(session), "The session may not be null");
+ }
+
+ var builtInRuleVerification = new BuiltInRuleVerification(Guid.NewGuid(), null, null)
+ {
+ Name = name,
+ IsActive = false,
+ Status = RuleVerificationStatusKind.NONE
+ };
+
+ var clone = ruleVerificationList.Clone(false);
+ clone.RuleVerification.Add(builtInRuleVerification);
+
+ var transactionContext = TransactionContextResolver.ResolveContext(ruleVerificationList);
+ var transaction = new ThingTransaction(transactionContext, clone);
+ transaction.Create(builtInRuleVerification);
+
+ try
+ {
+ var operationContainer = transaction.FinalizeTransaction();
+ await session.Write(operationContainer);
+ }
+ catch (Exception ex)
+ {
+ throw;
+ }
+ }
+
+ ///
+ /// Create a new
+ ///
+ ///
+ /// The container of the that is to be created.
+ ///
+ ///
+ /// The referenced of the that is to be created.
+ ///
+ ///
+ /// The that is the owner of the that is to be created.
+ ///
+ ///
+ /// The in which the current is to be added
+ ///
+ public async Task CreateElementUsage(ElementDefinition container, ElementDefinition referencedDefinition, DomainOfExpertise owner, ISession session)
+ {
+ if (container == null)
+ {
+ throw new ArgumentNullException(nameof(container), "The container must not be null");
+ }
+
+ if (referencedDefinition == null)
+ {
+ throw new ArgumentNullException(nameof(referencedDefinition), "The referencedDefinition must not be null");
+ }
+
+ if (owner == null)
+ {
+ throw new ArgumentNullException(nameof(owner), "The owner must not be null");
+ }
+
+ if (session == null)
+ {
+ throw new ArgumentNullException(nameof(session), "The session may not be null");
+ }
+
+ var clone = container.Clone(false);
+ var usage = new ElementUsage
+ {
+ Name = referencedDefinition.Name,
+ ShortName = referencedDefinition.ShortName,
+ Owner = owner,
+ ElementDefinition = referencedDefinition
+ };
+
+ clone.ContainedElement.Add(usage);
+
+ var transactionContext = TransactionContextResolver.ResolveContext(container);
+ var transaction = new ThingTransaction(transactionContext, clone);
+ transaction.Create(usage);
+
+ try
+ {
+ var operationContainer = transaction.FinalizeTransaction();
+ await session.Write(operationContainer);
+ }
+ catch (Exception ex)
+ {
+ throw;
+ }
+ }
+
+ ///
+ /// Method for creating a for requirement verification between a and a .
+ ///
+ /// The for which the will be created
+ /// The for which the will be created
+ /// The that acts as the source of the
+ /// The that acts as the target of the
+ /// An awaitable
+ public async Task CreateBinaryRelationshipForRequirementVerification(ISession session, Iteration iteration, ParameterOrOverrideBase parameter, RelationalExpression relationalExpression)
+ {
+ session.OpenIterations.TryGetValue(iteration, out var tuple);
+
+ var binaryRelationship = new BinaryRelationship(Guid.NewGuid(), null, null) { Owner = tuple?.Item1 };
+
+ var transaction = new ThingTransaction(TransactionContextResolver.ResolveContext(relationalExpression));
+
+ binaryRelationship.Container = iteration;
+ binaryRelationship.Source = parameter;
+ binaryRelationship.Target = relationalExpression;
+
+ var iterationClone = iteration.Clone(false);
+ iterationClone.Relationship.Add(binaryRelationship);
+ transaction.CreateOrUpdate(iterationClone);
+ transaction.Create(binaryRelationship);
+
+ try
+ {
+ var operationContainer = transaction.FinalizeTransaction();
+ await session.Write(operationContainer);
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+
+ ///
+ /// Checks if creating a for requirement verification is allowed for these two objects
+ ///
+ /// The
+ /// The
+ /// True if creation is allowed
+ public bool IsCreateBinaryRelationshipForRequirementVerificationAllowed(ParameterOrOverrideBase parameter, RelationalExpression relationalExpression)
+ {
+ return (parameter.ParameterType.Iid == relationalExpression.ParameterType.Iid) &&
+ (!(parameter.ParameterType is QuantityKind) || (parameter.Scale == relationalExpression.Scale)) &&
+ !relationalExpression.QueryRelationships
+ .Any(
+ x => x is BinaryRelationship relationship
+ && (relationship.Source.Iid == parameter.Iid));
+ }
+ }
+}
diff --git a/COMET.Web.Common/ViewModels/Components/OpenModelViewModel.cs b/COMET.Web.Common/ViewModels/Components/OpenModelViewModel.cs
index 170f5ddb..d256e177 100644
--- a/COMET.Web.Common/ViewModels/Components/OpenModelViewModel.cs
+++ b/COMET.Web.Common/ViewModels/Components/OpenModelViewModel.cs
@@ -222,6 +222,11 @@ public virtual async Task> OpenSession()
return Result.Fail(["The selected iteration and the domain of expertise should not be null"]);
}
+ if (this.sessionService.OpenIterations.Items.Any(x => x.IterationSetup.Iid == this.SelectedIterationSetup.IterationSetupId))
+ {
+ return Result.Fail(["The selected iteration is already openened"]);
+ }
+
this.IsOpeningSession = true;
var result = await this.sessionService.ReadIteration(this.SelectedEngineeringModel.IterationSetup
diff --git a/COMET.Web.Common/wwwroot/css/styles.css b/COMET.Web.Common/wwwroot/css/styles.css
index 83c84789..c82e3775 100644
--- a/COMET.Web.Common/wwwroot/css/styles.css
+++ b/COMET.Web.Common/wwwroot/css/styles.css
@@ -606,4 +606,13 @@
left: 5px; /* Position of the icon */
color: #757575; /* Icon color */
font-size: 16px; /* Icon size */
- }
\ No newline at end of file
+ }
+
+.treeview-drag-over {
+ background-color: burlywood;
+}
+
+.treeview-item-drag-over {
+ background-color: burlywood;
+ box-shadow: 0px 0px 2px 10px burlywood;
+}
\ No newline at end of file
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor
new file mode 100644
index 00000000..9327cfdd
--- /dev/null
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor
@@ -0,0 +1,92 @@
+@using COMET.Web.Common.Model
+@using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows
+@using CDP4JsonSerializer.JsonConverter
+@inject IJSRuntime JSRuntime
+
+
+ }
+
+
+
+
+
+
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
new file mode 100644
index 00000000..f700d27f
--- /dev/null
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
@@ -0,0 +1,256 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua
+//
+// This file is part of COMET WEB Community Edition
+// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C.
+//
+// The COMET WEB Community Edition is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Affero General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// The COMET WEB Community Edition is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMETwebapp.Components.MultiModelEditor
+{
+ using CDP4Common.EngineeringModelData;
+
+ using COMETwebapp.ViewModels.Components.MultiModelEditor;
+ using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows;
+
+ using DevExpress.Blazor;
+
+ using Microsoft.AspNetCore.Components;
+
+ ///
+ /// Support class for the component
+ ///
+ public partial class ElementDefinitionTree
+ {
+ ///
+ /// The injected
+ ///
+ [Inject]
+ public IElementDefinitionTreeViewModel ViewModel { get; set; }
+
+ ///
+ /// The Iteration
+ ///
+ [Parameter]
+ public Iteration Iteration { get; set; }
+
+ ///
+ /// Gets or sets a value indicating that another model can be selected for this TreeView or not
+ ///
+ [Parameter]
+ public bool IsModelSelectionEnabled { get; set; }
+
+ ///
+ /// Fires after node selection has been changed for a specific item.
+ ///
+ [Parameter]
+ public EventCallback SelectionChanged { get; set; }
+
+ ///
+ /// Fires after node dragging has been started for a specific item.
+ ///
+ [Parameter]
+ public EventCallback<(ElementDefinitionTree, ElementBaseTreeRowViewModel)> OnDragStart { get; set; } = new();
+
+ ///
+ /// Fires after node dragging has been ended for a specific item.
+ ///
+ [Parameter]
+ public EventCallback<(ElementDefinitionTree, ElementBaseTreeRowViewModel)> OnDragEnd { get; set; } = new();
+
+ ///
+ /// Fires after node drop has been executed on a specific item.
+ ///
+ [Parameter]
+ public EventCallback<(ElementDefinitionTree, ElementBaseTreeRowViewModel)> OnDrop { get; set; } = new();
+
+ ///
+ /// Fires after node drag-over has been started for a specific item.
+ ///
+ [Parameter]
+ public EventCallback<(ElementDefinitionTree, object)> OnDragEnter { get; set; } = new();
+
+ ///
+ /// Fires after node drag-over has been ended for a specific item.
+ ///
+ [Parameter]
+ public EventCallback<(ElementDefinitionTree, object)> OnDragLeave { get; set; } = new();
+
+ ///
+ /// Fires when calculation of is necessary
+ ///
+ [Parameter]
+ public EventCallback OnCalculateDropIsAllowed { get; set; } = new();
+
+ ///
+ /// Is evaluated when calculation if a node is draggable is necessary
+ ///
+ [Parameter]
+ public Func AllowNodeDrag { get; set; } = (x, y) => true;
+
+ ///
+ /// Gets or sets a value indicating that dragging a node is allowed for this
+ ///
+ [Parameter]
+ public bool AllowDrag { get; set; }
+
+ ///
+ /// Gets or sets a value indicating that dropping a node is allowed for this
+ ///
+ [Parameter]
+ public bool AllowDrop { get; set; }
+
+ ///
+ /// Holds a reference to the object where the dragged node is dragged over
+ ///
+ private object dragOverNode;
+
+ ///
+ /// Gets or sets a value indicating that dropping a node is allowed for this
+ ///
+ public bool AllowNodeDrop { get; set; }
+
+ ///
+ /// A collection of
+ ///
+ public IEnumerable Rows { get; set; }
+
+ ///
+ /// Gets or sets the
+ ///
+ public DxTreeView TreeView { get; set; }
+
+ ///
+ /// Method invoked after each time the component has been rendered. Note that the component does
+ /// not automatically re-render after the completion of any returned , because
+ /// that would cause an infinite render loop.
+ ///
+ ///
+ /// Set to true if this is the first time has been invoked
+ /// on this component instance; otherwise false.
+ ///
+ /// A representing any asynchronous operation.
+ ///
+ /// The and lifecycle methods
+ /// are useful for performing interop, or interacting with values received from @ref.
+ /// Use the parameter to ensure that initialization work is only performed
+ /// once.
+ ///
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ if (this.ViewModel.Iteration != this.Iteration)
+ {
+ this.Iteration = this.ViewModel.Iteration;
+ }
+ }
+
+ ///
+ /// Method invoked when the component has received parameters from its parent in
+ /// the render tree, and the incoming values have been assigned to properties.
+ ///
+ protected override void OnParametersSet()
+ {
+ base.OnParametersSet();
+
+ this.ViewModel.Iteration = this.Iteration;
+ }
+
+ ///
+ /// Clears the selected node(s) in this
+ ///
+ public void ClearSelection()
+ {
+ this.TreeView.ClearSelection();
+ }
+
+ ///
+ /// Is executed when dragging of a node has started
+ ///
+ /// The node where dragging has been started for
+ /// an awaitable
+ private async Task DragStart(ElementBaseTreeRowViewModel node)
+ {
+ await this.OnDragStart.InvokeAsync((this, node));
+ await this.OnCalculateDropIsAllowed.InvokeAsync(this);
+ Console.WriteLine("DragStart");
+ }
+
+ ///
+ /// Is executed when dragging of a node has ended
+ ///
+ /// The node where dragging has been ended for
+ /// an awaitable
+ private async Task DragEnd(ElementBaseTreeRowViewModel node)
+ {
+ await this.OnDragEnd.InvokeAsync((this, node));
+ await this.OnCalculateDropIsAllowed.InvokeAsync(this);
+ Console.WriteLine("DragEnd");
+ }
+
+ ///
+ /// Is executed when a node has been dropped onto another node
+ ///
+ /// The node where the dragged node has been dropped onto
+ /// an awaitable
+ private async Task Drop(ElementBaseTreeRowViewModel node)
+ {
+ this.dragOverNode = null;
+
+ if (this.AllowDrop)
+ {
+ await this.OnDrop.InvokeAsync((this, node));
+ await this.OnCalculateDropIsAllowed.InvokeAsync(this);
+ Console.WriteLine("Drop");
+ }
+ }
+
+ ///
+ /// Is executed when a dragged node hover over another droppable node
+ ///
+ /// The node where the dragged node has been hovered over
+ /// an awaitable
+ private async Task DragEnter(object node)
+ {
+ if (this.AllowDrop)
+ {
+ this.dragOverNode = node;
+ await this.OnDragEnter.InvokeAsync((this, node));
+ await this.OnCalculateDropIsAllowed.InvokeAsync(this);
+
+ Console.WriteLine("DragEnter");
+ }
+ }
+
+ ///
+ /// Is executed when a dragged node is not hovered over another droppable node anymore
+ ///
+ /// The node where the dragged node had been hovered over
+ /// an awaitable
+ private async Task DragLeave(object node)
+ {
+ if (this.AllowDrop)
+ {
+ this.dragOverNode = null;
+ await this.OnDragLeave.InvokeAsync((this, node));
+ await this.OnCalculateDropIsAllowed.InvokeAsync(this);
+ Console.WriteLine("DragLeave");
+ }
+ }
+ }
+}
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.css b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.css
new file mode 100644
index 00000000..bf169aed
--- /dev/null
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.css
@@ -0,0 +1,5 @@
+.sticky-scrollable-column {
+ position: sticky;
+ top: 0px;
+ overflow: hidden;
+}
diff --git a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
new file mode 100644
index 00000000..9f6f1d0f
--- /dev/null
+++ b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
@@ -0,0 +1,96 @@
+
+
+@using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows
+@inherits SingleIterationApplicationBase;
+
+
+
+
+
+
+
Source Model
+
+
+
+
+
Target Model
+
+
+
+
+
+
+
+ @if (this.ViewModel.SelectedElementDefinition is not null)
+ {
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor.cs b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor.cs
new file mode 100644
index 00000000..32c4a3c4
--- /dev/null
+++ b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor.cs
@@ -0,0 +1,244 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua
+//
+// This file is part of COMET WEB Community Edition
+// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C.
+//
+// The COMET WEB Community Edition is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Affero General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// The COMET WEB Community Edition is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMETwebapp.Components.MultiModelEditor
+{
+ using CDP4Common.EngineeringModelData;
+
+ using COMET.Web.Common.Components.Applications;
+ using COMET.Web.Common.Extensions;
+
+ using COMETwebapp.ViewModels.Components.ModelEditor.Rows;
+ using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows;
+
+ using DevExpress.Blazor;
+
+ using ReactiveUI;
+
+ ///
+ /// Support class for the component
+ ///
+ public partial class MultiModelEditor
+ {
+ ///
+ /// Holds a reference to the data of the node where another node is dragged over
+ ///
+ private (ElementDefinitionTree, object) DragOverObject;
+
+ ///
+ /// Holds a reference to the data of the node that is currently dragged
+ ///
+ private (ElementDefinitionTree, ElementBaseTreeRowViewModel) DragObject;
+
+ ///
+ /// The validation messages to display
+ ///
+ private string ErrorMessage { get; set; }
+
+ ///
+ /// Gets or sets the source tree component
+ ///
+ public ElementDefinitionTree SourceTree { get; set; }
+
+ ///
+ /// Gets or sets the target tree component
+ ///
+ public ElementDefinitionTree TargetTree { get; set; }
+
+ ///
+ /// Handles the post-assignement flow of the property
+ ///
+ protected override void OnViewModelAssigned()
+ {
+ base.OnViewModelAssigned();
+
+ this.Disposables.Add(this.WhenAnyValue(x => x.ViewModel.IsOnCreationMode).SubscribeAsync(_ => this.InvokeAsync(this.StateHasChanged)));
+ this.Disposables.Add(this.WhenAnyValue(x => x.ViewModel.IsOnAddingParameterMode).SubscribeAsync(_ => this.InvokeAsync(this.StateHasChanged)));
+ }
+
+ ///
+ /// Initializes values of the component and of the ViewModel based on parameters provided from the url
+ ///
+ /// A for parameters
+ protected override void InitializeValues(Dictionary parameters)
+ {
+ }
+
+ ///
+ /// Method executed when a is selected
+ ///
+ /// The
+ private void OnElementSelected(ElementBaseTreeRowViewModel args)
+ {
+ this.ViewModel.SelectElement(args?.ElementBase);
+ }
+
+ ///
+ /// Is executed when dragging has been started for a specific node () in a specific
+ ///
+ /// A that contains the specific and the specific node ()
+ /// an awaitable
+ private Task OnDragStart((ElementDefinitionTree, ElementBaseTreeRowViewModel) nodeData)
+ {
+ this.DragObject = nodeData;
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Is executed when dragging has been ended for a specific node () in a specific
+ ///
+ /// A that contains the specific and the specific node ()
+ /// an awaitable
+ private Task OnDragEnd((ElementDefinitionTree, ElementBaseTreeRowViewModel) nodeData)
+ {
+ this.DragObject = (null, null);
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Is executed when a dragged node () has been dropped onto another element in a specific
+ ///
+ /// A that contains the specific and the specific node ()
+ /// an awaitable
+ private async Task OnDrop((ElementDefinitionTree, ElementBaseTreeRowViewModel) nodeData)
+ {
+ this.ErrorMessage = string.Empty;
+
+ if (this.DragObject.Item2 is not ElementDefinitionTreeTreeRowViewModel elementDefinitionTreeRowViewModel)
+ {
+ return;
+ }
+
+ try
+ {
+ if (nodeData.Item2 == null)
+ {
+ // Drop in the same model
+ await this.ViewModel.CopyAndAddNewElement(elementDefinitionTreeRowViewModel.ElementBase);
+ }
+ else
+ {
+ await this.ViewModel.AddNewElementUsage(elementDefinitionTreeRowViewModel.ElementBase, nodeData.Item2.ElementBase);
+ }
+ }
+ catch (Exception ex)
+ {
+ this.ErrorMessage = ex.Message;
+ }
+ finally
+ {
+ this.DragOverObject = (null, null);
+ this.DragObject = (null, null);
+
+ this.StateHasChanged();
+ }
+ }
+
+ ///
+ /// Is executed when a dragged node () hovers over a specific element () in a specific
+ ///
+ /// A that contains the specific and the specific element ()
+ /// an awaitable
+ private Task OnDragEnter((ElementDefinitionTree, object) elementData)
+ {
+ this.DragOverObject = elementData;
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Is executed when a dragged node () leaves a previously hovered over specific element () in a specific
+ ///
+ /// A that contains the specific and the specific element ()
+ /// an awaitable
+ private Task OnDragLeave((ElementDefinitionTree, object) elementData)
+ {
+ this.DragOverObject = (null, null);
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Sets the AllowNodeDrop property for a specific node in a , based on the calculated data for and
+ ///
+ /// The to calculate this for
+ /// an awaitable
+ private Task SetDropIsAllowed(ElementDefinitionTree elementDefinitionTree)
+ {
+ elementDefinitionTree.AllowNodeDrop = this.CalculateDropIsAllowed(elementDefinitionTree);
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Calculates is dropping of a dragged node () is allowed onto the node where it hovers over ()
+ ///
+ /// The where to calculate for
+ /// A value indicating is dropping is actually allowed
+ private bool CalculateDropIsAllowed(ElementDefinitionTree elementDefinitionTree)
+ {
+ var dragOverObject = this.DragOverObject;
+ var dragObject = this.DragObject;
+
+ if (elementDefinitionTree.AllowDrop)
+ {
+ if (dragObject != (null, null))
+ {
+ if (dragOverObject == dragObject)
+ {
+ return false;
+ }
+
+ if (dragOverObject.Item2 is ElementDefinitionTreeTreeRowViewModel dragOverVm)
+ {
+ if (dragObject.Item2 is ElementDefinitionTreeTreeRowViewModel dragVm)
+ {
+ if (dragOverVm.ElementBase == dragVm.ElementBase)
+ {
+ return false;
+ }
+
+ if (dragOverVm.ElementBase.GetContainerOfType() == dragVm.ElementBase.GetContainerOfType())
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ return false;
+ }
+
+ if (dragOverObject.Item1 == elementDefinitionTree)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ return false;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor.css b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor.css
new file mode 100644
index 00000000..bc45ff21
--- /dev/null
+++ b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor.css
@@ -0,0 +1,3 @@
+.sticky-scrollable-column {
+ max-height: 80vh;
+}
diff --git a/COMETwebapp/Extensions/ServiceCollectionExtensions.cs b/COMETwebapp/Extensions/ServiceCollectionExtensions.cs
index b9127ddd..99cc9f37 100644
--- a/COMETwebapp/Extensions/ServiceCollectionExtensions.cs
+++ b/COMETwebapp/Extensions/ServiceCollectionExtensions.cs
@@ -57,6 +57,7 @@ namespace COMETwebapp.Extensions
using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileHandler;
using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileRevisionHandler;
using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FolderHandler;
+ using COMETwebapp.ViewModels.Components.MultiModelEditor;
using COMETwebapp.ViewModels.Components.ParameterEditor.BatchParameterEditor;
using COMETwebapp.ViewModels.Components.ReferenceData;
using COMETwebapp.ViewModels.Pages;
@@ -104,6 +105,8 @@ public static void RegisterViewModels(this IServiceCollection serviceCollection)
serviceCollection.AddTransient();
serviceCollection.AddTransient();
serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
serviceCollection.AddTransient();
serviceCollection.AddTransient();
serviceCollection.AddTransient();
diff --git a/COMETwebapp/Model/Applications.cs b/COMETwebapp/Model/Applications.cs
index b17029b7..5f1a2aed 100644
--- a/COMETwebapp/Model/Applications.cs
+++ b/COMETwebapp/Model/Applications.cs
@@ -30,6 +30,7 @@ namespace COMETwebapp.Model
using COMETwebapp.Components.EngineeringModel;
using COMETwebapp.Components.ModelDashboard;
using COMETwebapp.Components.ModelEditor;
+ using COMETwebapp.Components.MultiModelEditor;
using COMETwebapp.Components.ParameterEditor;
using COMETwebapp.Components.ReferenceData;
using COMETwebapp.Components.SiteDirectory;
@@ -103,6 +104,16 @@ private static List InitializesApplications()
ComponentType = typeof(ElementDefinitionTable)
},
+ new TabbedApplication
+ {
+ Name = "Multi Model Editor",
+ Color = "#76fd98",
+ IconType = typeof(FeatherBox),
+ Description = "Populate multiple models",
+ Url = WebAppConstantValues.MultiModelEditorPage,
+ ComponentType = typeof(MultiModelEditor)
+ },
+
new TabbedApplication
{
Name = "Parameter Editor",
diff --git a/COMETwebapp/Utilities/WebAppConstantValues.cs b/COMETwebapp/Utilities/WebAppConstantValues.cs
index cf450af4..2f36c412 100644
--- a/COMETwebapp/Utilities/WebAppConstantValues.cs
+++ b/COMETwebapp/Utilities/WebAppConstantValues.cs
@@ -126,6 +126,11 @@ public static class WebAppConstantValues
///
public const string ModelEditorPage = "ModelEditor";
+ ///
+ /// The page name of the Model Editor
+ ///
+ public const string MultiModelEditorPage = "MultiModelEditor";
+
///
/// The page nastringthe Book Editor
///
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/ElementDefinitionTreeViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/ElementDefinitionTreeViewModel.cs
new file mode 100644
index 00000000..ac849a57
--- /dev/null
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/ElementDefinitionTreeViewModel.cs
@@ -0,0 +1,254 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua
+//
+// This file is part of COMET WEB Community Edition
+// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C.
+//
+// The COMET WEB Community Edition is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Affero General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// The COMET WEB Community Edition is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMETwebapp.ViewModels.Components.MultiModelEditor
+{
+ using System.Collections.ObjectModel;
+
+ using CDP4Common.CommonData;
+ using CDP4Common.EngineeringModelData;
+
+ using CDP4Dal;
+ using CDP4Dal.Events;
+
+ using COMET.Web.Common.Model;
+ using COMET.Web.Common.Services.SessionManagement;
+ using COMET.Web.Common.ViewModels.Components.Applications;
+
+ using COMETwebapp.Components.ModelEditor;
+ using COMETwebapp.Components.MultiModelEditor;
+ using COMETwebapp.ViewModels.Components.ModelEditor;
+ using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows;
+
+ using DynamicData;
+
+ using ReactiveUI;
+
+ ///
+ /// ViewModel for the
+ ///
+ public class ElementDefinitionTreeViewModel : ApplicationBaseViewModel, IElementDefinitionTreeViewModel
+ {
+ ///
+ /// The
+ ///
+ private readonly ISessionService sessionService;
+
+ ///
+ /// Backing field for
+ ///
+ private Iteration iteration;
+
+ ///
+ /// Gets the Description of the selected model and iteration
+ ///
+ public string Description => this.selectedIterationData?.IterationName ?? "Please select a model";
+
+ ///
+ /// Backing field for the property
+ ///
+ private IterationData selectedIterationData;
+
+ ///
+ /// Creates a new instance of
+ ///
+ /// the
+ /// The
+ public ElementDefinitionTreeViewModel(ISessionService sessionService, ICDPMessageBus messageBus) : base(sessionService, messageBus)
+ {
+ this.sessionService = sessionService;
+ this.RegisterViewModelWithReusableRows(this);
+ this.Iterations.Add(null);
+
+ this.Disposables.Add(this.WhenAnyValue(x => x.SelectedIterationData).Subscribe(x => this.Iteration = this.sessionService.OpenIterations.Items.SingleOrDefault(y => y.IterationSetup.Iid == x?.IterationSetupId)));
+
+ this.Disposables.Add(this.WhenAnyValue(x => x.Iteration).Subscribe(x =>
+ {
+ this.Rows.Clear();
+
+ if (x != null)
+ {
+ this.AddRows(x.Element);
+ this.SelectedIterationData = this.Iterations.SingleOrDefault(y => y?.IterationSetupId == x.IterationSetup.Iid);
+ }
+ }));
+
+ this.Disposables.Add(this.sessionService.OpenIterations.Connect().Subscribe(this.RefreshIterations));
+
+ this.InitializeSubscriptions([typeof(ElementBase)]);
+ }
+
+ ///
+ /// The from which to build the tree
+ ///
+ public Iteration Iteration
+ {
+ get => this.iteration;
+ set => this.RaiseAndSetIfChanged(ref this.iteration, value);
+ }
+
+ ///
+ /// The from which to build the tree
+ ///
+ public IterationData SelectedIterationData
+ {
+ get => this.selectedIterationData;
+ set => this.RaiseAndSetIfChanged(ref this.selectedIterationData, value);
+ }
+
+ ///
+ /// Gets or a collection of selectable s
+ ///
+ public ObservableCollection Iterations { get; } = new();
+
+ ///
+ /// All of the iteration
+ ///
+ public List Elements { get; set; } = [];
+
+ ///
+ /// Gets the collection of the for target model
+ ///
+ public ObservableCollection Rows { get; set; } = [];
+
+ ///
+ /// Represents the selected ElementDefinitionRowViewModel
+ ///
+ public ElementDefinition SelectedElementDefinition { get; set; }
+
+ ///
+ /// Add rows related to that has been added
+ ///
+ /// A collection of added
+ public void AddRows(IEnumerable addedThings)
+ {
+ var listOfAddedElementBases = addedThings.OfType().Where(x => this.Iteration?.Element.Contains(x) ?? false).ToList();
+ this.Rows.AddRange(listOfAddedElementBases.Select(e => new ElementDefinitionTreeTreeRowViewModel(e)));
+ }
+
+ ///
+ /// Updates rows related to that have been updated
+ ///
+ /// A collection of updated
+ public void UpdateRows(IEnumerable updatedThings)
+ {
+ foreach (var element in updatedThings.OfType().Where(x => this.Iteration?.Element.Contains(x) ?? false).ToList())
+ {
+ var row = this.Rows.FirstOrDefault(x => x.ElementBase.Iid == element.Iid);
+ row?.UpdateProperties(new ElementDefinitionTreeTreeRowViewModel(element));
+ }
+ }
+
+ ///
+ /// Remove rows related to a that has been deleted
+ ///
+ /// A collection of deleted
+ public void RemoveRows(IEnumerable deletedThings)
+ {
+ foreach (var elementId in deletedThings.OfType().Select(x => x.Iid))
+ {
+ var row = this.Rows.FirstOrDefault(x => x.ElementBase.Iid == elementId);
+
+ if (row != null)
+ {
+ this.Rows.Remove(row);
+ }
+ }
+ }
+
+ ///
+ /// Handles the message received
+ ///
+ /// A
+ protected override async Task OnEndUpdate()
+ {
+ await this.OnSessionRefreshed();
+ }
+
+ ///
+ /// Handles the refresh of the current
+ ///
+ /// A
+ protected override async Task OnSessionRefreshed()
+ {
+ if (this.AddedThings.Count == 0 && this.DeletedThings.Count == 0 && this.UpdatedThings.Count == 0)
+ {
+ return;
+ }
+
+ this.IsLoading = true;
+ await Task.Delay(1);
+
+ this.UpdateInnerComponents();
+ this.ClearRecordedChanges();
+ this.IsLoading = false;
+ }
+
+ ///
+ /// Refreshes the property
+ ///
+ /// The containing all the necessary changes
+ private void RefreshIterations(IChangeSet changeSet)
+ {
+ foreach (var change in changeSet)
+ {
+ switch (change.Reason)
+ {
+ case ListChangeReason.AddRange:
+ foreach (var changeItem in change.Range)
+ {
+ var newChangeIterationData = new IterationData(changeItem.IterationSetup, true);
+
+ if (!this.Iterations.Contains(newChangeIterationData))
+ {
+ this.Iterations.Add(newChangeIterationData);
+ }
+ }
+
+ break;
+
+ case ListChangeReason.Add:
+ var newIterationData = new IterationData(change.Item.Current.IterationSetup, true);
+
+ if (!this.Iterations.Contains(newIterationData))
+ {
+ this.Iterations.Add(newIterationData);
+ }
+
+ break;
+
+ case ListChangeReason.Remove:
+ var currentItem = this.Iterations.FirstOrDefault(x => x?.IterationSetupId == change.Item.Current.IterationSetup.Iid);
+
+ if (currentItem != null)
+ {
+ this.Iterations.Remove(currentItem);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/IElementDefinitionTreeViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/IElementDefinitionTreeViewModel.cs
new file mode 100644
index 00000000..dd2fea60
--- /dev/null
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/IElementDefinitionTreeViewModel.cs
@@ -0,0 +1,71 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua
+//
+// This file is part of COMET WEB Community Edition
+// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C.
+//
+// The COMET WEB Community Edition is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Affero General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// The COMET WEB Community Edition is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMETwebapp.ViewModels.Components.MultiModelEditor
+{
+ using System.Collections.ObjectModel;
+
+ using CDP4Common.EngineeringModelData;
+
+ using COMET.Web.Common.Model;
+ using COMET.Web.Common.ViewModels.Components.Applications;
+
+ using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows;
+
+ ///
+ /// Interface for the
+ ///
+ public interface IElementDefinitionTreeViewModel : IHaveReusableRows
+ {
+ ///
+ /// Gets the collection of the
+ ///
+ ObservableCollection Rows { get; set; }
+
+ ///
+ /// The from which to build the tree
+ ///
+ Iteration Iteration { get; set; }
+
+ ///
+ /// Represents the selected ElementDefinitionRowViewModel
+ ///
+ ElementDefinition SelectedElementDefinition { get; set; }
+
+ ///
+ /// Gets the Description of the selected model and iteration
+ ///
+ string Description { get; }
+
+ ///
+ /// Gets or a collection of selectable s
+ ///
+ ObservableCollection Iterations { get; }
+
+ ///
+ /// The from which to build the tree
+ ///
+ IterationData SelectedIterationData { get; set; }
+ }
+}
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/IMultiModelEditorViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/IMultiModelEditorViewModel.cs
new file mode 100644
index 00000000..4fb2ae07
--- /dev/null
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/IMultiModelEditorViewModel.cs
@@ -0,0 +1,110 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua
+//
+// This file is part of COMET WEB Community Edition
+// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C.
+//
+// The COMET WEB Community Edition is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Affero General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// The COMET WEB Community Edition is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMETwebapp.ViewModels.Components.MultiModelEditor
+{
+ using CDP4Common.EngineeringModelData;
+
+ using COMET.Web.Common.ViewModels.Components.Applications;
+
+ using COMETwebapp.Components.ModelEditor;
+ using COMETwebapp.ViewModels.Components.ModelEditor;
+ using COMETwebapp.ViewModels.Components.ModelEditor.AddParameterViewModel;
+ using COMETwebapp.ViewModels.Components.SystemRepresentation;
+
+ ///
+ /// Interface for the
+ ///
+ public interface IMultiModelEditorViewModel : ISingleIterationApplicationBaseViewModel
+ {
+ ///
+ /// Gets the target />
+ ///
+ Iteration TargetIteration { get; }
+
+ ///
+ /// Gets the source
+ ///
+ Iteration SourceIteration { get; }
+
+ ///
+ /// Value indicating the user is currently creating a new
+ ///
+ bool IsOnCreationMode { get; set; }
+
+ ///
+ /// Represents the selected ElementDefinitionRowViewModel
+ ///
+ ElementDefinition SelectedElementDefinition { get; set; }
+
+ ///
+ /// The
+ ///
+ IElementDefinitionDetailsViewModel ElementDefinitionDetailsViewModel { get; }
+
+ ///
+ /// Gets the
+ ///
+ IElementDefinitionCreationViewModel ElementDefinitionCreationViewModel { get; set; }
+
+ ///
+ /// Gets the
+ ///
+ IAddParameterViewModel AddParameterViewModel { get; set; }
+
+ ///
+ /// Value indicating the user is currently adding a new to a
+ ///
+ bool IsOnAddingParameterMode { get; set; }
+
+ ///
+ /// Opens the popup
+ ///
+ void OpenCreateElementDefinitionCreationPopup();
+
+ ///
+ /// Set the selected
+ ///
+ /// The selected
+ void SelectElement(ElementBase selectedElementBase);
+
+ ///
+ /// Opens the popup
+ ///
+ void OpenAddParameterPopup();
+
+ ///
+ /// Add a new based on an existing
+ ///
+ /// The
+ Task CopyAndAddNewElement(ElementBase elementBase);
+
+ ///
+ /// Add a new based on an existing
+ ///
+ /// The to be added as
+ /// The where to add the new to
+ Task AddNewElementUsage(ElementBase fromElementBase, ElementBase toElementBase);
+ }
+}
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs
new file mode 100644
index 00000000..08f58b4f
--- /dev/null
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs
@@ -0,0 +1,330 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua
+//
+// This file is part of COMET WEB Community Edition
+// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C.
+//
+// The COMET WEB Community Edition is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Affero General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// The COMET WEB Community Edition is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMETwebapp.ViewModels.Components.MultiModelEditor
+{
+ using CDP4Common.CommonData;
+ using CDP4Common.EngineeringModelData;
+ using CDP4Common.SiteDirectoryData;
+
+ using CDP4Dal;
+ using CDP4Dal.Events;
+
+ using COMET.Web.Common.Services.SessionManagement;
+ using COMET.Web.Common.Utilities;
+ using COMET.Web.Common.ViewModels.Components.Applications;
+
+ using COMETwebapp.Components.ModelEditor;
+ using COMETwebapp.ViewModels.Components.ModelEditor;
+ using COMETwebapp.ViewModels.Components.ModelEditor.AddParameterViewModel;
+ using COMETwebapp.ViewModels.Components.SystemRepresentation;
+ using COMETwebapp.ViewModels.Components.SystemRepresentation.Rows;
+
+ using Microsoft.AspNetCore.Components;
+
+ using ReactiveUI;
+
+ ///
+ /// ViewModel for the
+ ///
+ public class MultiModelEditorViewModel : SingleIterationApplicationBaseViewModel, IMultiModelEditorViewModel
+ {
+ ///
+ /// The
+ ///
+ private readonly ISessionService sessionService;
+
+ ///
+ /// Backing field for
+ ///
+ private bool isOnAddingParameterMode;
+
+ ///
+ /// Backing field for
+ ///
+ private bool isOnCreationMode;
+
+ ///
+ /// Creates a new instance of
+ ///
+ /// the
+ /// The
+ public MultiModelEditorViewModel(ISessionService sessionService, ICDPMessageBus messageBus) : base(sessionService, messageBus)
+ {
+ this.sessionService = sessionService;
+ var eventCallbackFactory = new EventCallbackFactory();
+
+ this.ElementDefinitionCreationViewModel = new ElementDefinitionCreationViewModel(sessionService, messageBus)
+ {
+ OnValidSubmit = eventCallbackFactory.Create(this, this.AddingElementDefinition)
+ };
+
+ this.AddParameterViewModel = new ModelEditor.AddParameterViewModel.AddParameterViewModel(sessionService, messageBus)
+ {
+ OnParameterAdded = eventCallbackFactory.Create(this, () => this.IsOnAddingParameterMode = false)
+ };
+
+ this.InitializeSubscriptions([typeof(ElementBase)]);
+
+ this.TargetIteration = this.CurrentThing;
+ }
+
+ ///
+ /// Represents the selected ElementDefinitionRowViewModel
+ ///
+ public ElementDefinition SelectedElementDefinition { get; set; }
+
+ ///
+ /// The
+ ///
+ public IElementDefinitionDetailsViewModel ElementDefinitionDetailsViewModel { get; } = new ElementDefinitionDetailsViewModel();
+
+ ///
+ /// Gets the
+ ///
+ public IElementDefinitionCreationViewModel ElementDefinitionCreationViewModel { get; set; }
+
+ ///
+ /// Gets the
+ ///
+ public IAddParameterViewModel AddParameterViewModel { get; set; }
+
+ ///
+ /// Gets target
+ ///
+ public Iteration TargetIteration { get; set; }
+
+ ///
+ /// Gets source
+ ///
+ public Iteration SourceIteration { get; set; }
+
+ ///
+ /// Value indicating the user is currently creating a new
+ ///
+ public bool IsOnCreationMode
+ {
+ get => this.isOnCreationMode;
+ set => this.RaiseAndSetIfChanged(ref this.isOnCreationMode, value);
+ }
+
+ ///
+ /// Value indicating the user is currently adding a new to a
+ ///
+ public bool IsOnAddingParameterMode
+ {
+ get => this.isOnAddingParameterMode;
+ set => this.RaiseAndSetIfChanged(ref this.isOnAddingParameterMode, value);
+ }
+
+ ///
+ /// Set the selected
+ ///
+ /// The selected
+ public void SelectElement(ElementBase selectedElementBase)
+ {
+ // It is preferable to have a selection based on the Iid of the Thing
+ this.ElementDefinitionDetailsViewModel.SelectedSystemNode = selectedElementBase;
+
+ this.SelectedElementDefinition = selectedElementBase switch
+ {
+ ElementDefinition definition => definition,
+ ElementUsage usage => usage.ElementDefinition,
+ _ => null
+ };
+
+ this.ElementDefinitionDetailsViewModel.Rows = this.SelectedElementDefinition?.Parameter.Select(x => new ElementDefinitionDetailsRowViewModel(x)).ToList();
+ this.AddParameterViewModel.SetSelectedElementDefinition(this.SelectedElementDefinition);
+ }
+
+ ///
+ /// Opens the popup
+ ///
+ public void OpenCreateElementDefinitionCreationPopup()
+ {
+ this.ElementDefinitionCreationViewModel.ElementDefinition = new ElementDefinition();
+ this.ElementDefinitionCreationViewModel.SelectedCategories = new List();
+ this.IsOnCreationMode = true;
+ }
+
+ ///
+ /// Opens the popup
+ ///
+ public void OpenAddParameterPopup()
+ {
+ this.AddParameterViewModel.ResetValues();
+ this.IsOnAddingParameterMode = true;
+ }
+
+ ///
+ /// Add a new based on an existing
+ ///
+ /// The
+ public async Task CopyAndAddNewElement(ElementBase elementBase)
+ {
+ this.IsLoading = true;
+
+ if (elementBase.GetContainerOfType() == this.CurrentThing)
+ {
+ var copyCreator = new CopyElementDefinitionCreator(this.sessionService.Session);
+
+ try
+ {
+ await copyCreator.Copy((ElementDefinition)elementBase, true);
+ }
+ catch (Exception exception)
+ {
+ Console.WriteLine(exception.Message);
+ throw;
+ }
+ finally
+ {
+ this.IsLoading = false;
+ }
+ }
+ else
+ {
+ var copyCreator = new CopyCreator(this.sessionService.Session);
+
+ try
+ {
+ await copyCreator.Copy((ElementDefinition)elementBase, this.CurrentThing);
+ }
+ catch (Exception exception)
+ {
+ Console.WriteLine(exception.Message);
+ throw;
+ }
+ finally
+ {
+ this.IsLoading = false;
+ }
+ }
+ }
+
+ ///
+ /// Add a new based on an existing
+ ///
+ /// The to be added as
+ /// The where to add the new to
+ public async Task AddNewElementUsage(ElementBase fromElementBase, ElementBase toElementBase)
+ {
+ if (fromElementBase.GetContainerOfType() == this.CurrentThing && toElementBase.GetContainerOfType() == this.CurrentThing)
+ {
+ this.IsLoading = true;
+
+ var thingCreator = new ThingCreator();
+
+ try
+ {
+ await thingCreator.CreateElementUsage((ElementDefinition)toElementBase, (ElementDefinition)fromElementBase, this.sessionService.Session.OpenIterations.First(x => x.Key == this.CurrentThing).Value.Item1, this.sessionService.Session);
+ }
+ catch (Exception exception)
+ {
+ Console.WriteLine(exception.Message);
+ throw;
+ }
+ finally
+ {
+ this.IsLoading = false;
+ }
+ }
+ }
+
+ ///
+ /// Tries to create a new
+ ///
+ /// A
+ public async Task AddingElementDefinition()
+ {
+ var thingsToCreate = new List();
+
+ if (this.ElementDefinitionCreationViewModel.SelectedCategories.Any())
+ {
+ this.ElementDefinitionCreationViewModel.ElementDefinition.Category = this.ElementDefinitionCreationViewModel.SelectedCategories.ToList();
+ }
+
+ this.ElementDefinitionCreationViewModel.ElementDefinition.Container = this.CurrentThing;
+ thingsToCreate.Add(this.ElementDefinitionCreationViewModel.ElementDefinition);
+ var clonedIteration = this.CurrentThing.Clone(false);
+
+ if (this.ElementDefinitionCreationViewModel.IsTopElement)
+ {
+ clonedIteration.TopElement = this.ElementDefinitionCreationViewModel.ElementDefinition;
+ }
+
+ clonedIteration.Element.Add(this.ElementDefinitionCreationViewModel.ElementDefinition);
+ thingsToCreate.Add(clonedIteration);
+
+ try
+ {
+ await this.sessionService.CreateOrUpdateThings(clonedIteration, thingsToCreate);
+ this.IsOnCreationMode = false;
+ }
+ catch (Exception exception)
+ {
+ Console.WriteLine(exception.Message);
+ throw;
+ }
+ }
+
+ ///
+ /// Handles the message received
+ ///
+ /// A
+ protected override async Task OnEndUpdate()
+ {
+ await this.OnSessionRefreshed();
+ }
+
+ ///
+ /// Handles the refresh of the current
+ ///
+ /// A
+ protected override Task OnSessionRefreshed()
+ {
+ this.SelectElement(this.SelectedElementDefinition);
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Update this view model properties
+ ///
+ /// A
+ protected override async Task OnThingChanged()
+ {
+ await base.OnThingChanged();
+
+ if (this.CurrentThing == null)
+ {
+ return;
+ }
+
+ this.AddParameterViewModel.InitializeViewModel(this.CurrentThing);
+ this.ElementDefinitionCreationViewModel.InitializeViewModel(this.CurrentThing);
+
+ this.IsLoading = false;
+ }
+ }
+}
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementBaseTreeRowViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementBaseTreeRowViewModel.cs
new file mode 100644
index 00000000..97ff46b8
--- /dev/null
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementBaseTreeRowViewModel.cs
@@ -0,0 +1,93 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua
+//
+// This file is part of COMET WEB Community Edition
+// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C.
+//
+// The COMET WEB Community Edition is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Affero General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// The COMET WEB Community Edition is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMETwebapp.ViewModels.Components.MultiModelEditor.Rows
+{
+ using CDP4Common.EngineeringModelData;
+
+ using COMETwebapp.ViewModels.Components.ModelEditor.Rows;
+
+ using ReactiveUI;
+
+ ///
+ /// Row View Model for
+ ///
+ public abstract class ElementBaseTreeRowViewModel : ReactiveObject
+ {
+ ///
+ /// Backing field for
+ ///
+ private ElementBase elementBase;
+
+ ///
+ /// Backing field for
+ ///
+ private string elementName;
+
+ ///
+ /// Initializes a new instance of the class.
+ /// the
+ ///
+ protected ElementBaseTreeRowViewModel(ElementBase elementBase)
+ {
+ this.ElementBase = elementBase;
+ this.ElementName = elementBase.Name;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected ElementBaseTreeRowViewModel()
+ {
+ }
+
+ ///
+ /// The name of the
+ ///
+ public string ElementName
+ {
+ get => this.elementName;
+ set => this.RaiseAndSetIfChanged(ref this.elementName, value);
+ }
+
+ ///
+ /// The
+ ///
+ public ElementBase ElementBase
+ {
+ get => this.elementBase;
+ set => this.RaiseAndSetIfChanged(ref this.elementBase, value);
+ }
+
+ ///
+ /// Update this row view model properties
+ ///
+ /// The to use for updating
+ public void UpdateProperties(ElementBaseTreeRowViewModel elementBaseTreeRow)
+ {
+ this.ElementBase = elementBaseTreeRow.elementBase;
+ this.ElementName = elementBaseTreeRow.elementName;
+ }
+ }
+}
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementDefinitionTreeTreeRowViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementDefinitionTreeTreeRowViewModel.cs
new file mode 100644
index 00000000..6d0d62bf
--- /dev/null
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementDefinitionTreeTreeRowViewModel.cs
@@ -0,0 +1,103 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua
+//
+// This file is part of COMET WEB Community Edition
+// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C.
+//
+// The COMET WEB Community Edition is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Affero General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// The COMET WEB Community Edition is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMETwebapp.ViewModels.Components.MultiModelEditor.Rows
+{
+ using System.Collections.ObjectModel;
+
+ using CDP4Common.EngineeringModelData;
+
+ using COMETwebapp.ViewModels.Components.ModelEditor.Rows;
+
+ using DynamicData;
+
+ using ReactiveUI;
+
+ ///
+ /// Row View Model for
+ ///
+ public class ElementDefinitionTreeTreeRowViewModel : ElementBaseTreeRowViewModel
+ {
+ ///
+ /// Backing field for
+ ///
+ private bool isTopElement;
+
+ ///
+ /// Gets or the collection of
+ ///
+ public ObservableCollection Rows { get; } = new();
+
+ ///
+ /// Initializes a new instance of the class.
+ /// the
+ ///
+ public ElementDefinitionTreeTreeRowViewModel(ElementDefinition elementBase) : base(elementBase)
+ {
+ this.IsTopElement = elementBase == elementBase.GetContainerOfType().TopElement;
+
+ var elementUsages = elementBase?.ContainedElement;
+
+ if (elementUsages?.Any() ?? false)
+ {
+ this.Rows.AddRange(elementUsages.Select(x => new ElementUsageTreeTreeRowViewModel(x)));
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ElementDefinitionTreeTreeRowViewModel()
+ {
+ }
+
+ ///
+ /// The value to check if the element base is the top element
+ ///
+ public bool IsTopElement
+ {
+ get => this.isTopElement;
+ set => this.RaiseAndSetIfChanged(ref this.isTopElement, value);
+ }
+
+ ///
+ /// Update this row view model properties
+ ///
+ /// The to use for updating
+ public void UpdateProperties(ElementDefinitionTreeTreeRowViewModel elementDefinitionTreeRow)
+ {
+ base.UpdateProperties(elementDefinitionTreeRow);
+ this.IsTopElement = elementDefinitionTreeRow.isTopElement;
+
+ this.Rows.Clear();
+
+ var elementUsages = (elementDefinitionTreeRow.ElementBase as ElementDefinition)?.ContainedElement;
+
+ if (elementUsages != null)
+ {
+ this.Rows.AddRange(elementUsages.Select(x => new ElementUsageTreeTreeRowViewModel(x)));
+ }
+ }
+ }
+}
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementUsageTreeTreeRowViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementUsageTreeTreeRowViewModel.cs
new file mode 100644
index 00000000..bb489957
--- /dev/null
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementUsageTreeTreeRowViewModel.cs
@@ -0,0 +1,60 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua
+//
+// This file is part of COMET WEB Community Edition
+// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C.
+//
+// The COMET WEB Community Edition is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Affero General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// The COMET WEB Community Edition is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMETwebapp.ViewModels.Components.MultiModelEditor.Rows
+{
+ using CDP4Common.EngineeringModelData;
+
+ using COMETwebapp.ViewModels.Components.ModelEditor.Rows;
+
+ ///
+ /// Row View Model for
+ ///
+ public class ElementUsageTreeTreeRowViewModel : ElementBaseTreeRowViewModel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ /// the
+ ///
+ public ElementUsageTreeTreeRowViewModel(ElementUsage elementUsage) : base(elementUsage)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ElementUsageTreeTreeRowViewModel()
+ {
+ }
+
+ ///
+ /// Update this row view model properties
+ ///
+ /// The to use for updating
+ public void UpdateProperties(ElementUsageTreeTreeRowViewModel elementUsageTreeRow)
+ {
+ base.UpdateProperties(elementUsageTreeRow);
+ }
+ }
+}
From 6b2f7f1300877daefeb52a558ce16a830feb4e0f Mon Sep 17 00:00:00 2001
From: Alexander van Delft
Date: Fri, 20 Dec 2024 14:56:53 +0100
Subject: [PATCH 02/23] Bugfixes, make copy two-way and show pills
---
.../ElementDefinitionTree.razor | 32 ++++++++++----
.../MultiModelEditor/MultiModelEditor.razor | 42 ++++++++++---------
.../MultiModelEditor.razor.cs | 6 +--
.../Rows/ElementBaseTreeRowViewModel.cs | 21 +++++++++-
4 files changed, 68 insertions(+), 33 deletions(-)
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor
index 9327cfdd..9e7765e9 100644
--- a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor
@@ -1,8 +1,4 @@
-@using COMET.Web.Common.Model
-@using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows
-@using CDP4JsonSerializer.JsonConverter
-@inject IJSRuntime JSRuntime
-
+@using COMET.Web.Common.Model
+@using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows
+@using CDP4JsonSerializer.JsonConverter
+@inject IJSRuntime JSRuntime
+
}
diff --git a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
index 9f6f1d0f..e42d29d9 100644
--- a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
+++ b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
@@ -20,6 +20,7 @@
// along with this program. If not, see http://www.gnu.org/licenses/.
------------------------------------------------------------------------------->
+@using COMETwebapp.Components.ModelEditor
@using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows
@inherits SingleIterationApplicationBase;
@@ -31,35 +32,36 @@
Source Model
+ IsModelSelectionEnabled="true">
Target Model
+ @ref="this.TargetTree"
+ Iteration="@this.ViewModel.CurrentThing"
+ SelectionChanged="model => { if (model != null) this.SourceTree.ClearSelection(); this.OnElementSelected(model);}"
+ AllowDrag="true"
+ AllowDrop="true"
+ OnCalculateDropIsAllowed="@(async x => await this.SetDropIsAllowed(x))"
+ OnDragEnter="@(async x => await this.OnDragEnter(x))"
+ OnDragLeave="@(async x => await this.OnDragLeave(x))"
+ OnDragStart="@(async x => await this.OnDragStart(x))"
+ OnDragEnd="@(async x => await this.OnDragEnd(x))"
+ OnDrop="@(async x => await this.OnDrop(x))"
+ AllowNodeDrag="(tree, model) => model is ElementDefinitionTreeTreeRowViewModel"
+ IsModelSelectionEnabled="false">
diff --git a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor.cs b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor.cs
index 32c4a3c4..b81926a7 100644
--- a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor.cs
+++ b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor.cs
@@ -88,10 +88,10 @@ protected override void InitializeValues(Dictionary parameters)
///
/// Method executed when a is selected
///
- /// The
- private void OnElementSelected(ElementBaseTreeRowViewModel args)
+ /// The
+ private void OnElementSelected(ElementBaseTreeRowViewModel elementRowViewModel)
{
- this.ViewModel.SelectElement(args?.ElementBase);
+ this.ViewModel.SelectElement(elementRowViewModel?.ElementBase);
}
///
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementBaseTreeRowViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementBaseTreeRowViewModel.cs
index 97ff46b8..56f7ab3d 100644
--- a/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementBaseTreeRowViewModel.cs
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementBaseTreeRowViewModel.cs
@@ -25,6 +25,7 @@
namespace COMETwebapp.ViewModels.Components.MultiModelEditor.Rows
{
using CDP4Common.EngineeringModelData;
+ using CDP4Common.SiteDirectoryData;
using COMETwebapp.ViewModels.Components.ModelEditor.Rows;
@@ -45,14 +46,20 @@ public abstract class ElementBaseTreeRowViewModel : ReactiveObject
///
private string elementName;
+ ///
+ /// Backing field for
+ ///
+ private string ownerShortName;
+
///
/// Initializes a new instance of the class.
- /// the
+ /// the
///
protected ElementBaseTreeRowViewModel(ElementBase elementBase)
{
this.ElementBase = elementBase;
this.ElementName = elementBase.Name;
+ this.OwnerShortName = elementBase.Owner.ShortName;
}
///
@@ -62,6 +69,15 @@ protected ElementBaseTreeRowViewModel()
{
}
+ ///
+ /// The shortname of the owning
+ ///
+ public string OwnerShortName
+ {
+ get => this.ownerShortName;
+ set => this.RaiseAndSetIfChanged(ref this.ownerShortName, value);
+ }
+
///
/// The name of the
///
@@ -72,7 +88,7 @@ public string ElementName
}
///
- /// The
+ /// The
///
public ElementBase ElementBase
{
@@ -88,6 +104,7 @@ public void UpdateProperties(ElementBaseTreeRowViewModel elementBaseTreeRow)
{
this.ElementBase = elementBaseTreeRow.elementBase;
this.ElementName = elementBaseTreeRow.elementName;
+ this.OwnerShortName = elementBaseTreeRow.OwnerShortName;
}
}
}
From a649e9e7a933bc0606a2f926372eea486e91365f Mon Sep 17 00:00:00 2001
From: Alexander van Delft
Date: Fri, 20 Dec 2024 14:59:09 +0100
Subject: [PATCH 03/23] Review Comments
---
COMET.Web.Common/Utilities/CopyCreator.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/COMET.Web.Common/Utilities/CopyCreator.cs b/COMET.Web.Common/Utilities/CopyCreator.cs
index bba23e52..839d632d 100644
--- a/COMET.Web.Common/Utilities/CopyCreator.cs
+++ b/COMET.Web.Common/Utilities/CopyCreator.cs
@@ -93,4 +93,4 @@ private async Task WriteCopyOperation(Thing thingToCopy, Thing targetContainer,
await this.session.Write(transaction.FinalizeTransaction());
}
}
-}
\ No newline at end of file
+}
From bd46ec259479db0b59db5f7c72092757b29c140c Mon Sep 17 00:00:00 2001
From: Alexander van Delft
Date: Fri, 20 Dec 2024 16:53:34 +0100
Subject: [PATCH 04/23] [FIX] - Copy problem for two-way drag-drop source <=>
target
---
.../ElementDefinitionTree.razor.cs | 8 ++++----
.../MultiModelEditor/MultiModelEditor.razor | 4 ++--
.../MultiModelEditor/MultiModelEditor.razor.cs | 2 +-
.../MultiModelEditor/IMultiModelEditorViewModel.cs | 6 ++++--
.../MultiModelEditor/MultiModelEditorViewModel.cs | 14 ++++++++------
5 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
index f700d27f..8d38a07f 100644
--- a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
@@ -48,7 +48,7 @@ public partial class ElementDefinitionTree
/// The Iteration
///
[Parameter]
- public Iteration Iteration { get; set; }
+ public Iteration InitialIteration { get; set; }
///
/// Gets or sets a value indicating that another model can be selected for this TreeView or not
@@ -154,9 +154,9 @@ public partial class ElementDefinitionTree
///
protected override async Task OnAfterRenderAsync(bool firstRender)
{
- if (this.ViewModel.Iteration != this.Iteration)
+ if (this.ViewModel.Iteration != this.InitialIteration)
{
- this.Iteration = this.ViewModel.Iteration;
+ this.InitialIteration = this.ViewModel.Iteration;
}
}
@@ -168,7 +168,7 @@ protected override void OnParametersSet()
{
base.OnParametersSet();
- this.ViewModel.Iteration = this.Iteration;
+ this.ViewModel.Iteration ??= this.InitialIteration;
}
///
diff --git a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
index e42d29d9..ad61947a 100644
--- a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
+++ b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
@@ -32,7 +32,7 @@
Source Model
Target Model
/// Add a new based on an existing
///
- /// The
- Task CopyAndAddNewElement(ElementBase elementBase);
+ /// The to copy the node to
+ /// The to copy
+ Task CopyAndAddNewElement(ElementDefinitionTree elementDefinitionTree, ElementBase elementBase);
///
/// Add a new based on an existing
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs
index 08f58b4f..9d05f059 100644
--- a/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs
@@ -36,6 +36,7 @@ namespace COMETwebapp.ViewModels.Components.MultiModelEditor
using COMET.Web.Common.ViewModels.Components.Applications;
using COMETwebapp.Components.ModelEditor;
+ using COMETwebapp.Components.MultiModelEditor;
using COMETwebapp.ViewModels.Components.ModelEditor;
using COMETwebapp.ViewModels.Components.ModelEditor.AddParameterViewModel;
using COMETwebapp.ViewModels.Components.SystemRepresentation;
@@ -180,12 +181,13 @@ public void OpenAddParameterPopup()
///
/// Add a new based on an existing
///
- /// The
- public async Task CopyAndAddNewElement(ElementBase elementBase)
+ /// The to copy the node to
+ /// The to copy
+ public async Task CopyAndAddNewElement(ElementDefinitionTree elementDefinitionTree, ElementBase elementBase)
{
this.IsLoading = true;
- if (elementBase.GetContainerOfType() == this.CurrentThing)
+ if (elementBase.GetContainerOfType() == elementDefinitionTree.ViewModel.Iteration)
{
var copyCreator = new CopyElementDefinitionCreator(this.sessionService.Session);
@@ -209,7 +211,7 @@ public async Task CopyAndAddNewElement(ElementBase elementBase)
try
{
- await copyCreator.Copy((ElementDefinition)elementBase, this.CurrentThing);
+ await copyCreator.Copy((ElementDefinition)elementBase, elementDefinitionTree.ViewModel.Iteration);
}
catch (Exception exception)
{
@@ -230,7 +232,7 @@ public async Task CopyAndAddNewElement(ElementBase elementBase)
/// The where to add the new to
public async Task AddNewElementUsage(ElementBase fromElementBase, ElementBase toElementBase)
{
- if (fromElementBase.GetContainerOfType() == this.CurrentThing && toElementBase.GetContainerOfType() == this.CurrentThing)
+ if (fromElementBase.GetContainerOfType() == toElementBase.GetContainerOfType())
{
this.IsLoading = true;
@@ -238,7 +240,7 @@ public async Task AddNewElementUsage(ElementBase fromElementBase, ElementBase to
try
{
- await thingCreator.CreateElementUsage((ElementDefinition)toElementBase, (ElementDefinition)fromElementBase, this.sessionService.Session.OpenIterations.First(x => x.Key == this.CurrentThing).Value.Item1, this.sessionService.Session);
+ await thingCreator.CreateElementUsage((ElementDefinition)toElementBase, (ElementDefinition)fromElementBase, this.sessionService.Session.OpenIterations.First(x => x.Key == toElementBase.GetContainerOfType()).Value.Item1, this.sessionService.Session);
}
catch (Exception exception)
{
From 8c79b35a92fd1b3c5c005187f4ee0e1cdf7a2836 Mon Sep 17 00:00:00 2001
From: Alexander van Delft
Date: Mon, 6 Jan 2025 13:32:02 +0100
Subject: [PATCH 05/23] Review comments and fix unit tests
---
.../Utilities/CopyElementDefinitionCreator.cs | 2 +-
.../Model/ApplicationsTestFixture.cs | 2 +-
.../Common/OpenTabViewModelTestFixture.cs | 44 +++++++++++++++----
.../ElementDefinitionTree.razor.cs | 16 ++++---
COMETwebapp/Utilities/WebAppConstantValues.cs | 2 +-
.../Common/OpenTab/OpenTabViewModel.cs | 2 +-
.../MultiModelEditorViewModel.cs | 17 ++++---
.../Rows/ElementBaseTreeRowViewModel.cs | 2 +-
8 files changed, 63 insertions(+), 24 deletions(-)
diff --git a/COMET.Web.Common/Utilities/CopyElementDefinitionCreator.cs b/COMET.Web.Common/Utilities/CopyElementDefinitionCreator.cs
index a30fd89d..f8b01040 100644
--- a/COMET.Web.Common/Utilities/CopyElementDefinitionCreator.cs
+++ b/COMET.Web.Common/Utilities/CopyElementDefinitionCreator.cs
@@ -182,4 +182,4 @@ private void ResolveReferences(ElementDefinition original, ElementDefinition dee
}
}
}
-}
\ No newline at end of file
+}
diff --git a/COMETwebapp.Tests/Model/ApplicationsTestFixture.cs b/COMETwebapp.Tests/Model/ApplicationsTestFixture.cs
index ec6a201f..7bea67cc 100644
--- a/COMETwebapp.Tests/Model/ApplicationsTestFixture.cs
+++ b/COMETwebapp.Tests/Model/ApplicationsTestFixture.cs
@@ -36,7 +36,7 @@ public void VerifyApplications()
{
var applications = Applications.ExistingApplications;
- Assert.That(applications, Has.Count.EqualTo(13));
+ Assert.That(applications, Has.Count.EqualTo(14));
foreach (var application in applications)
{
diff --git a/COMETwebapp.Tests/ViewModels/Components/Common/OpenTabViewModelTestFixture.cs b/COMETwebapp.Tests/ViewModels/Components/Common/OpenTabViewModelTestFixture.cs
index 27974a0c..7e00f4ea 100644
--- a/COMETwebapp.Tests/ViewModels/Components/Common/OpenTabViewModelTestFixture.cs
+++ b/COMETwebapp.Tests/ViewModels/Components/Common/OpenTabViewModelTestFixture.cs
@@ -53,6 +53,7 @@ public class OpenTabViewModelTestFixture
private Mock sessionService;
private Mock configurationService;
private Mock tabsViewModel;
+ private SourceList alreadyOpenIterations;
[SetUp]
public void Setup()
@@ -62,24 +63,25 @@ public void Setup()
this.cacheService = new Mock();
this.tabsViewModel = new Mock();
- var id = Guid.NewGuid();
+ var alreadyOpenIterationSetup = new IterationSetup { Iid = Guid.NewGuid() };
- var iterationToAdd = new Iteration
+ var alreadyOpenIteration = new Iteration
{
Container = new EngineeringModel
{
EngineeringModelSetup = new EngineeringModelSetup
{
- IterationSetup = { new IterationSetup { Iid = id } }
+ IterationSetup = { alreadyOpenIterationSetup }
}
},
- Iid = id
+ Iid = Guid.NewGuid(),
+ IterationSetup = alreadyOpenIterationSetup
};
- var openIterations = new SourceList();
- openIterations.Add(iterationToAdd);
+ this.alreadyOpenIterations = new SourceList();
+ this.alreadyOpenIterations.Add(alreadyOpenIteration);
- this.sessionService.Setup(x => x.OpenIterations).Returns(openIterations);
+ this.sessionService.Setup(x => x.OpenIterations).Returns(this.alreadyOpenIterations);
this.sessionService.Setup(x => x.OpenEngineeringModels).Returns([]);
this.sessionService.Setup(x => x.ReadEngineeringModels(It.IsAny>())).Returns(Task.FromResult(new Result()));
this.sessionService.Setup(x => x.ReadIteration(It.IsAny(), It.IsAny())).Returns(Task.FromResult(new Result()));
@@ -105,11 +107,35 @@ public async Task VerifyOpenIterationAndModel()
this.sessionService.Verify(x => x.ReadIteration(It.IsAny(), It.IsAny()), Times.Never);
});
+ var toBeOpenedIterationSetup = new IterationSetup { Iid = Guid.NewGuid() };
+
+ var toBeOpenedIteration = new Iteration
+ {
+ Container = new EngineeringModel
+ {
+ EngineeringModelSetup = new EngineeringModelSetup
+ {
+ IterationSetup = { toBeOpenedIterationSetup }
+ }
+ },
+ Iid = Guid.NewGuid(),
+ IterationSetup = toBeOpenedIterationSetup
+ };
+
var engineeringModelBodyApplication = Applications.ExistingApplications.OfType().First(x => x.Url == WebAppConstantValues.EngineeringModelPage);
this.viewModel.SelectedApplication = engineeringModelBodyApplication;
- this.viewModel.SelectedEngineeringModel = ((EngineeringModel)this.sessionService.Object.OpenIterations.Items.First().Container).EngineeringModelSetup;
- this.viewModel.SelectedIterationSetup = new IterationData(this.viewModel.SelectedEngineeringModel.IterationSetup[0]);
+ this.viewModel.SelectedEngineeringModel = ((EngineeringModel)toBeOpenedIteration.Container).EngineeringModelSetup;
+ this.viewModel.SelectedIterationSetup = new IterationData(toBeOpenedIterationSetup);
this.viewModel.SelectedDomainOfExpertise = new DomainOfExpertise();
+
+ //Make sure that toBeOpenedIteration is added to sessionService.OpenIterations when new Iteration is open
+ this.sessionService.Setup(x => x.ReadIteration(toBeOpenedIterationSetup, It.IsAny()))
+ .Returns(Task.FromResult(new Result()))
+ .Callback(() =>
+ {
+ this.alreadyOpenIterations.Add(toBeOpenedIteration);
+ });
+
await this.viewModel.OpenTab(panel);
Assert.Multiple(() =>
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
index 8d38a07f..4fafebb6 100644
--- a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
@@ -44,6 +44,12 @@ public partial class ElementDefinitionTree
[Inject]
public IElementDefinitionTreeViewModel ViewModel { get; set; }
+ ///
+ /// Gets or sets the injected
+ ///
+ [Inject]
+ public ILogger Logger { get; set; }
+
///
/// The Iteration
///
@@ -188,7 +194,7 @@ private async Task DragStart(ElementBaseTreeRowViewModel node)
{
await this.OnDragStart.InvokeAsync((this, node));
await this.OnCalculateDropIsAllowed.InvokeAsync(this);
- Console.WriteLine("DragStart");
+ this.Logger.LogDebug("DragStart");
}
///
@@ -200,7 +206,7 @@ private async Task DragEnd(ElementBaseTreeRowViewModel node)
{
await this.OnDragEnd.InvokeAsync((this, node));
await this.OnCalculateDropIsAllowed.InvokeAsync(this);
- Console.WriteLine("DragEnd");
+ this.Logger.LogDebug("DragEnd");
}
///
@@ -216,7 +222,7 @@ private async Task Drop(ElementBaseTreeRowViewModel node)
{
await this.OnDrop.InvokeAsync((this, node));
await this.OnCalculateDropIsAllowed.InvokeAsync(this);
- Console.WriteLine("Drop");
+ this.Logger.LogDebug("Drop");
}
}
@@ -233,7 +239,7 @@ private async Task DragEnter(object node)
await this.OnDragEnter.InvokeAsync((this, node));
await this.OnCalculateDropIsAllowed.InvokeAsync(this);
- Console.WriteLine("DragEnter");
+ this.Logger.LogDebug("DragEnter");
}
}
@@ -249,7 +255,7 @@ private async Task DragLeave(object node)
this.dragOverNode = null;
await this.OnDragLeave.InvokeAsync((this, node));
await this.OnCalculateDropIsAllowed.InvokeAsync(this);
- Console.WriteLine("DragLeave");
+ this.Logger.LogDebug("DragLeave");
}
}
}
diff --git a/COMETwebapp/Utilities/WebAppConstantValues.cs b/COMETwebapp/Utilities/WebAppConstantValues.cs
index 2f36c412..d9307bd0 100644
--- a/COMETwebapp/Utilities/WebAppConstantValues.cs
+++ b/COMETwebapp/Utilities/WebAppConstantValues.cs
@@ -77,7 +77,7 @@ public static class WebAppConstantValues
public const string ModelDashboardPage = "ModelDashboard";
///
- /// The page name of the Engieering Model data
+ /// The page name of the Engineering Model data
///
public const string EngineeringModelPage = "EngineeringModel";
diff --git a/COMETwebapp/ViewModels/Components/Common/OpenTab/OpenTabViewModel.cs b/COMETwebapp/ViewModels/Components/Common/OpenTab/OpenTabViewModel.cs
index 236f0916..32882ac6 100644
--- a/COMETwebapp/ViewModels/Components/Common/OpenTab/OpenTabViewModel.cs
+++ b/COMETwebapp/ViewModels/Components/Common/OpenTab/OpenTabViewModel.cs
@@ -66,7 +66,7 @@ public class OpenTabViewModel : OpenModelViewModel, IOpenTabViewModel
/// The
/// The
/// The
- ///
+ /// The
public OpenTabViewModel(ISessionService sessionService, IConfigurationService configurationService, ITabsViewModel tabsViewModel, ICacheService cacheService) : base(sessionService, configurationService, cacheService)
{
this.sessionService = sessionService;
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs
index 9d05f059..3ccd732b 100644
--- a/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs
@@ -56,6 +56,11 @@ public class MultiModelEditorViewModel : SingleIterationApplicationBaseViewModel
///
private readonly ISessionService sessionService;
+ ///
+ /// The injected
+ ///
+ private readonly ILogger logger;
+
///
/// Backing field for
///
@@ -71,9 +76,11 @@ public class MultiModelEditorViewModel : SingleIterationApplicationBaseViewModel
///
/// the
/// The
- public MultiModelEditorViewModel(ISessionService sessionService, ICDPMessageBus messageBus) : base(sessionService, messageBus)
+ /// The injected
+ public MultiModelEditorViewModel(ISessionService sessionService, ICDPMessageBus messageBus, ILogger logger) : base(sessionService, messageBus)
{
this.sessionService = sessionService;
+ this.logger = logger;
var eventCallbackFactory = new EventCallbackFactory();
this.ElementDefinitionCreationViewModel = new ElementDefinitionCreationViewModel(sessionService, messageBus)
@@ -197,7 +204,7 @@ public async Task CopyAndAddNewElement(ElementDefinitionTree elementDefinitionTr
}
catch (Exception exception)
{
- Console.WriteLine(exception.Message);
+ this.logger.LogError(exception, string.Empty);
throw;
}
finally
@@ -215,7 +222,7 @@ public async Task CopyAndAddNewElement(ElementDefinitionTree elementDefinitionTr
}
catch (Exception exception)
{
- Console.WriteLine(exception.Message);
+ this.logger.LogError(exception, string.Empty);
throw;
}
finally
@@ -244,7 +251,7 @@ public async Task AddNewElementUsage(ElementBase fromElementBase, ElementBase to
}
catch (Exception exception)
{
- Console.WriteLine(exception.Message);
+ this.logger.LogError(exception, string.Empty);
throw;
}
finally
@@ -286,7 +293,7 @@ public async Task AddingElementDefinition()
}
catch (Exception exception)
{
- Console.WriteLine(exception.Message);
+ this.logger.LogError(exception, string.Empty);
throw;
}
}
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementBaseTreeRowViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementBaseTreeRowViewModel.cs
index 56f7ab3d..1bbfba65 100644
--- a/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementBaseTreeRowViewModel.cs
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/Rows/ElementBaseTreeRowViewModel.cs
@@ -73,7 +73,7 @@ protected ElementBaseTreeRowViewModel()
/// The shortname of the owning
///
public string OwnerShortName
- {
+ {
get => this.ownerShortName;
set => this.RaiseAndSetIfChanged(ref this.ownerShortName, value);
}
From a0a11706b0c25a4ab81db56f350a6c74fe84b78d Mon Sep 17 00:00:00 2001
From: Alexander van Delft
Date: Mon, 6 Jan 2025 14:49:37 +0100
Subject: [PATCH 06/23] Add SonarLint settings
---
.sonarlint/COMETwebapp.json | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 .sonarlint/COMETwebapp.json
diff --git a/.sonarlint/COMETwebapp.json b/.sonarlint/COMETwebapp.json
new file mode 100644
index 00000000..6951b41d
--- /dev/null
+++ b/.sonarlint/COMETwebapp.json
@@ -0,0 +1,4 @@
+{
+ "SonarCloudOrganization": "stariongroup",
+ "ProjectKey": "STARIONGROUP_COMET-WEB-Community-Edition"
+}
\ No newline at end of file
From 14990c001973948602ad2aaf64138d0af6bf8048 Mon Sep 17 00:00:00 2001
From: Alexander van Delft
Date: Mon, 6 Jan 2025 16:48:52 +0100
Subject: [PATCH 07/23] SonarQube fixes
---
.../Components/CardView/CardView.razor | 2 +-
.../Components/CardView/CardView.razor.cs | 2 +-
COMET.Web.Common/Utilities/CopyCreator.cs | 12 +-
.../Utilities/CopyElementDefinitionCreator.cs | 170 +++++++++---
COMET.Web.Common/Utilities/ThingCreator.cs | 262 ++----------------
.../Common/OpenTabViewModelTestFixture.cs | 3 +-
.../ElementDefinitionTree.razor | 16 +-
.../ElementDefinitionTree.razor.cs | 12 +-
.../MultiModelEditor/MultiModelEditor.razor | 14 +-
.../MultiModelEditor.razor.cs | 2 +-
.../ElementDefinitionCreationViewModel.cs | 2 +
.../IMultiModelEditorViewModel.cs | 2 +-
.../MultiModelEditorViewModel.cs | 96 +++----
13 files changed, 224 insertions(+), 371 deletions(-)
diff --git a/COMET.Web.Common/Components/CardView/CardView.razor b/COMET.Web.Common/Components/CardView/CardView.razor
index 63e8ad35..6c63e8a2 100644
--- a/COMET.Web.Common/Components/CardView/CardView.razor
+++ b/COMET.Web.Common/Components/CardView/CardView.razor
@@ -41,7 +41,7 @@
-
+
/// The request to perform filtering of the items list
/// an waitable
- private ValueTask> LoadItems(ItemsProviderRequest request)
+ private ValueTask> LoadItemsAsync(ItemsProviderRequest request)
{
// Filter items based on the SearchTerm
var filteredItems = !this.AllowSearch || string.IsNullOrWhiteSpace(this.SearchTerm)
diff --git a/COMET.Web.Common/Utilities/CopyCreator.cs b/COMET.Web.Common/Utilities/CopyCreator.cs
index 839d632d..36701615 100644
--- a/COMET.Web.Common/Utilities/CopyCreator.cs
+++ b/COMET.Web.Common/Utilities/CopyCreator.cs
@@ -59,19 +59,15 @@ public CopyCreator(ISession session)
///
/// The to copy
/// The target container
- public async Task Copy(ElementDefinition elementDefinition, Iteration targetIteration)
+ public async Task CopyAsync(ElementDefinition elementDefinition, Iteration targetIteration)
{
// copy the payload to this iteration
var copyOperationHelper = new CopyPermissionHelper(this.session, false);
var copyPermissionResult = await copyOperationHelper.ComputeCopyPermissionAsync(elementDefinition, targetIteration);
- if (copyPermissionResult.ErrorList.Any())
+ if (copyPermissionResult.ErrorList.Any() || copyPermissionResult.CopyableThings.Any())
{
- await this.WriteCopyOperation(elementDefinition, targetIteration, OperationKind.CopyKeepValuesChangeOwner);
- }
- else if (copyPermissionResult.CopyableThings.Any())
- {
- await this.WriteCopyOperation(elementDefinition, targetIteration, OperationKind.CopyKeepValuesChangeOwner);
+ await this.WriteCopyOperationAsync(elementDefinition, targetIteration, OperationKind.CopyKeepValuesChangeOwner);
}
}
@@ -81,7 +77,7 @@ public async Task Copy(ElementDefinition elementDefinition, Iteration targetIter
/// The to copy
/// The target container
/// The
- private async Task WriteCopyOperation(Thing thingToCopy, Thing targetContainer, OperationKind operationKind)
+ private async Task WriteCopyOperationAsync(Thing thingToCopy, Thing targetContainer, OperationKind operationKind)
{
var clone = thingToCopy.Clone(false);
var containerClone = targetContainer.Clone(false);
diff --git a/COMET.Web.Common/Utilities/CopyElementDefinitionCreator.cs b/COMET.Web.Common/Utilities/CopyElementDefinitionCreator.cs
index f8b01040..47edce39 100644
--- a/COMET.Web.Common/Utilities/CopyElementDefinitionCreator.cs
+++ b/COMET.Web.Common/Utilities/CopyElementDefinitionCreator.cs
@@ -73,7 +73,19 @@ public CopyElementDefinitionCreator(ISession session)
///
/// The to copy
/// Do we need to copy the ElementUsages also?
- public async Task Copy(ElementDefinition elementDefinition, bool areUsagesCopied)
+ public Task CopyAsync(ElementDefinition elementDefinition, bool areUsagesCopied)
+ {
+ ArgumentNullException.ThrowIfNull(elementDefinition);
+
+ return this.CopyImplAsync(elementDefinition, areUsagesCopied);
+ }
+
+ ///
+ /// Perform the copy operation of an
+ ///
+ /// The to copy
+ /// Do we need to copy the ElementUsages also?
+ private async Task CopyImplAsync(ElementDefinition elementDefinition, bool areUsagesCopied)
{
var iterationClone = (Iteration)elementDefinition.Container.Clone(false);
var transactionContext = TransactionContextResolver.ResolveContext(iterationClone);
@@ -99,27 +111,107 @@ public async Task Copy(ElementDefinition elementDefinition, bool areUsagesCopied
/// Resolve the references of the copy
///
/// The original
- /// The clone
+ /// The clone
private void ResolveReferences(ElementDefinition original, ElementDefinition deepClone)
{
+ this.groupMap.Clear();
+ this.valueSetMap.Clear();
+
// Order of the item in a list is should be kept when cloning
- // register mapping between original and copy
- for (var i = 0; i < original.ParameterGroup.Count; i++)
+ this.RegisterParameterGroupMap(original, deepClone);
+ this.RegisterParameterValueSets(original, deepClone);
+ this.RegisterParameterOverrideValueSets(original, deepClone);
+
+ // Resolve references
+ this.ResolveGroupMappings();
+ this.ResolveParameterMappings(deepClone);
+ this.ResolveParameterSubscriptionValueSets(deepClone);
+ this.ResolveContainedElementSubscriptions(deepClone);
+ }
+
+ ///
+ /// Resolve s for ContainedElements of the cloned
+ ///
+ /// The deeply cloned
+ private void ResolveContainedElementSubscriptions(ElementDefinition deepClone)
+ {
+ // fix the references of the subscription value set
+ foreach (var elementUsage in deepClone.ContainedElement)
{
- this.groupMap.Add(original.ParameterGroup[i], deepClone.ParameterGroup[i]);
+ foreach (var parameterOverride in elementUsage.ParameterOverride)
+ {
+ this.TryResolveParameterSubscriptionValueSets(parameterOverride);
+ }
}
+ }
- for (var i = 0; i < deepClone.Parameter.Count; i++)
+ ///
+ /// Resolve the already registered mappings for the original s' s
+ ///
+ /// The deeply cloned
+ private void ResolveParameterMappings(ElementDefinition deepClone)
+ {
+ // fix the group of the cloned parameters
+ foreach (var parameter in deepClone.Parameter)
{
- var originalParameter = original.Parameter[i];
- var cloneParameter = deepClone.Parameter[i];
+ if (parameter.Group != null)
+ {
+ parameter.Group = this.groupMap[parameter.Group];
+ }
+ }
+ }
- for (var j = 0; j < originalParameter.ValueSet.Count; j++)
+ ///
+ /// Resolve already registered mappings for the cloned
+ ///
+ /// The deeply cloned
+ private void ResolveParameterSubscriptionValueSets(ElementDefinition deepClone)
+ {
+ // fix the group of the cloned parameters
+ foreach (var parameter in deepClone.Parameter)
+ {
+ this.TryResolveParameterSubscriptionValueSets(parameter);
+ }
+ }
+
+ ///
+ /// Resolve already registered mappings for a specific
+ ///
+ /// The used to search for s
+ private void TryResolveParameterSubscriptionValueSets(ParameterOrOverrideBase parameterOrOverride)
+ {
+ foreach (var parameterSubscription in parameterOrOverride.ParameterSubscription)
+ {
+ foreach (var parameterSubscriptionValueSet in parameterSubscription.ValueSet)
{
- this.valueSetMap.Add(originalParameter.ValueSet[j], cloneParameter.ValueSet[j]);
+ parameterSubscriptionValueSet.SubscribedValueSet =
+ this.valueSetMap[parameterSubscriptionValueSet.SubscribedValueSet];
}
}
+ }
+ ///
+ /// Resolve the already registered mappings
+ ///
+ private void ResolveGroupMappings()
+ {
+ foreach (var group in this.groupMap.Values)
+ {
+ if (group.ContainingGroup != null)
+ {
+ // use the mapped group
+ group.ContainingGroup = this.groupMap[group.ContainingGroup];
+ }
+ }
+ }
+
+ ///
+ /// Register mapping between original and copy s' contained s
+ ///
+ /// The source
+ /// The deeply cloned
+ private void RegisterParameterOverrideValueSets(ElementDefinition original, ElementDefinition deepClone)
+ {
for (var i = 0; i < deepClone.ContainedElement.Count; i++)
{
var originalUsage = original.ContainedElement[i];
@@ -136,49 +228,37 @@ private void ResolveReferences(ElementDefinition original, ElementDefinition dee
}
}
}
+ }
- // Resolve references
- foreach (var group in this.groupMap.Values)
- {
- if (group.ContainingGroup != null)
- {
- // use the mapped group
- group.ContainingGroup = this.groupMap[group.ContainingGroup];
- }
- }
-
- // fix the group of the cloned parameters
- foreach (var parameter in deepClone.Parameter)
+ ///
+ /// Register mapping between original and copy s
+ ///
+ /// The source
+ /// The deeply cloned
+ private void RegisterParameterValueSets(ElementDefinition original, ElementDefinition deepClone)
+ {
+ for (var i = 0; i < deepClone.Parameter.Count; i++)
{
- if (parameter.Group != null)
- {
- parameter.Group = this.groupMap[parameter.Group];
- }
+ var originalParameter = original.Parameter[i];
+ var cloneParameter = deepClone.Parameter[i];
- foreach (var parameterSubscription in parameter.ParameterSubscription)
+ for (var j = 0; j < originalParameter.ValueSet.Count; j++)
{
- foreach (var parameterSubscriptionValueSet in parameterSubscription.ValueSet)
- {
- parameterSubscriptionValueSet.SubscribedValueSet =
- this.valueSetMap[parameterSubscriptionValueSet.SubscribedValueSet];
- }
+ this.valueSetMap.Add(originalParameter.ValueSet[j], cloneParameter.ValueSet[j]);
}
}
+ }
- // fix the references of the subscription value set
- foreach (var elementUsage in deepClone.ContainedElement)
+ ///
+ /// Register mapping between original and copy s
+ ///
+ /// The source
+ /// The deeply cloned
+ private void RegisterParameterGroupMap(ElementDefinition original, ElementDefinition deepClone)
+ {
+ for (var i = 0; i < original.ParameterGroup.Count; i++)
{
- foreach (var parameterOverride in elementUsage.ParameterOverride)
- {
- foreach (var parameterSubscription in parameterOverride.ParameterSubscription)
- {
- foreach (var parameterSubscriptionValueSet in parameterSubscription.ValueSet)
- {
- parameterSubscriptionValueSet.SubscribedValueSet =
- this.valueSetMap[parameterSubscriptionValueSet.SubscribedValueSet];
- }
- }
- }
+ this.groupMap.Add(original.ParameterGroup[i], deepClone.ParameterGroup[i]);
}
}
}
diff --git a/COMET.Web.Common/Utilities/ThingCreator.cs b/COMET.Web.Common/Utilities/ThingCreator.cs
index 95f1f814..9a7aa29f 100644
--- a/COMET.Web.Common/Utilities/ThingCreator.cs
+++ b/COMET.Web.Common/Utilities/ThingCreator.cs
@@ -26,7 +26,6 @@
namespace COMET.Web.Common.Utilities
{
using System;
- using System.Linq;
using System.Threading.Tasks;
using CDP4Common.EngineeringModelData;
@@ -41,180 +40,28 @@ namespace COMET.Web.Common.Utilities
public class ThingCreator
{
///
- /// Create a new
+ /// Create a new
///
- ///
- /// The container of the that is to be created.
- ///
- ///
- /// The that the is to be grouped in.
- ///
- ///
- /// The that the new references
+ ///
+ /// The container of the that is to be created.
///
- ///
- /// The that the references in case the is a
+ ///
+ /// The referenced of the that is to be created.
///
///
- /// The that is the owner of the that is to be created.
+ /// The that is the owner of the that is to be created.
///
///
/// The in which the current is to be added
///
- public async Task CreateParameter(ElementDefinition elementDefinition, ParameterGroup group, ParameterType parameterType, MeasurementScale measurementScale, DomainOfExpertise owner, ISession session)
- {
- if (elementDefinition == null)
- {
- throw new ArgumentNullException(nameof(elementDefinition), "The container ElementDefinition may not be null");
- }
-
- if (parameterType == null)
- {
- throw new ArgumentNullException(nameof(parameterType), "The ParameterType may not be null");
- }
-
- if (owner == null)
- {
- throw new ArgumentNullException(nameof(owner), "The owner DomainOfExpertise may not be null");
- }
-
- if (session == null)
- {
- throw new ArgumentNullException(nameof(session), "The session may not be null");
- }
-
- var parameter = new Parameter(Guid.NewGuid(), null, null)
- {
- Owner = owner,
- ParameterType = parameterType,
- Scale = measurementScale,
- Group = group
- };
-
- var clone = elementDefinition.Clone(false);
- clone.Parameter.Add(parameter);
-
- var transactionContext = TransactionContextResolver.ResolveContext(elementDefinition);
- var transaction = new ThingTransaction(transactionContext, clone);
- transaction.Create(parameter);
-
- try
- {
- var operationContainer = transaction.FinalizeTransaction();
- await session.Write(operationContainer);
- }
- catch (Exception ex)
- {
- throw;
- }
- }
-
- ///
- /// Create a new
- ///
- ///
- /// The container of the that is to be created.
- ///
- ///
- /// The that the new references.
- ///
- ///
- /// The in which the new is to be added
- ///
- public async Task CreateUserRuleVerification(RuleVerificationList ruleVerificationList, Rule rule, ISession session)
- {
- if (ruleVerificationList == null)
- {
- throw new ArgumentNullException(nameof(ruleVerificationList), "The ruleVerificationList must not be null");
- }
-
- if (rule == null)
- {
- throw new ArgumentNullException(nameof(rule), "The rule must not be null");
- }
-
- if (session == null)
- {
- throw new ArgumentNullException(nameof(session), "The session may not be null");
- }
-
- var userRuleVerification = new UserRuleVerification(Guid.NewGuid(), null, null)
- {
- Rule = rule,
- IsActive = false,
- Status = RuleVerificationStatusKind.NONE
- };
-
- var clone = ruleVerificationList.Clone(false);
- clone.RuleVerification.Add(userRuleVerification);
-
- var transactionContext = TransactionContextResolver.ResolveContext(ruleVerificationList);
- var transaction = new ThingTransaction(transactionContext, clone);
- transaction.Create(userRuleVerification);
-
- try
- {
- var operationContainer = transaction.FinalizeTransaction();
- await session.Write(operationContainer);
- }
- catch (Exception ex)
- {
- throw;
- }
- }
-
- ///
- /// Create a new
- ///
- ///
- /// The container of the that is to be created.
- ///
- ///
- /// The name for the
- ///
- ///
- /// The in which the new is to be added
- ///
- public async Task CreateBuiltInRuleVerification(RuleVerificationList ruleVerificationList, string name, ISession session)
+ public Task CreateElementUsageAsync(ElementDefinition container, ElementDefinition referencedDefinition, DomainOfExpertise owner, ISession session)
{
- if (ruleVerificationList == null)
- {
- throw new ArgumentNullException(nameof(ruleVerificationList), "The ruleVerificationList must not be null");
- }
-
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentException("The name may not be null or empty");
- }
-
- if (session == null)
- {
- throw new ArgumentNullException(nameof(session), "The session may not be null");
- }
+ ArgumentNullException.ThrowIfNull(container);
+ ArgumentNullException.ThrowIfNull(referencedDefinition);
+ ArgumentNullException.ThrowIfNull(owner);
+ ArgumentNullException.ThrowIfNull(session);
- var builtInRuleVerification = new BuiltInRuleVerification(Guid.NewGuid(), null, null)
- {
- Name = name,
- IsActive = false,
- Status = RuleVerificationStatusKind.NONE
- };
-
- var clone = ruleVerificationList.Clone(false);
- clone.RuleVerification.Add(builtInRuleVerification);
-
- var transactionContext = TransactionContextResolver.ResolveContext(ruleVerificationList);
- var transaction = new ThingTransaction(transactionContext, clone);
- transaction.Create(builtInRuleVerification);
-
- try
- {
- var operationContainer = transaction.FinalizeTransaction();
- await session.Write(operationContainer);
- }
- catch (Exception ex)
- {
- throw;
- }
+ return this.CreateElementUsageImplAsync(container, referencedDefinition, owner, session);
}
///
@@ -232,29 +79,10 @@ public async Task CreateBuiltInRuleVerification(RuleVerificationList ruleVerific
///
/// The in which the current is to be added
///
- public async Task CreateElementUsage(ElementDefinition container, ElementDefinition referencedDefinition, DomainOfExpertise owner, ISession session)
+ private async Task CreateElementUsageImplAsync(ElementDefinition container, ElementDefinition referencedDefinition, DomainOfExpertise owner, ISession session)
{
- if (container == null)
- {
- throw new ArgumentNullException(nameof(container), "The container must not be null");
- }
-
- if (referencedDefinition == null)
- {
- throw new ArgumentNullException(nameof(referencedDefinition), "The referencedDefinition must not be null");
- }
-
- if (owner == null)
- {
- throw new ArgumentNullException(nameof(owner), "The owner must not be null");
- }
-
- if (session == null)
- {
- throw new ArgumentNullException(nameof(session), "The session may not be null");
- }
-
var clone = container.Clone(false);
+
var usage = new ElementUsage
{
Name = referencedDefinition.Name,
@@ -269,66 +97,8 @@ public async Task CreateElementUsage(ElementDefinition container, ElementDefinit
var transaction = new ThingTransaction(transactionContext, clone);
transaction.Create(usage);
- try
- {
- var operationContainer = transaction.FinalizeTransaction();
- await session.Write(operationContainer);
- }
- catch (Exception ex)
- {
- throw;
- }
- }
-
- ///
- /// Method for creating a for requirement verification between a and a .
- ///
- /// The for which the will be created
- /// The for which the will be created
- /// The that acts as the source of the
- /// The that acts as the target of the
- /// An awaitable
- public async Task CreateBinaryRelationshipForRequirementVerification(ISession session, Iteration iteration, ParameterOrOverrideBase parameter, RelationalExpression relationalExpression)
- {
- session.OpenIterations.TryGetValue(iteration, out var tuple);
-
- var binaryRelationship = new BinaryRelationship(Guid.NewGuid(), null, null) { Owner = tuple?.Item1 };
-
- var transaction = new ThingTransaction(TransactionContextResolver.ResolveContext(relationalExpression));
-
- binaryRelationship.Container = iteration;
- binaryRelationship.Source = parameter;
- binaryRelationship.Target = relationalExpression;
-
- var iterationClone = iteration.Clone(false);
- iterationClone.Relationship.Add(binaryRelationship);
- transaction.CreateOrUpdate(iterationClone);
- transaction.Create(binaryRelationship);
-
- try
- {
- var operationContainer = transaction.FinalizeTransaction();
- await session.Write(operationContainer);
- }
- catch (Exception ex)
- {
- }
- }
-
- ///
- /// Checks if creating a for requirement verification is allowed for these two objects
- ///
- /// The
- /// The
- /// True if creation is allowed
- public bool IsCreateBinaryRelationshipForRequirementVerificationAllowed(ParameterOrOverrideBase parameter, RelationalExpression relationalExpression)
- {
- return (parameter.ParameterType.Iid == relationalExpression.ParameterType.Iid) &&
- (!(parameter.ParameterType is QuantityKind) || (parameter.Scale == relationalExpression.Scale)) &&
- !relationalExpression.QueryRelationships
- .Any(
- x => x is BinaryRelationship relationship
- && (relationship.Source.Iid == parameter.Iid));
+ var operationContainer = transaction.FinalizeTransaction();
+ await session.Write(operationContainer);
}
}
}
diff --git a/COMETwebapp.Tests/ViewModels/Components/Common/OpenTabViewModelTestFixture.cs b/COMETwebapp.Tests/ViewModels/Components/Common/OpenTabViewModelTestFixture.cs
index 7e00f4ea..2b3c71d6 100644
--- a/COMETwebapp.Tests/ViewModels/Components/Common/OpenTabViewModelTestFixture.cs
+++ b/COMETwebapp.Tests/ViewModels/Components/Common/OpenTabViewModelTestFixture.cs
@@ -92,7 +92,8 @@ public void Setup()
[TearDown]
public void Teardown()
{
- this.viewModel.Dispose();
+ this.viewModel?.Dispose();
+ this.alreadyOpenIterations?.Dispose();
}
[Test]
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor
index 9e7765e9..f3798286 100644
--- a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor
@@ -44,9 +44,9 @@ else
this.DragEnter(this.TreeView))"
- @ondragleave="@(() => this.DragLeave(this.TreeView))"
- @ondrop="@(() => this.Drop(null))"
+ @ondragenter="@(() => this.DragEnterAsync(this.TreeView))"
+ @ondragleave="@(() => this.DragLeaveAsync(this.TreeView))"
+ @ondrop="@(() => this.DropAsync(null))"
style="height:25px;width:inherit;color:cadetblue;@(this.AllowDrop ? "" : "visibility:hidden;")">
Drop here to create new element...
@@ -69,12 +69,12 @@ else
await this.DragStart(dataItem))"
- @ondragend="@(async () => await this.DragEnd(dataItem))"
+ @ondragstart="@(async () => await this.DragStartAsync(dataItem))"
+ @ondragend="@(async () => await this.DragEndAsync(dataItem))"
ondragover="@(this.AllowDrop && this.dragOverNode == dataItem && this.AllowNodeDrop ? "event.preventDefault();" : "")"
- @ondragenter="@(async () => await this.DragEnter(dataItem))"
- @ondragleave="@(async () => await this.DragLeave(dataItem))"
- @ondrop="@(async () => await this.Drop(dataItem))">
+ @ondragenter="@(async () => await this.DragEnterAsync(dataItem))"
+ @ondragleave="@(async () => await this.DragLeaveAsync(dataItem))"
+ @ondrop="@(async () => await this.DropAsync(dataItem))">
@dataItem.ElementName
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
index 4fafebb6..1672ab72 100644
--- a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
@@ -108,7 +108,7 @@ public partial class ElementDefinitionTree
/// Is evaluated when calculation if a node is draggable is necessary
///
[Parameter]
- public Func AllowNodeDrag { get; set; } = (x, y) => true;
+ public Func AllowNodeDrag { get; set; } = (_, _) => true;
///
/// Gets or sets a value indicating that dragging a node is allowed for this
@@ -190,7 +190,7 @@ public void ClearSelection()
///
/// The node where dragging has been started for
/// an awaitable
- private async Task DragStart(ElementBaseTreeRowViewModel node)
+ private async Task DragStartAsync(ElementBaseTreeRowViewModel node)
{
await this.OnDragStart.InvokeAsync((this, node));
await this.OnCalculateDropIsAllowed.InvokeAsync(this);
@@ -202,7 +202,7 @@ private async Task DragStart(ElementBaseTreeRowViewModel node)
///
/// The node where dragging has been ended for
/// an awaitable
- private async Task DragEnd(ElementBaseTreeRowViewModel node)
+ private async Task DragEndAsync(ElementBaseTreeRowViewModel node)
{
await this.OnDragEnd.InvokeAsync((this, node));
await this.OnCalculateDropIsAllowed.InvokeAsync(this);
@@ -214,7 +214,7 @@ private async Task DragEnd(ElementBaseTreeRowViewModel node)
///
/// The node where the dragged node has been dropped onto
/// an awaitable
- private async Task Drop(ElementBaseTreeRowViewModel node)
+ private async Task DropAsync(ElementBaseTreeRowViewModel node)
{
this.dragOverNode = null;
@@ -231,7 +231,7 @@ private async Task Drop(ElementBaseTreeRowViewModel node)
///
/// The node where the dragged node has been hovered over
/// an awaitable
- private async Task DragEnter(object node)
+ private async Task DragEnterAsync(object node)
{
if (this.AllowDrop)
{
@@ -248,7 +248,7 @@ private async Task DragEnter(object node)
///
/// The node where the dragged node had been hovered over
/// an awaitable
- private async Task DragLeave(object node)
+ private async Task DragLeaveAsync(object node)
{
if (this.AllowDrop)
{
diff --git a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
index ad61947a..a3798da5 100644
--- a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
+++ b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
@@ -33,7 +33,12 @@
The
public void InitializeViewModel(Iteration iteration)
{
+ ArgumentNullException.ThrowIfNull(iteration);
+
this.DomainOfExpertiseSelectorViewModel.CurrentIteration = iteration;
this.DomainOfExpertiseSelectorViewModel.AvailableDomainsOfExpertise = ((EngineeringModel)iteration.Container).EngineeringModelSetup.ActiveDomain.OrderBy(x => x.Name, StringComparer.InvariantCultureIgnoreCase);
}
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/IMultiModelEditorViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/IMultiModelEditorViewModel.cs
index 5dcaeecd..576c722b 100644
--- a/COMETwebapp/ViewModels/Components/MultiModelEditor/IMultiModelEditorViewModel.cs
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/IMultiModelEditorViewModel.cs
@@ -100,7 +100,7 @@ public interface IMultiModelEditorViewModel : ISingleIterationApplicationBaseVie
///
/// The to copy the node to
/// The to copy
- Task CopyAndAddNewElement(ElementDefinitionTree elementDefinitionTree, ElementBase elementBase);
+ Task CopyAndAddNewElementAsync(ElementDefinitionTree elementDefinitionTree, ElementBase elementBase);
///
/// Add a new based on an existing
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs
index 3ccd732b..b3608ded 100644
--- a/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/MultiModelEditorViewModel.cs
@@ -56,11 +56,6 @@ public class MultiModelEditorViewModel : SingleIterationApplicationBaseViewModel
///
private readonly ISessionService sessionService;
- ///
- /// The injected
- ///
- private readonly ILogger logger;
-
///
/// Backing field for
///
@@ -76,19 +71,17 @@ public class MultiModelEditorViewModel : SingleIterationApplicationBaseViewModel
///
/// the
/// The
- /// The injected
- public MultiModelEditorViewModel(ISessionService sessionService, ICDPMessageBus messageBus, ILogger logger) : base(sessionService, messageBus)
+ public MultiModelEditorViewModel(ISessionService sessionService, ICDPMessageBus messageBus) : base(sessionService, messageBus)
{
this.sessionService = sessionService;
- this.logger = logger;
var eventCallbackFactory = new EventCallbackFactory();
this.ElementDefinitionCreationViewModel = new ElementDefinitionCreationViewModel(sessionService, messageBus)
{
- OnValidSubmit = eventCallbackFactory.Create(this, this.AddingElementDefinition)
+ OnValidSubmit = eventCallbackFactory.Create(this, this.AddingElementDefinitionAsync)
};
- this.AddParameterViewModel = new ModelEditor.AddParameterViewModel.AddParameterViewModel(sessionService, messageBus)
+ this.AddParameterViewModel = new AddParameterViewModel(sessionService, messageBus)
{
OnParameterAdded = eventCallbackFactory.Create(this, () => this.IsOnAddingParameterMode = false)
};
@@ -190,45 +183,40 @@ public void OpenAddParameterPopup()
///
/// The to copy the node to
/// The to copy
- public async Task CopyAndAddNewElement(ElementDefinitionTree elementDefinitionTree, ElementBase elementBase)
+ public Task CopyAndAddNewElementAsync(ElementDefinitionTree elementDefinitionTree, ElementBase elementBase)
+ {
+ ArgumentNullException.ThrowIfNull(elementDefinitionTree);
+ ArgumentNullException.ThrowIfNull(elementBase);
+
+ return this.CopyAndAddNewElementImplAsync(elementDefinitionTree, elementBase);
+ }
+
+ ///
+ /// Add a new based on an existing
+ ///
+ /// The to copy the node to
+ /// The to copy
+ private async Task CopyAndAddNewElementImplAsync(ElementDefinitionTree elementDefinitionTree, ElementBase elementBase)
{
this.IsLoading = true;
- if (elementBase.GetContainerOfType() == elementDefinitionTree.ViewModel.Iteration)
+ try
{
- var copyCreator = new CopyElementDefinitionCreator(this.sessionService.Session);
-
- try
+ if (elementBase.GetContainerOfType() == elementDefinitionTree.ViewModel.Iteration)
{
- await copyCreator.Copy((ElementDefinition)elementBase, true);
+ var copyCreator = new CopyElementDefinitionCreator(this.sessionService.Session);
+ await copyCreator.CopyAsync((ElementDefinition)elementBase, true);
}
- catch (Exception exception)
+ else
{
- this.logger.LogError(exception, string.Empty);
- throw;
- }
- finally
- {
- this.IsLoading = false;
+ var copyCreator = new CopyCreator(this.sessionService.Session);
+ await copyCreator.CopyAsync((ElementDefinition)elementBase, elementDefinitionTree.ViewModel.Iteration);
}
}
- else
- {
- var copyCreator = new CopyCreator(this.sessionService.Session);
+ finally
- try
- {
- await copyCreator.Copy((ElementDefinition)elementBase, elementDefinitionTree.ViewModel.Iteration);
- }
- catch (Exception exception)
- {
- this.logger.LogError(exception, string.Empty);
- throw;
- }
- finally
- {
- this.IsLoading = false;
- }
+ {
+ this.IsLoading = false;
}
}
@@ -237,7 +225,20 @@ public async Task CopyAndAddNewElement(ElementDefinitionTree elementDefinitionTr
///
/// The to be added as
/// The where to add the new to
- public async Task AddNewElementUsage(ElementBase fromElementBase, ElementBase toElementBase)
+ public Task AddNewElementUsage(ElementBase fromElementBase, ElementBase toElementBase)
+ {
+ ArgumentNullException.ThrowIfNull(fromElementBase);
+ ArgumentNullException.ThrowIfNull(toElementBase);
+
+ return this.AddNewElementUsageImplAsync(fromElementBase, toElementBase);
+ }
+
+ ///
+ /// Add a new based on an existing
+ ///
+ /// The to be added as
+ /// The where to add the new to
+ private async Task AddNewElementUsageImplAsync(ElementBase fromElementBase, ElementBase toElementBase)
{
if (fromElementBase.GetContainerOfType() == toElementBase.GetContainerOfType())
{
@@ -247,12 +248,7 @@ public async Task AddNewElementUsage(ElementBase fromElementBase, ElementBase to
try
{
- await thingCreator.CreateElementUsage((ElementDefinition)toElementBase, (ElementDefinition)fromElementBase, this.sessionService.Session.OpenIterations.First(x => x.Key == toElementBase.GetContainerOfType()).Value.Item1, this.sessionService.Session);
- }
- catch (Exception exception)
- {
- this.logger.LogError(exception, string.Empty);
- throw;
+ await thingCreator.CreateElementUsageAsync((ElementDefinition)toElementBase, (ElementDefinition)fromElementBase, this.sessionService.Session.OpenIterations.First(x => x.Key == toElementBase.GetContainerOfType()).Value.Item1, this.sessionService.Session);
}
finally
{
@@ -265,7 +261,7 @@ public async Task AddNewElementUsage(ElementBase fromElementBase, ElementBase to
/// Tries to create a new
///
/// A
- public async Task AddingElementDefinition()
+ public async Task AddingElementDefinitionAsync()
{
var thingsToCreate = new List();
@@ -289,12 +285,10 @@ public async Task AddingElementDefinition()
try
{
await this.sessionService.CreateOrUpdateThings(clonedIteration, thingsToCreate);
- this.IsOnCreationMode = false;
}
- catch (Exception exception)
+ finally
{
- this.logger.LogError(exception, string.Empty);
- throw;
+ this.IsOnCreationMode = false;
}
}
From fe557d0a0bf241f4b8f29ddb85b30f301c7c35d2 Mon Sep 17 00:00:00 2001
From: Alexander van Delft
Date: Mon, 6 Jan 2025 17:16:11 +0100
Subject: [PATCH 08/23] More SQ comments
---
.../ElementDefinitionTree.razor | 8 +-
.../ElementDefinitionTree.razor.cs | 4 +-
.../MultiModelEditor/MultiModelEditor.razor | 46 ++++++-----
.../MultiModelEditor.razor.cs | 78 +++++++++----------
4 files changed, 69 insertions(+), 67 deletions(-)
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor
index f3798286..43b9e4fa 100644
--- a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor
@@ -78,10 +78,10 @@ else
@dataItem.ElementName
- @foreach (var category in @dataItem.ElementBase.GetAllCategories())
+ @foreach (var categoryShortName in @dataItem.ElementBase.GetAllCategories().Select(x => x.ShortName))
{
-
+
}
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor.cs b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor.cs
new file mode 100644
index 00000000..fb3485f6
--- /dev/null
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor.cs
@@ -0,0 +1,54 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) 2024 Starion Group S.A.
+//
+// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua
+//
+// This file is part of COMET WEB Community Edition
+// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C.
+//
+// The COMET WEB Community Edition is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Affero General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// The COMET WEB Community Edition is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace COMETwebapp.Components.MultiModelEditor
+{
+ using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows;
+
+ using Microsoft.AspNetCore.Components;
+
+ ///
+ /// Support class for the component
+ ///
+ public partial class ElementDefinitionTreeItem
+ {
+ ///
+ /// The css class string for the item
+ ///
+ [Parameter]
+ public string CssClass { get; set; } = string.Empty;
+
+ ///
+ /// The to show in the item
+ ///
+ [Parameter]
+ public ElementBaseTreeRowViewModel ElementBaseTreeRowViewModel { get; set; }
+
+ ///
+ /// Handle unmatched values, like "draggable" html attribute, so no error is thrown
+ ///
+ [Parameter(CaptureUnmatchedValues = true)]
+ public Dictionary AdditionalAttributes { get; set; }
+ }
+}
diff --git a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
index 4ad3b32d..b2d90f09 100644
--- a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
+++ b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor
@@ -99,14 +99,14 @@
-
+
-
+
-
+
-
+
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/ElementDefinitionTreeViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/ElementDefinitionTreeViewModel.cs
index 1c535259..f8b00b05 100644
--- a/COMETwebapp/ViewModels/Components/MultiModelEditor/ElementDefinitionTreeViewModel.cs
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/ElementDefinitionTreeViewModel.cs
@@ -126,11 +126,6 @@ public IterationData SelectedIterationData
///
public ObservableCollection Rows { get; private set; } = [];
- ///
- /// Represents the selected ElementDefinitionRowViewModel
- ///
- public ElementDefinition SelectedElementDefinition { get; set; }
-
///
/// Add rows related to that has been added
///
diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/IElementDefinitionTreeViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/IElementDefinitionTreeViewModel.cs
index 0bbb0cd2..720aa34d 100644
--- a/COMETwebapp/ViewModels/Components/MultiModelEditor/IElementDefinitionTreeViewModel.cs
+++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/IElementDefinitionTreeViewModel.cs
@@ -48,11 +48,6 @@ public interface IElementDefinitionTreeViewModel : IHaveReusableRows
///
Iteration Iteration { get; set; }
- ///
- /// Represents the selected ElementDefinitionRowViewModel
- ///
- ElementDefinition SelectedElementDefinition { get; set; }
-
///
/// Gets the Description of the selected model and iteration
///
From a67b0dd7a8b674a2f379bf7682015b2021d83da0 Mon Sep 17 00:00:00 2001
From: Alexander van Delft
Date: Thu, 9 Jan 2025 14:43:10 +0100
Subject: [PATCH 20/23] [FIX] sizing issues
---
.../Components/CardView/CardView.razor | 2 +-
COMET.Web.Common/wwwroot/css/styles.css | 6 +-
.../ElementDefinitionTree.razor | 84 ++++++++++---------
.../MultiModelEditor/MultiModelEditor.razor | 42 +++++-----
4 files changed, 69 insertions(+), 65 deletions(-)
diff --git a/COMET.Web.Common/Components/CardView/CardView.razor b/COMET.Web.Common/Components/CardView/CardView.razor
index 6c63e8a2..4ef7adc2 100644
--- a/COMET.Web.Common/Components/CardView/CardView.razor
+++ b/COMET.Web.Common/Components/CardView/CardView.razor
@@ -39,7 +39,7 @@
-
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor
index 0da81199..3599d225 100644
--- a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor
@@ -20,15 +20,17 @@
// along with this program. If not, see http://www.gnu.org/licenses/.
------------------------------------------------------------------------------->
-
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
index 72bc27ff..29d6f988 100644
--- a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs
@@ -122,6 +122,12 @@ public partial class ElementDefinitionTree
[Parameter]
public bool AllowDrop { get; set; }
+ ///
+ /// CssClass of the ScrollableArea
+ ///
+ [Parameter]
+ public string ScrollableAreaCssClass { get; set; } = string.Empty;
+
///
/// Holds a reference to the object where the dragged node is dragged over
///
@@ -142,6 +148,21 @@ public partial class ElementDefinitionTree
///
public DxTreeView TreeView { get; set; }
+ ///
+ /// Gets or sets a value indication that searching is allowed
+ ///
+ public bool AllowSearch { get; set; } = true;
+
+ ///
+ /// Gets or sets a collection of propertynames of type T to perform search on
+ ///
+ public HashSet SearchFields { get; private set; } = [];
+
+ ///
+ /// Gets or sets the term where to search/filter items on
+ ///
+ public string SearchTerm { get; set; } = string.Empty;
+
///
/// Method invoked after each time the component has been rendered. Note that the component does
/// not automatically re-render after the completion of any returned , because
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor
index 3599d225..550922a0 100644
--- a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor
+++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor
@@ -19,17 +19,20 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
------------------------------------------------------------------------------->
-