From 825bef3681e69154944058416c94009f80b58c7f Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Wed, 13 Sep 2023 17:20:50 +0200 Subject: [PATCH 01/20] Add BookApplication and make first draft of page --- .../wwwroot/css/SVG/icons/plus-circle.svg | 1 + COMET.Web.Common/wwwroot/css/styles.css | 4 + .../BookEditor/BookEditorBody.razor | 105 ++++++++++ .../BookEditor/BookEditorBody.razor.cs | 40 ++++ .../BookEditor/BookEditorBody.razor.css | 121 +++++++++++ COMETwebapp/Model/Applications.cs | 8 + COMETwebapp/Pages/BookEditor/BookEditor.razor | 31 +++ COMETwebapp/Program.cs | 3 + COMETwebapp/Utilities/WebAppConstantValues.cs | 5 + .../BookEditor/BookEditorBodyViewModel.cs | 191 ++++++++++++++++++ .../BookEditor/IBookEditorBodyViewModel.cs | 59 ++++++ 11 files changed, 568 insertions(+) create mode 100644 COMET.Web.Common/wwwroot/css/SVG/icons/plus-circle.svg create mode 100644 COMETwebapp/Components/BookEditor/BookEditorBody.razor create mode 100644 COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs create mode 100644 COMETwebapp/Components/BookEditor/BookEditorBody.razor.css create mode 100644 COMETwebapp/Pages/BookEditor/BookEditor.razor create mode 100644 COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs create mode 100644 COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs diff --git a/COMET.Web.Common/wwwroot/css/SVG/icons/plus-circle.svg b/COMET.Web.Common/wwwroot/css/SVG/icons/plus-circle.svg new file mode 100644 index 00000000..76491c9d --- /dev/null +++ b/COMET.Web.Common/wwwroot/css/SVG/icons/plus-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/COMET.Web.Common/wwwroot/css/styles.css b/COMET.Web.Common/wwwroot/css/styles.css index 340570d3..35dbfd09 100644 --- a/COMET.Web.Common/wwwroot/css/styles.css +++ b/COMET.Web.Common/wwwroot/css/styles.css @@ -36,6 +36,10 @@ background-image: url(SVG/icons/upload-cloud.svg) } +.icon-plus { + background-image: url(SVG/icons/plus-circle.svg) +} + .logo-size { max-width: 95px; height: auto; diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor b/COMETwebapp/Components/BookEditor/BookEditorBody.razor new file mode 100644 index 00000000..ab9174e5 --- /dev/null +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor @@ -0,0 +1,105 @@ + +@using COMETwebapp.Utilities +@using COMETwebapp.Extensions +@inherits SingleIterationApplicationBase + + +
+
+
+
Books
+ +
+
+
+ @foreach (var book in this.ViewModel.AvailableBooks.Items) + { + var conditionalClass = this.ViewModel.SelectedBook == book ? "selected" : ""; + +
+ } +
+
+
+
+
Sections
+ +
+
+ @if (this.ViewModel.SelectedBook != null && this.ViewModel.SelectedBook.Section.Any()) + { +
+ foreach (var sectionValue in this.ViewModel.SelectedBook.Section.ToList()) + { + var conditionalClass = this.ViewModel.SelectedSection == sectionValue ? "selected" : ""; + +
+ } + } +
+
+
+
+
Pages
+ +
+
+ @if (this.ViewModel.SelectedSection != null && this.ViewModel.SelectedSection.Page.Any()) + { +
+ foreach (var pageValue in this.ViewModel.SelectedSection.Page.ToList()) + { + var conditionalClass = this.ViewModel.SelectedPage == pageValue ? "selected" : ""; + +
+ } + } +
+
+
+
+
Notes
+ +
+
+ @if (this.ViewModel.SelectedPage != null && this.ViewModel.SelectedPage.Note.Any()) + { +
+ foreach (var note in this.ViewModel.SelectedPage.Note.ToList()) + { + +
+ } + } +
+
+
+
\ No newline at end of file diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs new file mode 100644 index 00000000..eddcc556 --- /dev/null +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs @@ -0,0 +1,40 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.BookEditor +{ + /// + /// Core component for the Book Editor application + /// + public partial class BookEditorBody + { + /// + /// 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) + { + } + } +} diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.css b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.css new file mode 100644 index 00000000..c415dc58 --- /dev/null +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.css @@ -0,0 +1,121 @@ +.book-editor-body { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-template-rows: 1fr; + grid-column-gap: 0px; + grid-row-gap: 0px; +} + +.book-editor-column{ + display:flex; + flex-direction:column; + min-height: 85vh; + border: 0px dashed #555; + padding: 5% 0%; +} + +#books-column { + grid-area: 1 / 1 / 2 / 2; +} + +#sections-column { + grid-area: 1 / 2 / 2 / 3; +} + +#pages-column { + grid-area: 1 / 3 / 2 / 4; +} + +#notes-column { + grid-area: 1 / 4 / 2 / 5; +} + +.column-header{ + display:flex; + align-items:center; + justify-content:center; + column-gap: 5px; +} + + .column-header > h5 { + font-size: 1rem; + text-transform: uppercase; + text-align: center; + margin: 0; + box-shadow: 0px 0px 4px rgba(0,0,0,0.2); + padding: 1%; + border-radius:15px; + color:white; + } + +.add-item-button{ + border:0; + background-color:transparent; +} + + +.column-content { + width: 100%; + height: 100%; + overflow-y: auto; + display: flex; + flex-direction:column; + align-items:center; + justify-content:flex-start; + row-gap:10px; + padding-top: 2%; + position:relative; +} + +.node-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; +} + +.book-button,.section-button,.page-button,.note-button{ + border: 0; + box-shadow: 0px 0px 4px rgba(0,0,0,0.2); + padding: 5%; + min-width: 50%; + border-radius: 7px; + transition: all 0.3s; + z-index:1; +} + + .book-button:hover, .section-button:hover, .page-button:hover, .note-button:hover { + box-shadow: 0px 0px 0px 2px rgba(0,0,0,0.2); + } + +.selected{ + box-shadow: 0px 0px 0px 2px #333388 !important; +} + +.vertical-line { + background-color: transparent; + height: 15px; + border: 0; + border-left: 2px dashed #CCC; +} + +.horizontal-line { + background-color:transparent; + margin:0; + top:55px; + left:0px; + position:absolute; + width: 100%; + border: 0; + border-top: 2px dashed #555; +} + +.left-half-width{ + left: 0%; + width: 50%; +} + +.right-half-width{ + left:50%; + width:50%; +} diff --git a/COMETwebapp/Model/Applications.cs b/COMETwebapp/Model/Applications.cs index 1623d77a..6f572ca1 100644 --- a/COMETwebapp/Model/Applications.cs +++ b/COMETwebapp/Model/Applications.cs @@ -119,6 +119,14 @@ public static class Applications Icon = "box", Description = "Populate model", Url = WebAppConstantValues.ModelEditorPage + }, + new Application + { + Name = "Book Editor", + Color = "#76fd98", + Icon = "book", + Description = "Manage books", + Url = WebAppConstantValues.BookEditorPage } }; } diff --git a/COMETwebapp/Pages/BookEditor/BookEditor.razor b/COMETwebapp/Pages/BookEditor/BookEditor.razor new file mode 100644 index 00000000..07a71800 --- /dev/null +++ b/COMETwebapp/Pages/BookEditor/BookEditor.razor @@ -0,0 +1,31 @@ + +@using COMETwebapp.Utilities +@using COMETwebapp.Components.BookEditor +@attribute [Route(WebAppConstantValues.BookEditorPage)] +@inherits COMET.Web.Common.Pages.SingleIterationPageTemplate + + + + + + \ No newline at end of file diff --git a/COMETwebapp/Program.cs b/COMETwebapp/Program.cs index b4f83307..3b430917 100644 --- a/COMETwebapp/Program.cs +++ b/COMETwebapp/Program.cs @@ -50,6 +50,8 @@ namespace COMETwebapp using System.Reflection; + using COMETwebapp.ViewModels.Components.BookEditor; + /// /// Point of entry of the application /// @@ -114,6 +116,7 @@ public static void RegisterViewModels(WebAssemblyHostBuilder builder) builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(); } } } diff --git a/COMETwebapp/Utilities/WebAppConstantValues.cs b/COMETwebapp/Utilities/WebAppConstantValues.cs index 507915ea..d1491675 100644 --- a/COMETwebapp/Utilities/WebAppConstantValues.cs +++ b/COMETwebapp/Utilities/WebAppConstantValues.cs @@ -120,5 +120,10 @@ public static class WebAppConstantValues /// The page name of the Model Editor /// public const string ModelEditorPage = "ModelEditor"; + + /// + /// The page name of the Book Editor + /// + public const string BookEditorPage = "BookEditor"; } } diff --git a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs new file mode 100644 index 00000000..83ade683 --- /dev/null +++ b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs @@ -0,0 +1,191 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.BookEditor +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.ReportingData; + + using CDP4Dal; + + using COMET.Web.Common.Services.SessionManagement; + using COMET.Web.Common.ViewModels.Components; + + using DynamicData; + + using ReactiveUI; + + /// + /// ViewModel for the BookEditorBody component + /// + public class BookEditorBodyViewModel : SingleIterationApplicationBaseViewModel, IBookEditorBodyViewModel + { + /// + /// Backing field for the property + /// + private Book selectedBook; + + /// + /// Backing field for the property + /// + private Section selectedSection; + + /// + /// Backing field for the property + /// + private Page selectedPage; + + /// + /// Gets or sets the current selected + /// + public Book SelectedBook + { + get => this.selectedBook; + set => this.RaiseAndSetIfChanged(ref this.selectedBook, value); + } + + /// + /// Gets or sets the current selected + /// + public Section SelectedSection + { + get => this.selectedSection; + set => this.RaiseAndSetIfChanged(ref this.selectedSection, value); + } + + /// + /// Gets or sets the current selected + /// + public Page SelectedPage + { + get => this.selectedPage; + set => this.RaiseAndSetIfChanged(ref this.selectedPage, value); + } + + /// + /// Gets or sets the collection of available for this + /// + public SourceList AvailableBooks { get; set; } = new(); + + /// + /// Initializes a new instance of the class. + /// + /// The + public BookEditorBodyViewModel(ISessionService sessionService) : base(sessionService) + { + this.Disposables.Add(this.WhenAnyValue(x => x.SelectedBook).Subscribe(_ => this.OnSelectedBookChanged())); + this.Disposables.Add(this.WhenAnyValue(x => x.SelectedSection).Subscribe(_ => this.OnSelectedSectionChanged())); + } + + /// + /// Handles the refresh of the current + /// + /// A + protected override Task OnSessionRefreshed() + { + return this.OnIterationChanged(); + } + + /// + /// Update this view model properties when the has changed + /// + /// A + protected override async Task OnIterationChanged() + { + this.IsLoading = true; + await base.OnIterationChanged(); + var a = this.CurrentIteration; + + this.AvailableBooks.Edit(inner => + { + inner.Clear(); + inner.AddRange(((EngineeringModel)this.CurrentIteration.Container).Book); + }); + + this.CreateFakeData(); + this.IsLoading = false; + } + + /// + /// Handler for when the selected book changed + /// + private void OnSelectedBookChanged() + { + this.SelectedSection = null; + this.SelectedPage = null; + } + + /// + /// Handler for when the selected section changed + /// + private void OnSelectedSectionChanged() + { + this.SelectedPage = null; + } + + private void CreateFakeData() + { + var book = new Book + { + Name = "Example Book", + Section = + { + new Section + { + Name = "Example Section", + Page = + { + new Page + { + Name = "Example Page", + Note = + { + new BinaryNote + { + Name = "Binary Note" + }, + new TextualNote + { + Name = "Textual Note" + } + } + }, + new Page + { + Name = "Example Page 2" + } + } + }, + new Section + { + Name = "Empty Section" + } + } + }; + + this.AvailableBooks.Add(book); + } + } +} + diff --git a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs new file mode 100644 index 00000000..1783c67b --- /dev/null +++ b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs @@ -0,0 +1,59 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.BookEditor +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.ReportingData; + + using COMET.Web.Common.ViewModels.Components; + + using DynamicData; + + /// + /// ViewModel for the BookEditorBody component + /// + public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewModel + { + /// + /// Gets or sets the current selected + /// + Book SelectedBook { get; set; } + + /// + /// Gets or sets the current selected + /// + Section SelectedSection { get; set; } + + /// + /// Gets or sets the current selected + /// + Page SelectedPage { get; set; } + + /// + /// Gets or sets the collection of available for this + /// + SourceList AvailableBooks { get; set; } + } +} From 3e397bae4464c15fc18e5c49bcd22719a0b65cf7 Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Thu, 14 Sep 2023 14:19:50 +0200 Subject: [PATCH 02/20] Create BookInputColumn component --- COMET.Web.Common/COMET.Web.Common.csproj | 3 + .../Components/BookEditor/BookInput.razor | 62 +++++++ .../Components/BookEditor/BookInput.razor.cs | 61 +++++++ .../Components/BookEditor/BookInput.razor.css | 18 ++ .../BookEditor/BookEditorBody.razor | 81 +++++++-- .../BookEditor/BookEditorBody.razor.cs | 26 +++ .../BookEditor/BookEditorBody.razor.css | 24 +-- .../BookEditor/BookEditorColumn.razor | 50 ++++++ .../BookEditor/BookEditorColumn.razor.cs | 88 +++++++++ .../BookEditor/BookEditorColumn.razor.css | 103 +++++++++++ .../BookEditor/BookEditorBodyViewModel.cs | 168 +++++++++++++++++- .../BookEditor/IBookEditorBodyViewModel.cs | 67 +++++++ COMETwebapp/wwwroot/index.html | 1 + 13 files changed, 710 insertions(+), 42 deletions(-) create mode 100644 COMET.Web.Common/Components/BookEditor/BookInput.razor create mode 100644 COMET.Web.Common/Components/BookEditor/BookInput.razor.cs create mode 100644 COMET.Web.Common/Components/BookEditor/BookInput.razor.css create mode 100644 COMETwebapp/Components/BookEditor/BookEditorColumn.razor create mode 100644 COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs create mode 100644 COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css diff --git a/COMET.Web.Common/COMET.Web.Common.csproj b/COMET.Web.Common/COMET.Web.Common.csproj index 0b5381e7..26b8a489 100644 --- a/COMET.Web.Common/COMET.Web.Common.csproj +++ b/COMET.Web.Common/COMET.Web.Common.csproj @@ -50,4 +50,7 @@ Always + + + \ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/BookInput.razor b/COMET.Web.Common/Components/BookEditor/BookInput.razor new file mode 100644 index 00000000..c55b11f3 --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/BookInput.razor @@ -0,0 +1,62 @@ + +@namespace COMET.Web.Common.Components.BookEditor +@using CDP4Common.SiteDirectoryData +@inherits DisposableComponent + + + +
+
+

Short Name:

+ +
+
+

Name:

+ +
+
+

Owner:

+ +
+
+
+ +
+ + +
+
+
\ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/BookInput.razor.cs b/COMET.Web.Common/Components/BookEditor/BookInput.razor.cs new file mode 100644 index 00000000..9007d36d --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/BookInput.razor.cs @@ -0,0 +1,61 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.Components.BookEditor +{ + using CDP4Common.ReportingData; + using CDP4Common.SiteDirectoryData; + + using Microsoft.AspNetCore.Components; + + /// + /// Support class for the BookInput component + /// + public partial class BookInput + { + /// + /// Gets or sets the book for which the input is being provided + /// + [Parameter] + public Book Book { get; set; } + + /// + /// Gets or sets the active + /// + [Parameter] + public IEnumerable ActiveDomains { get; set; } + + /// + /// Gets or sets the collection of available + /// + [Parameter] + public IEnumerable AvailableCategories { get; set; } + + private void OnCategoryChange(IEnumerable categories) + { + this.Book.Category = categories.ToList(); + } + } +} diff --git a/COMET.Web.Common/Components/BookEditor/BookInput.razor.css b/COMET.Web.Common/Components/BookEditor/BookInput.razor.css new file mode 100644 index 00000000..8a530845 --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/BookInput.razor.css @@ -0,0 +1,18 @@ +.tab-content { + display: flex; + flex-direction: column; + align-items: start; + justify-content: flex-start; + row-gap: 5px; + padding:1%; +} + +.editor-row{ + align-items:center; + justify-content:space-between; +} + +.editor-row>p{ + width: 25%; + margin:0; +} \ No newline at end of file diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor b/COMETwebapp/Components/BookEditor/BookEditorBody.razor index ab9174e5..0f522af9 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor @@ -21,14 +21,59 @@ -------------------------------------------------------------------------------> @using COMETwebapp.Utilities @using COMETwebapp.Extensions +@using COMET.Web.Common.Components.BookEditor +@using CDP4Common.ReportingData @inherits SingleIterationApplicationBase + + @{ + var popupVisible = this.ViewModel.IsOnBookCreation || this.ViewModel.IsOnSectionCreation || this.ViewModel.IsOnPageCreation || this.ViewModel.IsOnNoteCreation; + var parameters = new Dictionary + { + {nameof(BookInput.Book), this.ViewModel.BookToCreate}, + {nameof(BookInput.ActiveDomains), this.ViewModel.ActiveDomains}, + {nameof(BookInput.AvailableCategories), this.ViewModel.AvailableCategories} + }; + var booksColumnConditionalClass = this.ViewModel.SelectedSection != null ? "collapsed" : ""; + } + + + + + + + + + +
-
+ + + + + + + + +@*
Books
- +

@@ -45,7 +90,7 @@
Sections
- +
@if (this.ViewModel.SelectedBook != null && this.ViewModel.SelectedBook.Section.Any()) @@ -65,27 +110,27 @@
Pages
- +
- @if (this.ViewModel.SelectedSection != null && this.ViewModel.SelectedSection.Page.Any()) - { -
- foreach (var pageValue in this.ViewModel.SelectedSection.Page.ToList()) - { - var conditionalClass = this.ViewModel.SelectedPage == pageValue ? "selected" : ""; - -
- } - } + @if (this.ViewModel.SelectedSection != null && this.ViewModel.SelectedSection.Page.Any()) + { +
+ foreach (var pageValue in this.ViewModel.SelectedSection.Page.ToList()) + { + var conditionalClass = this.ViewModel.SelectedPage == pageValue ? "selected" : ""; + +
+ } + }
Notes
- +
@if (this.ViewModel.SelectedPage != null && this.ViewModel.SelectedPage.Note.Any()) @@ -100,6 +145,6 @@ } }
-
+
*@
\ No newline at end of file diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs index eddcc556..d0828ba9 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs @@ -36,5 +36,31 @@ public partial class BookEditorBody protected override void InitializeValues(Dictionary parameters) { } + + /// + /// Gets the text of the header depending on the state of the ViewModel + /// + /// + private string GetHeaderText() + { + if (this.ViewModel.IsOnBookCreation) + { + return "Create a new Book"; + } + else if (this.ViewModel.IsOnSectionCreation) + { + return "Create a new Section"; + } + else if (this.ViewModel.IsOnPageCreation) + { + return "Create a new Page"; + } + else if (this.ViewModel.IsOnNoteCreation) + { + return "Create a new Node"; + } + + return string.Empty; + } } } diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.css b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.css index c415dc58..246bc333 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.css +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.css @@ -1,33 +1,19 @@ .book-editor-body { - display: grid; - grid-template-columns: repeat(4, 1fr); - grid-template-rows: 1fr; - grid-column-gap: 0px; - grid-row-gap: 0px; + display: flex; } .book-editor-column{ + width: 100%; display:flex; flex-direction:column; min-height: 85vh; border: 0px dashed #555; padding: 5% 0%; + transition: all 1s; } -#books-column { - grid-area: 1 / 1 / 2 / 2; -} - -#sections-column { - grid-area: 1 / 2 / 2 / 3; -} - -#pages-column { - grid-area: 1 / 3 / 2 / 4; -} - -#notes-column { - grid-area: 1 / 4 / 2 / 5; +.book-editor-column.collapsed{ + width:0%; } .column-header{ diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor new file mode 100644 index 00000000..0024d3ee --- /dev/null +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor @@ -0,0 +1,50 @@ + +@inherits DisposableComponent +@typeparam TItem + +@{ + var columnConditionalClass = this.IsCollapsed ? "collapsed" : ""; +} + +
+
+
@this.HeaderTitle
+ +
+
+
+ @if (this.Items.Any()) + { + + } + + @{ + var conditionalClass = this.SelectedValue.Equals(item) ? "selected" : ""; + +
+ } +
+
+
\ No newline at end of file diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs new file mode 100644 index 00000000..877754d1 --- /dev/null +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs @@ -0,0 +1,88 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.BookEditor +{ + using System.Drawing; + + using Microsoft.AspNetCore.Components; + + /// + /// Support class for the BookEditorColumn component + /// + public partial class BookEditorColumn + { + /// + /// Gets or sets the title of the header + /// + [Parameter] + public string HeaderTitle { get; set; } + + /// + /// Gets or sets the color of the header in hexadecimal format + /// + [Parameter] + public string HeaderHexColor { get; set; } + + /// + /// Gets or sets if the column is collapsed. + /// + [Parameter] + public bool IsCollapsed { get; set; } + + /// + /// Gets or set the collection of items + /// + [Parameter] + public ICollection Items { get; set; } = new List(); + + /// + /// Gets or sets the selected value + /// + [Parameter] + public TItem SelectedValue { get; set; } + + /// + /// Gets or sets the for when the changes + /// + [Parameter] + public EventCallback SelectedValueChanged { get; set; } + + /// + /// Gets or sets the for when a new item was requested + /// + [Parameter] + public EventCallback OnCreateNewItemClick { get; set; } + + /// + /// Hanlder for when the selected value changes + /// + /// the item selected + /// an asynchronous operation + private async Task OnSelectedValueChanged(TItem item) + { + await this.SelectedValueChanged.InvokeAsync(item); + } + } +} diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css new file mode 100644 index 00000000..87984a9a --- /dev/null +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css @@ -0,0 +1,103 @@ +.book-editor-column { + width: 100%; + display: flex; + flex-direction: column; + min-height: 85vh; + border: 0px dashed #555; + padding: 5% 0%; + transition: all 1s; +} + + .book-editor-column.collapsed { + width: 0%; + } + +.column-header { + display: flex; + align-items: center; + justify-content: center; + column-gap: 5px; +} + + .column-header > h5 { + font-size: 1rem; + text-transform: uppercase; + text-align: center; + margin: 0; + box-shadow: 0px 0px 4px rgba(0,0,0,0.2); + padding: 1%; + border-radius: 15px; + color: white; + } + +.add-item-button { + border: 0; + background-color: transparent; +} + + +.column-content { + width: 100%; + height: 100%; + overflow-y: auto; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + row-gap: 10px; + padding-top: 2%; + position: relative; +} + +.node-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; +} + +.book-button, .section-button, .page-button, .note-button { + border: 0; + box-shadow: 0px 0px 4px rgba(0,0,0,0.2); + padding: 5%; + min-width: 50%; + border-radius: 7px; + transition: all 0.3s; + z-index: 1; +} + + .book-button:hover, .section-button:hover, .page-button:hover, .note-button:hover { + box-shadow: 0px 0px 0px 2px rgba(0,0,0,0.2); + } + +.selected { + box-shadow: 0px 0px 0px 2px #333388 !important; +} + +.vertical-line { + background-color: transparent; + height: 15px; + border: 0; + border-left: 2px dashed #CCC; +} + +.horizontal-line { + background-color: transparent; + margin: 0; + top: 55px; + left: 0px; + position: absolute; + width: 100%; + border: 0; + border-top: 2px dashed #555; +} + +.left-half-width { + left: 0%; + width: 50%; +} + +.right-half-width { + left: 50%; + width: 50%; +} diff --git a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs index 83ade683..4d2a91d2 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs @@ -26,6 +26,7 @@ namespace COMETwebapp.ViewModels.Components.BookEditor { using CDP4Common.EngineeringModelData; using CDP4Common.ReportingData; + using CDP4Common.SiteDirectoryData; using CDP4Dal; @@ -34,6 +35,8 @@ namespace COMETwebapp.ViewModels.Components.BookEditor using DynamicData; + using Microsoft.AspNetCore.Components; + using ReactiveUI; /// @@ -41,6 +44,26 @@ namespace COMETwebapp.ViewModels.Components.BookEditor /// public class BookEditorBodyViewModel : SingleIterationApplicationBaseViewModel, IBookEditorBodyViewModel { + /// + /// Backing field for the property + /// + private bool isOnBookCreation; + + /// + /// Backing field for the property + /// + private bool isOnSectionCreation; + + /// + /// Backing field for the property + /// + private bool isOnPageCreation; + + /// + /// Backing field for the property + /// + private bool isOnNodeCreation; + /// /// Backing field for the property /// @@ -88,6 +111,82 @@ public Page SelectedPage /// public SourceList AvailableBooks { get; set; } = new(); + /// + /// Gets or sets the available categories + /// + public List AvailableCategories { get; set; } = new(); + + /// + /// Gets or sets the active + /// + public List ActiveDomains { get; set; } = new(); + + /// + /// Gets or sets if the ViewModel is on book creation state + /// + public bool IsOnBookCreation + { + get => this.isOnBookCreation; + set => this.RaiseAndSetIfChanged(ref this.isOnBookCreation, value); + } + + /// + /// Gets or sets if the ViewModel is on section creation state + /// + public bool IsOnSectionCreation + { + get => this.isOnSectionCreation; + set => this.RaiseAndSetIfChanged(ref this.isOnSectionCreation, value); + } + + /// + /// Gets or sets if the ViewModel is on page creation state + /// + public bool IsOnPageCreation + { + get => this.isOnPageCreation; + set => this.RaiseAndSetIfChanged(ref this.isOnPageCreation, value); + } + + /// + /// Gets or sets if the ViewModel is on node creation state + /// + public bool IsOnNoteCreation + { + get => this.isOnNodeCreation; + set => this.RaiseAndSetIfChanged(ref this.isOnNodeCreation, value); + } + + /// + /// Gets or sets the that's about to be created + /// + public Book BookToCreate { get; set; } + + /// + /// Gets or sets the that's about to be created + /// + public Section SectionToCreate { get; set; } + + /// + /// Gets or sets the that's about to be created + /// + public Page PageToCreate { get; set; } + + /// + /// Gets or sets the that's about to be created + /// + public Note NoteToCreate { get; set; } + + /// + /// Gets or sets the for when an item is created + /// + public EventCallback OnCreateItem { get; set; } + + /// + /// Gets or sets the for when an item has canceled it's creation + /// + public EventCallback OnCancelCreateItem { get; set; } + /// /// Initializes a new instance of the class. /// @@ -96,6 +195,12 @@ public BookEditorBodyViewModel(ISessionService sessionService) : base(sessionSer { this.Disposables.Add(this.WhenAnyValue(x => x.SelectedBook).Subscribe(_ => this.OnSelectedBookChanged())); this.Disposables.Add(this.WhenAnyValue(x => x.SelectedSection).Subscribe(_ => this.OnSelectedSectionChanged())); + + this.Disposables.Add(this.WhenAnyValue(x => x.IsOnBookCreation, x=> x.IsOnSectionCreation, x=>x.IsOnPageCreation, x=>x.IsOnNoteCreation).Subscribe(_=>this.ResetDataToCreate())); + + this.OnCreateItem = new EventCallbackFactory().Create(this, this.OnHandleCreateItem); + + this.OnCancelCreateItem = new EventCallbackFactory().Create(this, this.ResetCreationStates); } /// @@ -115,18 +220,46 @@ protected override async Task OnIterationChanged() { this.IsLoading = true; await base.OnIterationChanged(); - var a = this.CurrentIteration; - this.AvailableBooks.Edit(inner => + if (this.CurrentIteration.Container is EngineeringModel engineeringModel) { - inner.Clear(); - inner.AddRange(((EngineeringModel)this.CurrentIteration.Container).Book); - }); + this.AvailableBooks.Edit(inner => + { + inner.Clear(); + inner.AddRange(engineeringModel.Book); + }); + this.ActiveDomains.Clear(); + this.ActiveDomains.AddRange(engineeringModel.EngineeringModelSetup.ActiveDomain); + + this.AvailableCategories.Clear(); + var categories = engineeringModel.RequiredRdls.SelectMany(x => x.DefinedCategory); + this.AvailableCategories.AddRange(categories); + } + + this.ResetDataToCreate(); this.CreateFakeData(); this.IsLoading = false; } + /// + /// Resets the states for creation modes + /// + public void ResetCreationStates() + { + this.IsOnBookCreation = this.IsOnSectionCreation = this.IsOnPageCreation = this.IsOnNoteCreation = false; + } + + /// + /// Resets the data that is going to be used for creating a book, section or page + /// + private void ResetDataToCreate() + { + this.BookToCreate = new(); + this.SectionToCreate = new(); + this.PageToCreate = new(); + } + /// /// Handler for when the selected book changed /// @@ -144,6 +277,31 @@ private void OnSelectedSectionChanged() this.SelectedPage = null; } + /// + /// Handles the creation of an item + /// + private void OnHandleCreateItem() + { + if (this.IsOnBookCreation) + { + + } + else if (this.IsOnSectionCreation) + { + + } + else if (this.IsOnPageCreation) + { + + } + else if (this.IsOnNoteCreation) + { + + } + + this.ResetCreationStates(); + } + private void CreateFakeData() { var book = new Book diff --git a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs index 1783c67b..856fb0d3 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs @@ -26,10 +26,12 @@ namespace COMETwebapp.ViewModels.Components.BookEditor { using CDP4Common.EngineeringModelData; using CDP4Common.ReportingData; + using CDP4Common.SiteDirectoryData; using COMET.Web.Common.ViewModels.Components; using DynamicData; + using Microsoft.AspNetCore.Components; /// /// ViewModel for the BookEditorBody component @@ -55,5 +57,70 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// Gets or sets the collection of available for this /// SourceList AvailableBooks { get; set; } + + /// + /// Gets or sets the available categories + /// + List AvailableCategories { get; set; } + + /// + /// Gets or sets the active + /// + List ActiveDomains { get; set; } + + /// + /// Gets or sets if the ViewModel is on book creation state + /// + bool IsOnBookCreation { get; set; } + + /// + /// Gets or sets if the ViewModel is on section creation state + /// + bool IsOnSectionCreation { get; set; } + + /// + /// Gets or sets if the ViewModel is on page creation state + /// + bool IsOnPageCreation { get; set; } + + /// + /// Gets or sets if the ViewModel is on node creation state + /// + bool IsOnNoteCreation { get; set; } + + /// + /// Gets or sets the that's about to be created + /// + Book BookToCreate { get; set; } + + /// + /// Gets or sets the that's about to be created + /// + Section SectionToCreate { get; set; } + + /// + /// Gets or sets the that's about to be created + /// + Page PageToCreate { get; set; } + + /// + /// Gets or sets the that's about to be created + /// + Note NoteToCreate { get; set; } + + /// + /// Gets or sets the for when an item is created + /// + EventCallback OnCreateItem { get; set; } + + /// + /// Gets or sets the for when an item has canceled it's creation + /// + EventCallback OnCancelCreateItem { get; set; } + + /// + /// Resets the states for creation modes + /// + void ResetCreationStates(); } } diff --git a/COMETwebapp/wwwroot/index.html b/COMETwebapp/wwwroot/index.html index 5e26c30b..fcabbdd7 100644 --- a/COMETwebapp/wwwroot/index.html +++ b/COMETwebapp/wwwroot/index.html @@ -10,6 +10,7 @@ + From 399ad3530cf81bf54d7fa30522608779110dc630 Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Thu, 14 Sep 2023 15:16:09 +0200 Subject: [PATCH 03/20] Fix css to align the lines --- .../BookEditor/BookEditorBody.razor | 7 ++- .../BookEditor/BookEditorColumn.razor | 28 +++++---- .../BookEditor/BookEditorColumn.razor.cs | 12 ++++ .../BookEditor/BookEditorColumn.razor.css | 61 +++++++++++++------ .../BookEditor/BookEditorBodyViewModel.cs | 5 ++ 5 files changed, 81 insertions(+), 32 deletions(-) diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor b/COMETwebapp/Components/BookEditor/BookEditorBody.razor index 0f522af9..54a040f4 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor @@ -54,7 +54,9 @@ + OnCreateNewItemClick="@(() => this.ViewModel.IsOnBookCreation = true)" + LinesOnRight="true" + HorizontalLineHalfWidth="true"/> + OnCreateNewItemClick="@(() => this.ViewModel.IsOnNoteCreation = true)" + HorizontalLineHalfWidth="true" /> @*
diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor index 0024d3ee..01e79f7a 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor @@ -32,19 +32,25 @@
-
- @if (this.Items.Any()) + @if (this.Items != null && this.Items.Any()) { + + @{ + var conditionalClass = item.Equals(this.SelectedValue) ? "selected" : ""; + var firstClass = item.Equals(this.Items.First()) && this.Items.Count > 1 ? "first" : ""; + var lastClass = item.Equals(this.Items.Last()) && this.Items.Count > 1 ? "last" : ""; + var fullWidthClass = this.HorizontalLineHalfWidth ? "" : "full-width"; + var rightSideClass = this.LinesOnRight ? "right-side" : ""; +
+
+
+ +
+ } +
} - - @{ - var conditionalClass = this.SelectedValue.Equals(item) ? "selected" : ""; - -
- } -
\ No newline at end of file diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs index 877754d1..14f1e44e 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs @@ -33,6 +33,18 @@ namespace COMETwebapp.Components.BookEditor ///
public partial class BookEditorColumn { + /// + /// Gets or sets if the lines should be drawn in the right side + /// + [Parameter] + public bool LinesOnRight { get; set; } + + /// + /// Gets or sets if the first horizontal line should be the half width + /// + [Parameter] + public bool HorizontalLineHalfWidth { get; set; } + /// /// Gets or sets the title of the header /// diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css index 87984a9a..4592ecd5 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css @@ -44,19 +44,21 @@ flex-direction: column; align-items: center; justify-content: flex-start; - row-gap: 10px; padding-top: 2%; position: relative; } -.node-container { +.node-inner-content { + position: relative; + width: 100%; + border: 1px dashed #555; display: flex; - flex-direction: column; - align-items: center; - justify-content: flex-start; + flex-direction:row; + align-items:center; + justify-content:center; } -.book-button, .section-button, .page-button, .note-button { +.book-button { border: 0; box-shadow: 0px 0px 4px rgba(0,0,0,0.2); padding: 5%; @@ -75,29 +77,50 @@ } .vertical-line { + position:absolute; + left:15%; background-color: transparent; - height: 15px; + height: 100%; border: 0; - border-left: 2px dashed #CCC; + border-left: 2px dashed #555; } + .vertical-line.first { + height: 50%; + top: 55%; + transform: translateY(-50%); + } + + .vertical-line.right-side{ + left:85%; + } + + .vertical-line.last { + height: 50%; + top: 5%; + transform: translateY(-50%); + } + .horizontal-line { background-color: transparent; margin: 0; - top: 55px; - left: 0px; position: absolute; - width: 100%; border: 0; border-top: 2px dashed #555; -} - -.left-half-width { - left: 0%; width: 50%; + left: 15%; } -.right-half-width { - left: 50%; - width: 50%; -} + .horizontal-line.first { + width: 50%; + left: 0%; + } + + .horizontal-line.right-side { + left: 50%; + } + + .horizontal-line.first.full-width{ + width:100%; + } + diff --git a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs index 4d2a91d2..b7aa8c1c 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs @@ -338,11 +338,16 @@ private void CreateFakeData() new Section { Name = "Empty Section" + }, + new Section + { + Name = "Empty Section 2" } } }; this.AvailableBooks.Add(book); + this.AvailableBooks.Add(new Book()); } } } From 27f8c3fc1a8e5f33f3c75d9baea807b979233b4e Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Thu, 14 Sep 2023 16:48:03 +0200 Subject: [PATCH 04/20] Fix lines issues and add collapsible panels --- .../wwwroot/css/SVG/icons/arrow-left.svg | 1 + .../wwwroot/css/SVG/icons/arrow-right.svg | 1 + COMET.Web.Common/wwwroot/css/styles.css | 8 ++ .../BookEditor/BookEditorBody.razor | 132 ++++++------------ .../BookEditor/BookEditorBody.razor.cs | 22 +++ .../BookEditor/BookEditorColumn.razor | 10 +- .../BookEditor/BookEditorColumn.razor.cs | 18 +++ .../BookEditor/BookEditorColumn.razor.css | 31 ++-- .../BookEditor/BookEditorBodyViewModel.cs | 2 +- 9 files changed, 122 insertions(+), 103 deletions(-) create mode 100644 COMET.Web.Common/wwwroot/css/SVG/icons/arrow-left.svg create mode 100644 COMET.Web.Common/wwwroot/css/SVG/icons/arrow-right.svg diff --git a/COMET.Web.Common/wwwroot/css/SVG/icons/arrow-left.svg b/COMET.Web.Common/wwwroot/css/SVG/icons/arrow-left.svg new file mode 100644 index 00000000..a5058fc7 --- /dev/null +++ b/COMET.Web.Common/wwwroot/css/SVG/icons/arrow-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/COMET.Web.Common/wwwroot/css/SVG/icons/arrow-right.svg b/COMET.Web.Common/wwwroot/css/SVG/icons/arrow-right.svg new file mode 100644 index 00000000..939b57c5 --- /dev/null +++ b/COMET.Web.Common/wwwroot/css/SVG/icons/arrow-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/COMET.Web.Common/wwwroot/css/styles.css b/COMET.Web.Common/wwwroot/css/styles.css index 35dbfd09..0791ded7 100644 --- a/COMET.Web.Common/wwwroot/css/styles.css +++ b/COMET.Web.Common/wwwroot/css/styles.css @@ -40,6 +40,14 @@ background-image: url(SVG/icons/plus-circle.svg) } +.icon-arrow-left { + background-image: url(SVG/icons/arrow-left.svg) +} + +.icon-arrow-right { + background-image: url(SVG/icons/arrow-right.svg) +} + .logo-size { max-width: 95px; height: auto; diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor b/COMETwebapp/Components/BookEditor/BookEditorBody.razor index 54a040f4..94f3e0a7 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor @@ -26,21 +26,21 @@ @inherits SingleIterationApplicationBase - + @{ - var popupVisible = this.ViewModel.IsOnBookCreation || this.ViewModel.IsOnSectionCreation || this.ViewModel.IsOnPageCreation || this.ViewModel.IsOnNoteCreation; + var popupVisible = this.ViewModel.IsOnBookCreation || this.ViewModel.IsOnSectionCreation || this.ViewModel.IsOnPageCreation || this.ViewModel.IsOnNoteCreation; var parameters = new Dictionary - { - {nameof(BookInput.Book), this.ViewModel.BookToCreate}, - {nameof(BookInput.ActiveDomains), this.ViewModel.ActiveDomains}, - {nameof(BookInput.AvailableCategories), this.ViewModel.AvailableCategories} - }; + { + {nameof(BookInput.Book), this.ViewModel.BookToCreate}, + {nameof(BookInput.ActiveDomains), this.ViewModel.ActiveDomains}, + {nameof(BookInput.AvailableCategories), this.ViewModel.AvailableCategories} + }; var booksColumnConditionalClass = this.ViewModel.SelectedSection != null ? "collapsed" : ""; } - + - +
+ + HorizontalLineHalfWidth="true" + IsCollapsed="@this.IsBooksColumnCollapsed" + CollapseButtonIconClass="no-display"> + + @context.Name + + + OnCreateNewItemClick="@(() => this.ViewModel.IsOnSectionCreation = true)" + IsCollapsed="@this.IsSectionColumnCollapsed" + OnCollapseClicked="@(() => this.IsBooksColumnCollapsed = !this.IsBooksColumnCollapsed)" + CollapseButtonIconClass="@(this.IsBooksColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")"> + + @context.Name + + + OnCreateNewItemClick="@(() => this.ViewModel.IsOnPageCreation = true)" + IsCollapsed="@this.IsPageColumnCollapsed" + OnCollapseClicked="@(() => this.IsSectionColumnCollapsed = !this.IsSectionColumnCollapsed)" + CollapseButtonIconClass="@(this.IsSectionColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")"> + + @context.Name + + - -@*
-
-
Books
- -
-
-
- @foreach (var book in this.ViewModel.AvailableBooks.Items) - { - var conditionalClass = this.ViewModel.SelectedBook == book ? "selected" : ""; - -
- } -
-
-
-
-
Sections
- -
-
- @if (this.ViewModel.SelectedBook != null && this.ViewModel.SelectedBook.Section.Any()) - { -
- foreach (var sectionValue in this.ViewModel.SelectedBook.Section.ToList()) - { - var conditionalClass = this.ViewModel.SelectedSection == sectionValue ? "selected" : ""; - -
- } - } -
-
-
-
-
Pages
- -
-
- @if (this.ViewModel.SelectedSection != null && this.ViewModel.SelectedSection.Page.Any()) - { -
- foreach (var pageValue in this.ViewModel.SelectedSection.Page.ToList()) - { - var conditionalClass = this.ViewModel.SelectedPage == pageValue ? "selected" : ""; - -
- } - } -
-
-
-
-
Notes
- -
-
- @if (this.ViewModel.SelectedPage != null && this.ViewModel.SelectedPage.Note.Any()) - { -
- foreach (var note in this.ViewModel.SelectedPage.Note.ToList()) - { - -
- } - } -
-
*@ + HorizontalLineHalfWidth="true" + OnCollapseClicked="@(() => this.IsPageColumnCollapsed = !this.IsPageColumnCollapsed)" + CollapseButtonIconClass="@(this.IsPageColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")"> + + @context.Name + +
\ No newline at end of file diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs index d0828ba9..1cf877e7 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs @@ -22,6 +22,8 @@ // // -------------------------------------------------------------------------------------------------------------------- +using ReactiveUI; + namespace COMETwebapp.Components.BookEditor { /// @@ -29,6 +31,12 @@ namespace COMETwebapp.Components.BookEditor /// public partial class BookEditorBody { + private bool IsBooksColumnCollapsed { get; set; } + + private bool IsSectionColumnCollapsed { get; set; } + + private bool IsPageColumnCollapsed { get; set; } + /// /// Initializes values of the component and of the ViewModel based on parameters provided from the url /// @@ -37,6 +45,20 @@ protected override void InitializeValues(Dictionary parameters) { } + /// + /// Method invoked when the component is ready to start, having received its + /// initial parameters from its parent in the render tree. + /// + protected override void OnInitialized() + { + base.OnInitialized(); + this.Disposables.Add(this.WhenAnyValue(x => x.ViewModel.IsOnBookCreation, + x => x.ViewModel.IsOnSectionCreation, + x => x.ViewModel.IsOnPageCreation, + x => x.ViewModel.IsOnNoteCreation) + .Subscribe(_ => this.InvokeAsync(this.StateHasChanged))); + } + /// /// Gets the text of the header depending on the state of the ViewModel /// diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor index 01e79f7a..dbc8052b 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor @@ -28,6 +28,7 @@
+
@this.HeaderTitle
@@ -46,7 +47,14 @@

} diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs index 14f1e44e..24b14c15 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs @@ -33,6 +33,12 @@ namespace COMETwebapp.Components.BookEditor /// public partial class BookEditorColumn { + /// + /// Gets or sets the class to use for the collapse button + /// + [Parameter] + public string CollapseButtonIconClass { get; set; } + /// /// Gets or sets if the lines should be drawn in the right side /// @@ -87,6 +93,18 @@ public partial class BookEditorColumn [Parameter] public EventCallback OnCreateNewItemClick { get; set; } + /// + /// Gets or sets the for when the user request to collapse the column + /// + [Parameter] + public EventCallback OnCollapseClicked { get; set; } + + /// + /// Gets or sets the content template to render if any + /// + [Parameter] + public RenderFragment ContentTemplate { get; set; } + /// /// Hanlder for when the selected value changes /// diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css index 4592ecd5..09779d9d 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css @@ -1,4 +1,8 @@ -.book-editor-column { +.no-display{ + display:none; +} + +.book-editor-column { width: 100%; display: flex; flex-direction: column; @@ -6,16 +10,21 @@ border: 0px dashed #555; padding: 5% 0%; transition: all 1s; + box-shadow: 0px 0px 4px rgba(0,0,0,0.2); } .book-editor-column.collapsed { width: 0%; } + .book-editor-column.collapsed div{ + display:none; + } + .column-header { display: flex; align-items: center; - justify-content: center; + justify-content: space-around; column-gap: 5px; } @@ -30,7 +39,7 @@ color: white; } -.add-item-button { +.add-item-button,.collapse-button { border: 0; background-color: transparent; } @@ -51,11 +60,12 @@ .node-inner-content { position: relative; width: 100%; - border: 1px dashed #555; + border: 0; display: flex; flex-direction:row; align-items:center; justify-content:center; + padding:2%; } .book-button { @@ -78,7 +88,7 @@ .vertical-line { position:absolute; - left:15%; + left:10%; background-color: transparent; height: 100%; border: 0; @@ -87,8 +97,7 @@ .vertical-line.first { height: 50%; - top: 55%; - transform: translateY(-50%); + transform: translateY(50%); } .vertical-line.right-side{ @@ -97,7 +106,6 @@ .vertical-line.last { height: 50%; - top: 5%; transform: translateY(-50%); } @@ -108,7 +116,7 @@ border: 0; border-top: 2px dashed #555; width: 50%; - left: 15%; + left: 10%; } .horizontal-line.first { @@ -117,10 +125,13 @@ } .horizontal-line.right-side { - left: 50%; + left: 35%; } .horizontal-line.first.full-width{ width:100%; } + .horizontal-line.first.right-side { + left: 50%; + } diff --git a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs index b7aa8c1c..4b0e9d87 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs @@ -347,7 +347,7 @@ private void CreateFakeData() }; this.AvailableBooks.Add(book); - this.AvailableBooks.Add(new Book()); + this.AvailableBooks.Add(new Book(){Name = "Book 2"}); } } } From b95db190d60220e06b1e4d158506b93e3302c77b Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Fri, 15 Sep 2023 09:17:55 +0200 Subject: [PATCH 05/20] Add Section and Page Input components --- .../Components/BookEditor/BookInput.razor.cs | 4 ++ .../Components/BookEditor/PageInput.razor | 62 ++++++++++++++++++ .../Components/BookEditor/PageInput.razor.cs | 65 +++++++++++++++++++ .../Components/BookEditor/PageInput.razor.css | 18 +++++ .../Components/BookEditor/SectionInput.razor | 62 ++++++++++++++++++ .../BookEditor/SectionInput.razor.cs | 65 +++++++++++++++++++ .../BookEditor/SectionInput.razor.css | 18 +++++ .../BookEditor/BookEditorBody.razor | 18 ++--- .../BookEditor/BookEditorBody.razor.cs | 48 ++++++++++++-- .../BookEditor/BookEditorBodyViewModel.cs | 17 +++-- 10 files changed, 358 insertions(+), 19 deletions(-) create mode 100644 COMET.Web.Common/Components/BookEditor/PageInput.razor create mode 100644 COMET.Web.Common/Components/BookEditor/PageInput.razor.cs create mode 100644 COMET.Web.Common/Components/BookEditor/PageInput.razor.css create mode 100644 COMET.Web.Common/Components/BookEditor/SectionInput.razor create mode 100644 COMET.Web.Common/Components/BookEditor/SectionInput.razor.cs create mode 100644 COMET.Web.Common/Components/BookEditor/SectionInput.razor.css diff --git a/COMET.Web.Common/Components/BookEditor/BookInput.razor.cs b/COMET.Web.Common/Components/BookEditor/BookInput.razor.cs index 9007d36d..1593097b 100644 --- a/COMET.Web.Common/Components/BookEditor/BookInput.razor.cs +++ b/COMET.Web.Common/Components/BookEditor/BookInput.razor.cs @@ -53,6 +53,10 @@ public partial class BookInput [Parameter] public IEnumerable AvailableCategories { get; set; } + /// + /// Handler for when the selected categories changed + /// + /// private void OnCategoryChange(IEnumerable categories) { this.Book.Category = categories.ToList(); diff --git a/COMET.Web.Common/Components/BookEditor/PageInput.razor b/COMET.Web.Common/Components/BookEditor/PageInput.razor new file mode 100644 index 00000000..757b1a85 --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/PageInput.razor @@ -0,0 +1,62 @@ + +@namespace COMET.Web.Common.Components.BookEditor +@using CDP4Common.SiteDirectoryData +@inherits DisposableComponent + + + +
+
+

Short Name:

+ +
+
+

Name:

+ +
+
+

Owner:

+ +
+
+
+ +
+ + +
+
+
\ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/PageInput.razor.cs b/COMET.Web.Common/Components/BookEditor/PageInput.razor.cs new file mode 100644 index 00000000..3f83f7d5 --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/PageInput.razor.cs @@ -0,0 +1,65 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.Components.BookEditor +{ + using CDP4Common.ReportingData; + using CDP4Common.SiteDirectoryData; + + using Microsoft.AspNetCore.Components; + + /// + /// Support class for the PageInput component + /// + public partial class PageInput + { + /// + /// Gets or sets the page for which the input is being provided + /// + [Parameter] + public Page Page { get; set; } + + /// + /// Gets or sets the active + /// + [Parameter] + public IEnumerable ActiveDomains { get; set; } + + /// + /// Gets or sets the collection of available + /// + [Parameter] + public IEnumerable AvailableCategories { get; set; } + + /// + /// Handler for when the selected categories changed + /// + /// + private void OnCategoryChange(IEnumerable categories) + { + this.Page.Category = categories.ToList(); + } + } +} diff --git a/COMET.Web.Common/Components/BookEditor/PageInput.razor.css b/COMET.Web.Common/Components/BookEditor/PageInput.razor.css new file mode 100644 index 00000000..8a530845 --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/PageInput.razor.css @@ -0,0 +1,18 @@ +.tab-content { + display: flex; + flex-direction: column; + align-items: start; + justify-content: flex-start; + row-gap: 5px; + padding:1%; +} + +.editor-row{ + align-items:center; + justify-content:space-between; +} + +.editor-row>p{ + width: 25%; + margin:0; +} \ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/SectionInput.razor b/COMET.Web.Common/Components/BookEditor/SectionInput.razor new file mode 100644 index 00000000..bbe63166 --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/SectionInput.razor @@ -0,0 +1,62 @@ + +@namespace COMET.Web.Common.Components.BookEditor +@using CDP4Common.SiteDirectoryData +@inherits DisposableComponent + + + +
+
+

Short Name:

+ +
+
+

Name:

+ +
+
+

Owner:

+ +
+
+
+ +
+ + +
+
+
\ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/SectionInput.razor.cs b/COMET.Web.Common/Components/BookEditor/SectionInput.razor.cs new file mode 100644 index 00000000..8f9f261b --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/SectionInput.razor.cs @@ -0,0 +1,65 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.Components.BookEditor +{ + using CDP4Common.ReportingData; + using CDP4Common.SiteDirectoryData; + + using Microsoft.AspNetCore.Components; + + /// + /// Support class for the SectionInput component + /// + public partial class SectionInput + { + /// + /// Gets or sets the section for which the input is being provided + /// + [Parameter] + public Section Section { get; set; } + + /// + /// Gets or sets the active + /// + [Parameter] + public IEnumerable ActiveDomains { get; set; } + + /// + /// Gets or sets the collection of available + /// + [Parameter] + public IEnumerable AvailableCategories { get; set; } + + /// + /// Handler for when the selected categories changed + /// + /// + private void OnCategoryChange(IEnumerable categories) + { + this.Section.Category = categories.ToList(); + } + } +} diff --git a/COMET.Web.Common/Components/BookEditor/SectionInput.razor.css b/COMET.Web.Common/Components/BookEditor/SectionInput.razor.css new file mode 100644 index 00000000..8a530845 --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/SectionInput.razor.css @@ -0,0 +1,18 @@ +.tab-content { + display: flex; + flex-direction: column; + align-items: start; + justify-content: flex-start; + row-gap: 5px; + padding:1%; +} + +.editor-row{ + align-items:center; + justify-content:space-between; +} + +.editor-row>p{ + width: 25%; + margin:0; +} \ No newline at end of file diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor b/COMETwebapp/Components/BookEditor/BookEditorBody.razor index 94f3e0a7..e6296c66 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor @@ -28,19 +28,21 @@ @{ - var popupVisible = this.ViewModel.IsOnBookCreation || this.ViewModel.IsOnSectionCreation || this.ViewModel.IsOnPageCreation || this.ViewModel.IsOnNoteCreation; + var popupVisible = this.ViewModel.IsOnBookCreation || this.ViewModel.IsOnSectionCreation || this.ViewModel.IsOnPageCreation || this.ViewModel.IsOnNoteCreation; + + var parameter = this.GetDynamicComponentParameter(); + var parameters = new Dictionary - { - {nameof(BookInput.Book), this.ViewModel.BookToCreate}, - {nameof(BookInput.ActiveDomains), this.ViewModel.ActiveDomains}, - {nameof(BookInput.AvailableCategories), this.ViewModel.AvailableCategories} - }; - var booksColumnConditionalClass = this.ViewModel.SelectedSection != null ? "collapsed" : ""; + { + {parameter.key, parameter.item}, + {"ActiveDomains", this.ViewModel.ActiveDomains}, + {"AvailableCategories", this.ViewModel.AvailableCategories} + }; } - +
\ No newline at end of file diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs index 24b14c15..41484ed2 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs @@ -24,7 +24,9 @@ namespace COMETwebapp.Components.BookEditor { - using System.Drawing; + using CDP4JsonSerializer.JsonConverter; + + using COMETwebapp.Services.Interoperability; using Microsoft.AspNetCore.Components; @@ -33,6 +35,12 @@ namespace COMETwebapp.Components.BookEditor /// public partial class BookEditorColumn { + /// + /// Gets or sets the + /// + [Inject] + public IDomDataService DomDataService { get; set; } + /// /// Gets or sets the class to use for the collapse button /// @@ -105,14 +113,60 @@ public partial class BookEditorColumn [Parameter] public RenderFragment ContentTemplate { get; set; } + private float[] firstItemSizeAndPosition = {}; + + private float[] sizeAndPosition = {}; + + [Parameter] + public string CssClass { get; set; } + /// /// Hanlder for when the selected value changes /// /// the item selected /// an asynchronous operation - private async Task OnSelectedValueChanged(TItem item) + private async Task OnSelectedValueChanged(TItem item, int itemIndex) + { + try + { + this.firstItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(0, this.CssClass); + this.sizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(itemIndex, this.CssClass); + await this.SelectedValueChanged.InvokeAsync(item); + } + catch (Exception ex) + { + + } + } + + /// + /// Generate the path points for the polyline + /// + /// the path + private string GeneratePathPoints() { - await this.SelectedValueChanged.InvokeAsync(item); + if (this.sizeAndPosition.Length < 4 || this.firstItemSizeAndPosition.Length < 4) + { + return string.Empty; + } + + var left = this.sizeAndPosition[0]; + var top = this.sizeAndPosition[1]; + var width = this.sizeAndPosition[2]; + var height = this.sizeAndPosition[3]; + + var finalTop = (int)(this.firstItemSizeAndPosition[1] + this.firstItemSizeAndPosition[3] / 2.0f); + + var x1 = (int)(width*0.6); + var y1 = (int)(top + height/2.0f); + var x2 = (int)(width*0.9); + var y2 = (int)(top + height / 2.0f); + var x3 = (int)(width*0.9); + var y3 = finalTop; + var x4 = (int)(width); + var y4 = finalTop; + + return $"{x1},{y1},{x2},{y2},{x3},{y3},{x4},{y4}"; } } } diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css index 09779d9d..3f0b636b 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css @@ -6,7 +6,7 @@ width: 100%; display: flex; flex-direction: column; - min-height: 85vh; + max-height: 85vh; border: 0px dashed #555; padding: 5% 0%; transition: all 1s; @@ -129,9 +129,39 @@ } .horizontal-line.first.full-width{ - width:100%; + width:50%; } .horizontal-line.first.right-side { left: 50%; } + +.edit-node-container{ + display: flex; + flex-direction: column; + position:absolute; + right: 20%; + background-color: #EEE; + border-radius:5px; + overflow:hidden; + padding: 2px; + row-gap: 1px; +} + +.edit-node-container > button{ + border: 0; + background-color: transparent; +} + +.edit-node-container > button:hover{ + background-color: #CCC; +} + +.svg-path{ + position:absolute; + top: 0%; + left:0%; + width:100%; + height:100%; + z-index:-1; +} \ No newline at end of file diff --git a/COMETwebapp/Program.cs b/COMETwebapp/Program.cs index 3b430917..9546ef2c 100644 --- a/COMETwebapp/Program.cs +++ b/COMETwebapp/Program.cs @@ -92,6 +92,7 @@ public static void RegisterServices(WebAssemblyHostBuilder builder) builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddAntDesign(); } diff --git a/COMETwebapp/Services/Interoperability/BabylonInterop.cs b/COMETwebapp/Services/Interoperability/BabylonInterop.cs index 711b1973..101ae4a2 100644 --- a/COMETwebapp/Services/Interoperability/BabylonInterop.cs +++ b/COMETwebapp/Services/Interoperability/BabylonInterop.cs @@ -34,20 +34,14 @@ namespace COMETwebapp.Services.Interoperability /// /// Class used for calling the babylon.js methods /// - public class BabylonInterop : IBabylonInterop + public class BabylonInterop : InteroperabilityService, IBabylonInterop { - /// - /// Gets or sets the see - /// - public IJSRuntime JsRuntime { get;} - /// /// Creates a new instance of type /// - /// the - public BabylonInterop(IJSRuntime jsRuntime) + /// the + public BabylonInterop(IJSRuntime jsRuntime) : base(jsRuntime) { - this.JsRuntime = jsRuntime; } /// diff --git a/COMETwebapp/Services/Interoperability/DomDataService.cs b/COMETwebapp/Services/Interoperability/DomDataService.cs new file mode 100644 index 00000000..0cdc6224 --- /dev/null +++ b/COMETwebapp/Services/Interoperability/DomDataService.cs @@ -0,0 +1,54 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Justine Veirier d'aiguebonne, Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.Services.Interoperability +{ + using Microsoft.AspNetCore.Components; + using Microsoft.JSInterop; + + /// + /// The service used to retrieve several data from the DOM + /// + public class DomDataService : InteroperabilityService, IDomDataService + { + /// + /// Creates a new instance of + /// + /// the + public DomDataService(IJSRuntime jsRuntime) : base(jsRuntime) + { + } + + /// + /// Gets an element size and position + /// + /// the index of the element to search + /// the selector to use to select the items + /// the size and position + public async Task GetElementSizeAndPosition(int elementIndex, string cssSelector) + { + return await this.JsRuntime.InvokeAsync("GetElementSizeAndPosition", elementIndex, cssSelector); + } + } +} diff --git a/COMETwebapp/Services/Interoperability/DraggableElementService.cs b/COMETwebapp/Services/Interoperability/DraggableElementService.cs index 80f39118..a9c8e0ae 100644 --- a/COMETwebapp/Services/Interoperability/DraggableElementService.cs +++ b/COMETwebapp/Services/Interoperability/DraggableElementService.cs @@ -31,20 +31,14 @@ namespace COMETwebapp.Services.Interoperability /// /// Class used for calling the draggableElementGrid.js methods /// - public class DraggableElementService : IDraggableElementService + public class DraggableElementService : InteroperabilityService, IDraggableElementService { - /// - /// Gets or sets the see - /// - public IJSRuntime JsRuntime { get; } - /// /// Creates a new instance of type /// - /// the - public DraggableElementService(IJSRuntime jsRuntime) + /// the + public DraggableElementService(IJSRuntime jsRuntime) : base(jsRuntime) { - this.JsRuntime = jsRuntime; } /// @@ -54,7 +48,7 @@ public DraggableElementService(IJSRuntime jsRuntime) /// A public async Task LoadDotNetHelper(DotNetObjectReference dotNetHelper) { - await JsRuntime.InvokeVoidAsync("setDotNetHelper", dotNetHelper); + await this.JsRuntime.InvokeVoidAsync("setDotNetHelper", dotNetHelper); } /// @@ -65,7 +59,7 @@ public async Task LoadDotNetHelper(DotNetObjectReference /// A public async Task InitDraggableGrids(string firstGrid, string secondGrid) { - await JsRuntime.InvokeVoidAsync("initialize", firstGrid, secondGrid); + await this.JsRuntime.InvokeVoidAsync("initialize", firstGrid, secondGrid); } } } diff --git a/COMETwebapp/Services/Interoperability/IBabylonInterop.cs b/COMETwebapp/Services/Interoperability/IBabylonInterop.cs index e1cc34b8..a4d44c5f 100644 --- a/COMETwebapp/Services/Interoperability/IBabylonInterop.cs +++ b/COMETwebapp/Services/Interoperability/IBabylonInterop.cs @@ -27,18 +27,12 @@ namespace COMETwebapp.Services.Interoperability using COMETwebapp.Model; using Microsoft.AspNetCore.Components; - using Microsoft.JSInterop; /// /// The is used to call the babylon.js methods /// public interface IBabylonInterop { - /// - /// Gets or sets the see - /// - IJSRuntime JsRuntime { get; } - /// /// Initializes the canvas with the Babylon.js engine /// diff --git a/COMETwebapp/Services/Interoperability/IDomDataService.cs b/COMETwebapp/Services/Interoperability/IDomDataService.cs new file mode 100644 index 00000000..c0557859 --- /dev/null +++ b/COMETwebapp/Services/Interoperability/IDomDataService.cs @@ -0,0 +1,42 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Justine Veirier d'aiguebonne, Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.Services.Interoperability +{ + using Microsoft.AspNetCore.Components; + + /// + /// The service used to retrieve several data from the DOM + /// + public interface IDomDataService + { + /// + /// Gets an element size and position + /// + /// the index of the element to search + /// the selector to use to select the items + /// the size and position + Task GetElementSizeAndPosition(int elementIndex, string cssSelector); + } +} diff --git a/COMETwebapp/Services/Interoperability/IDraggableElementService.cs b/COMETwebapp/Services/Interoperability/IDraggableElementService.cs index 981f6512..557228ba 100644 --- a/COMETwebapp/Services/Interoperability/IDraggableElementService.cs +++ b/COMETwebapp/Services/Interoperability/IDraggableElementService.cs @@ -33,11 +33,6 @@ namespace COMETwebapp.Services.Interoperability /// public interface IDraggableElementService { - /// - /// Gets or sets the see - /// - IJSRuntime JsRuntime { get; } - /// /// Set the dotnet helper /// diff --git a/COMETwebapp/Services/Interoperability/InteroperabilityService.cs b/COMETwebapp/Services/Interoperability/InteroperabilityService.cs new file mode 100644 index 00000000..614ba6ca --- /dev/null +++ b/COMETwebapp/Services/Interoperability/InteroperabilityService.cs @@ -0,0 +1,48 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Justine Veirier d'aiguebonne, Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.Services.Interoperability +{ + using Microsoft.JSInterop; + + /// + /// Base service for services that need to invoke JS + /// + public abstract class InteroperabilityService + { + /// + /// Gets or sets the see + /// + protected IJSRuntime JsRuntime { get; } + + /// + /// Creates a new instance of type + /// + /// the + protected InteroperabilityService(IJSRuntime jsRuntime) + { + this.JsRuntime = jsRuntime; + } + } +} diff --git a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs index 8dfc5eb7..a73ced0a 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs @@ -24,7 +24,6 @@ namespace COMETwebapp.ViewModels.Components.BookEditor { - using CDP4Common.CommonData; using CDP4Common.EngineeringModelData; using CDP4Common.ReportingData; using CDP4Common.SiteDirectoryData; @@ -239,7 +238,7 @@ protected override async Task OnIterationChanged() } this.ResetDataToCreate(); - //this.CreateFakeData(); + this.CreateFakeData(); this.IsLoading = false; } @@ -286,9 +285,9 @@ private async Task OnHandleCreateItem() if (this.IsOnBookCreation) { var engineeringModel = this.CurrentIteration.Container as EngineeringModel; - var engineeringModelClone = engineeringModel?.Clone(false); - this.BookToCreate.Container = engineeringModel; - // engineeringModelClone?.Book.Add(this.BookToCreate); + //this.BookToCreate.Container = engineeringModel; + var engineeringModelClone = engineeringModel.Clone(false); + //engineeringModelClone?.Book.Add(this.BookToCreate); await this.SessionService.CreateThing(engineeringModelClone, this.BookToCreate); } else if (this.IsOnSectionCreation) @@ -341,12 +340,51 @@ private void CreateFakeData() } }, new Section + { + Name = "Section 2", + Page = + { + new Page + { + Name = "Page" + }, + new Page + { + Name = "Page 2" + } + } + }, + new Section { Name = "Empty Section" }, new Section { - Name = "Empty Section 2" + Name = "Empty Section" + }, + new Section + { + Name = "Empty Section" + }, + new Section + { + Name = "Empty Section" + }, + new Section + { + Name = "Empty Section" + }, + new Section + { + Name = "Empty Section" + }, + new Section + { + Name = "Empty Section" + }, + new Section + { + Name = "Empty Section" } } }; diff --git a/COMETwebapp/wwwroot/Scripts/DomData.js b/COMETwebapp/wwwroot/Scripts/DomData.js new file mode 100644 index 00000000..e73b510d --- /dev/null +++ b/COMETwebapp/wwwroot/Scripts/DomData.js @@ -0,0 +1,16 @@ +function GetElementSizeAndPosition(index, cssSelector) { + + var elements = document.getElementsByClassName(cssSelector); + var element = elements[index]; + console.log(elements); + if (element != null) { + try { + console.log(element); + return [element.offsetLeft, element.offsetTop, element.offsetWidth, element.offsetHeight]; + } catch (error) { + return [0, 0, 0, 0]; + } + } + console.log("Element not found"); + return [0, 0, 0, 0]; +} \ No newline at end of file diff --git a/COMETwebapp/wwwroot/index.html b/COMETwebapp/wwwroot/index.html index fcabbdd7..f76a344b 100644 --- a/COMETwebapp/wwwroot/index.html +++ b/COMETwebapp/wwwroot/index.html @@ -79,6 +79,7 @@ + From 55c68e945b14081c21419cdd2d81e611931c70e2 Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Tue, 19 Sep 2023 09:57:59 +0200 Subject: [PATCH 07/20] Remove projects dependencies --- COMET.Web.Common/COMET.Web.Common.csproj | 5 +- COMETwebapp.sln | 12 --- .../BookEditor/BookEditorBody.razor | 4 +- .../BookEditor/BookEditorColumn.razor | 92 +++++++++++-------- .../BookEditor/BookEditorColumn.razor.cs | 67 +++++++++----- .../BookEditor/BookEditorColumn.razor.css | 29 +++++- .../Interoperability/DomDataService.cs | 6 +- .../Interoperability/IDomDataService.cs | 5 +- .../BookEditor/BookEditorBodyViewModel.cs | 2 +- COMETwebapp/wwwroot/Scripts/DomData.js | 28 ++++-- 10 files changed, 153 insertions(+), 97 deletions(-) diff --git a/COMET.Web.Common/COMET.Web.Common.csproj b/COMET.Web.Common/COMET.Web.Common.csproj index a05facb8..26b8a489 100644 --- a/COMET.Web.Common/COMET.Web.Common.csproj +++ b/COMET.Web.Common/COMET.Web.Common.csproj @@ -27,7 +27,7 @@ - + @@ -53,7 +53,4 @@ - - - \ No newline at end of file diff --git a/COMETwebapp.sln b/COMETwebapp.sln index f42c51b1..789e8065 100644 --- a/COMETwebapp.sln +++ b/COMETwebapp.sln @@ -31,10 +31,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{ .github\workflows\publish-docker-container.yml = .github\workflows\publish-docker-container.yml EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CDP4Common", "..\COMET-SDK-Community-Edition\CDP4Common\CDP4Common.csproj", "{3BB827A8-4C73-4746-A898-50E09C5A6237}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CDP4ServicesDal", "..\COMET-SDK-Community-Edition\CDP4ServicesDal\CDP4ServicesDal.csproj", "{F1DE6907-1F53-43BB-95D0-8344C63D3F8D}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -59,14 +55,6 @@ Global {C1BEF589-7A99-4B95-A036-471259115495}.Debug|Any CPU.Build.0 = Debug|Any CPU {C1BEF589-7A99-4B95-A036-471259115495}.Release|Any CPU.ActiveCfg = Release|Any CPU {C1BEF589-7A99-4B95-A036-471259115495}.Release|Any CPU.Build.0 = Release|Any CPU - {3BB827A8-4C73-4746-A898-50E09C5A6237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3BB827A8-4C73-4746-A898-50E09C5A6237}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3BB827A8-4C73-4746-A898-50E09C5A6237}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3BB827A8-4C73-4746-A898-50E09C5A6237}.Release|Any CPU.Build.0 = Release|Any CPU - {F1DE6907-1F53-43BB-95D0-8344C63D3F8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1DE6907-1F53-43BB-95D0-8344C63D3F8D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1DE6907-1F53-43BB-95D0-8344C63D3F8D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1DE6907-1F53-43BB-95D0-8344C63D3F8D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor b/COMETwebapp/Components/BookEditor/BookEditorBody.razor index ce7e5a60..97f6428e 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor @@ -58,8 +58,7 @@ Items="@this.ViewModel.AvailableBooks.Items.ToList()" @bind-SelectedValue="@this.ViewModel.SelectedBook" OnCreateNewItemClick="@(() => this.ViewModel.IsOnBookCreation = true)" - LinesOnRight="true" - HorizontalLineHalfWidth="true" + DrawLeftLines="false" IsCollapsed="@this.IsBooksColumnCollapsed" CollapseButtonIconClass="no-display" CssClass="book-nodes"> @@ -97,7 +96,6 @@ diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor index 7a5416d9..629d0fe5 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor @@ -36,52 +36,68 @@
@if (this.Items != null && this.Items.Any()) { - - @{ - var isSelected = item.Equals(this.SelectedValue); - var conditionalClass = isSelected ? "selected" : ""; - var firstClass = item.Equals(this.Items.First()) && this.Items.Count > 1 ? "first" : ""; - var lastClass = item.Equals(this.Items.Last()) && this.Items.Count > 1 ? "last" : ""; +
- var fullWidthClass = this.HorizontalLineHalfWidth ? "" : "full-width"; - var rightSideClass = this.LinesOnRight ? "right-side" : ""; + + @{ + var isSelected = item.Equals(this.SelectedValue); + var isFirst = item.Equals(this.Items.First()) && this.Items.Count > 1; + var isLast = item.Equals(this.Items.Last()) && this.Items.Count > 1; + var conditionalClass = isSelected ? "selected" : ""; + var firstClass = isFirst ? "first" : ""; + var lastClass = isLast ? "last" : ""; - var index = this.Items.IndexOf(item); + var index = this.Items.IndexOf(item); -
- @if (!this.LinesOnRight) - { -
-
- } - + + @if (isSelected) { - @item.ToString() +
+ + + + +
} - +
+ } +
+ +
- @if (isSelected) - { -
- - - - -
- } -
- } - + + + } - - -
\ No newline at end of file diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs index 41484ed2..50a753a0 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs @@ -24,10 +24,10 @@ namespace COMETwebapp.Components.BookEditor { - using CDP4JsonSerializer.JsonConverter; - using COMETwebapp.Services.Interoperability; + using DynamicData; + using Microsoft.AspNetCore.Components; /// @@ -48,17 +48,11 @@ public partial class BookEditorColumn public string CollapseButtonIconClass { get; set; } /// - /// Gets or sets if the lines should be drawn in the right side - /// - [Parameter] - public bool LinesOnRight { get; set; } - - /// - /// Gets or sets if the first horizontal line should be the half width + /// Gets or sets if the lines should be drawn in the left side or not drawn at all /// [Parameter] - public bool HorizontalLineHalfWidth { get; set; } - + public bool DrawLeftLines { get; set; } = true; + /// /// Gets or sets the title of the header /// @@ -119,24 +113,29 @@ public partial class BookEditorColumn [Parameter] public string CssClass { get; set; } - + /// /// Hanlder for when the selected value changes /// /// the item selected + /// the index of the item selected /// an asynchronous operation private async Task OnSelectedValueChanged(TItem item, int itemIndex) { - try - { - this.firstItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(0, this.CssClass); - this.sizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(itemIndex, this.CssClass); - await this.SelectedValueChanged.InvokeAsync(item); - } - catch (Exception ex) - { + this.firstItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(0, this.CssClass, false); + this.sizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(itemIndex, this.CssClass, true); + await this.SelectedValueChanged.InvokeAsync(item); + } - } + /// + /// Callback method called from JS when an element performs scroll + /// + /// + public async Task OnScroll() + { + var itemIndex = this.Items.IndexOf(this.SelectedValue); + this.sizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(itemIndex, this.CssClass, true); + await this.InvokeAsync(this.StateHasChanged); } /// @@ -149,7 +148,7 @@ private string GeneratePathPoints() { return string.Empty; } - + var left = this.sizeAndPosition[0]; var top = this.sizeAndPosition[1]; var width = this.sizeAndPosition[2]; @@ -168,5 +167,29 @@ private string GeneratePathPoints() return $"{x1},{y1},{x2},{y2},{x3},{y3},{x4},{y4}"; } + + private (string verticalPath, string horizontalPath) GenerateLeftPathPoints(bool isFirst, bool isLast) + { + if (this.sizeAndPosition.Length < 4 || this.firstItemSizeAndPosition.Length < 4) + { + return (string.Empty, string.Empty); + } + + var top = this.sizeAndPosition[1]; + var width = this.sizeAndPosition[2]; + var height = this.sizeAndPosition[3]; + + var x1 = (int)(width*0.1); + var y1 = (int)(top); + var x2 = (int)(width*0.1); + var y2 = (int)(top + height); + + var x3 = (int)(width*0.0); + var y3 = (int)(top + height / 2.0f); + var x4 = (int)(width*0.2); + var y4 = (int)(top + height / 2.0f); + + return ($"{x1},{y1},{x2},{y2}",$"{x3},{y3},{x4},{y4}"); + } } } diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css index 3f0b636b..13ac4e56 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css @@ -8,7 +8,7 @@ flex-direction: column; max-height: 85vh; border: 0px dashed #555; - padding: 5% 0%; + padding: 2.5% 0%; transition: all 1s; box-shadow: 0px 0px 4px rgba(0,0,0,0.2); } @@ -48,7 +48,6 @@ .column-content { width: 100%; height: 100%; - overflow-y: auto; display: flex; flex-direction: column; align-items: center; @@ -57,6 +56,16 @@ position: relative; } +.column-scroll-content { + width: 100%; + height: 100%; + overflow-y: auto; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; +} + .node-inner-content { position: relative; width: 100%; @@ -78,7 +87,7 @@ z-index: 1; } - .book-button:hover, .section-button:hover, .page-button:hover, .note-button:hover { + .book-button:hover { box-shadow: 0px 0px 0px 2px rgba(0,0,0,0.2); } @@ -136,6 +145,20 @@ left: 50%; } + .horizontal-line.first-slice { + display:none; + width: 50%; + left: 0%; + border-top: 2px dashed red; + } + + .horizontal-line.first.first-slice { + display:block; + width: 20%; + left: 0%; + border-top: 2px dashed red; + } + .edit-node-container{ display: flex; flex-direction: column; diff --git a/COMETwebapp/Services/Interoperability/DomDataService.cs b/COMETwebapp/Services/Interoperability/DomDataService.cs index 0cdc6224..895c820c 100644 --- a/COMETwebapp/Services/Interoperability/DomDataService.cs +++ b/COMETwebapp/Services/Interoperability/DomDataService.cs @@ -24,7 +24,6 @@ namespace COMETwebapp.Services.Interoperability { - using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; /// @@ -45,10 +44,11 @@ public DomDataService(IJSRuntime jsRuntime) : base(jsRuntime) /// /// the index of the element to search /// the selector to use to select the items + /// If the scroll must be taken into account for the calculations /// the size and position - public async Task GetElementSizeAndPosition(int elementIndex, string cssSelector) + public async Task GetElementSizeAndPosition(int elementIndex, string cssSelector, bool useScroll) { - return await this.JsRuntime.InvokeAsync("GetElementSizeAndPosition", elementIndex, cssSelector); + return await this.JsRuntime.InvokeAsync("GetElementSizeAndPosition", elementIndex, cssSelector, useScroll); } } } diff --git a/COMETwebapp/Services/Interoperability/IDomDataService.cs b/COMETwebapp/Services/Interoperability/IDomDataService.cs index c0557859..4d9457eb 100644 --- a/COMETwebapp/Services/Interoperability/IDomDataService.cs +++ b/COMETwebapp/Services/Interoperability/IDomDataService.cs @@ -24,8 +24,6 @@ namespace COMETwebapp.Services.Interoperability { - using Microsoft.AspNetCore.Components; - /// /// The service used to retrieve several data from the DOM /// @@ -36,7 +34,8 @@ public interface IDomDataService /// /// the index of the element to search /// the selector to use to select the items + /// If the scroll must be taken into account for the calculations /// the size and position - Task GetElementSizeAndPosition(int elementIndex, string cssSelector); + Task GetElementSizeAndPosition(int elementIndex, string cssSelector, bool useScroll); } } diff --git a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs index a73ced0a..c0a9219c 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs @@ -238,7 +238,7 @@ protected override async Task OnIterationChanged() } this.ResetDataToCreate(); - this.CreateFakeData(); + //this.CreateFakeData(); this.IsLoading = false; } diff --git a/COMETwebapp/wwwroot/Scripts/DomData.js b/COMETwebapp/wwwroot/Scripts/DomData.js index e73b510d..977428b5 100644 --- a/COMETwebapp/wwwroot/Scripts/DomData.js +++ b/COMETwebapp/wwwroot/Scripts/DomData.js @@ -1,16 +1,28 @@ -function GetElementSizeAndPosition(index, cssSelector) { +function GetElementSizeAndPosition(index, cssSelector, useScroll) { var elements = document.getElementsByClassName(cssSelector); var element = elements[index]; - console.log(elements); - if (element != null) { - try { - console.log(element); + + if (element != null) + { + try + { + if (useScroll) + { + console.log("Position using scroll...") + var offsetTop = element.getBoundingClientRect().top - element.offsetParent.getBoundingClientRect().top; + return [element.offsetLeft, offsetTop, element.offsetWidth, element.offsetHeight]; + } + + console.log("Position NOT using scroll...") return [element.offsetLeft, element.offsetTop, element.offsetWidth, element.offsetHeight]; - } catch (error) { + } + catch (error) + { + console.log(error); return [0, 0, 0, 0]; } } - console.log("Element not found"); + return [0, 0, 0, 0]; -} \ No newline at end of file +} From afe0648324cccf5b47a81034ce3ab5d8641716f3 Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Tue, 19 Sep 2023 13:59:05 +0200 Subject: [PATCH 08/20] Fix svg lines issues --- .../BookEditor/BookEditorBody.razor.cs | 5 +- .../BookEditor/BookEditorColumn.razor | 33 ++++--- .../BookEditor/BookEditorColumn.razor.cs | 79 ++++++++++----- .../BookEditor/BookEditorColumn.razor.css | 1 + .../BookEditor/BookEditorBodyViewModel.cs | 99 ++----------------- 5 files changed, 85 insertions(+), 132 deletions(-) diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs index b8026bdd..a1ef0826 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs @@ -24,6 +24,8 @@ namespace COMETwebapp.Components.BookEditor { + using CDP4Common.ReportingData; + using COMET.Web.Common.Components.BookEditor; using ReactiveUI; @@ -120,7 +122,8 @@ private string GetHeaderText() return ("", null, null); } - return ("", null, null); + //Return must contain non null values for the dynamic component to work + return (nameof(BookInput.Book), new Book(), typeof(BookInput)); } } } diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor index 629d0fe5..544a5f98 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor @@ -52,19 +52,11 @@
@if (this.DrawLeftLines) { -
-
- - // var points = this.GenerateLeftPathPoints(isFirst, isLast); - - // - // - // - // + + + }
- + + + @if (this.DrawLeftLines) + { + var (verticalLine, horizontalSlice) = this.GenerateCommonLeftPathPoints(); + + + + + } }
diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs index 50a753a0..5a1c598b 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs @@ -107,13 +107,27 @@ public partial class BookEditorColumn [Parameter] public RenderFragment ContentTemplate { get; set; } + /// + /// The data of size and position of the first item in the list + /// private float[] firstItemSizeAndPosition = {}; - private float[] sizeAndPosition = {}; + /// + /// The data of size and position of the last item in the list + /// + private float[] lastItemSizeAndPosition = {}; + + /// + /// The data of size and position of the selected item + /// + private float[] selectedItemSizeAndPosition = {}; + /// + /// Gets or sets the class used to selected the nodes + /// [Parameter] public string CssClass { get; set; } - + /// /// Hanlder for when the selected value changes /// @@ -123,7 +137,8 @@ public partial class BookEditorColumn private async Task OnSelectedValueChanged(TItem item, int itemIndex) { this.firstItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(0, this.CssClass, false); - this.sizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(itemIndex, this.CssClass, true); + this.lastItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(this.Items.Count - 1, this.CssClass, false); + this.selectedItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(itemIndex, this.CssClass, true); await this.SelectedValueChanged.InvokeAsync(item); } @@ -134,7 +149,7 @@ private async Task OnSelectedValueChanged(TItem item, int itemIndex) public async Task OnScroll() { var itemIndex = this.Items.IndexOf(this.SelectedValue); - this.sizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(itemIndex, this.CssClass, true); + this.selectedItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(itemIndex, this.CssClass, true); await this.InvokeAsync(this.StateHasChanged); } @@ -144,15 +159,14 @@ public async Task OnScroll() /// the path private string GeneratePathPoints() { - if (this.sizeAndPosition.Length < 4 || this.firstItemSizeAndPosition.Length < 4) + if (this.selectedItemSizeAndPosition.Length < 4 || this.firstItemSizeAndPosition.Length < 4) { return string.Empty; } - - var left = this.sizeAndPosition[0]; - var top = this.sizeAndPosition[1]; - var width = this.sizeAndPosition[2]; - var height = this.sizeAndPosition[3]; + + var top = this.selectedItemSizeAndPosition[1]; + var width = this.selectedItemSizeAndPosition[2]; + var height = this.selectedItemSizeAndPosition[3]; var finalTop = (int)(this.firstItemSizeAndPosition[1] + this.firstItemSizeAndPosition[3] / 2.0f); @@ -168,28 +182,45 @@ private string GeneratePathPoints() return $"{x1},{y1},{x2},{y2},{x3},{y3},{x4},{y4}"; } - private (string verticalPath, string horizontalPath) GenerateLeftPathPoints(bool isFirst, bool isLast) + /// + /// Generate the path points for the common left polylines + /// + /// the path + private (string verticalPath, string staticLine) GenerateCommonLeftPathPoints() { - if (this.sizeAndPosition.Length < 4 || this.firstItemSizeAndPosition.Length < 4) + if (this.firstItemSizeAndPosition.Length < 4 || this.lastItemSizeAndPosition.Length < 4) { return (string.Empty, string.Empty); } - var top = this.sizeAndPosition[1]; - var width = this.sizeAndPosition[2]; - var height = this.sizeAndPosition[3]; + var topY = (int)(this.firstItemSizeAndPosition[1] + this.firstItemSizeAndPosition[3] / 2.0f); + var bottomY = (int)(this.lastItemSizeAndPosition[1] + this.lastItemSizeAndPosition[3] / 2.0f); + + var width = this.firstItemSizeAndPosition[2]; + + var x = (int)(width * 0.1); + + return ($"{x},{topY},{x},{bottomY}", $"{0},{topY},{x},{topY}"); + } + + /// + /// Generae the path points for the horizontal lines in the nodes + /// + /// + private string GenerateHorizontalPathPoints() + { + if (this.firstItemSizeAndPosition.Length < 4) + { + return string.Empty; + } - var x1 = (int)(width*0.1); - var y1 = (int)(top); - var x2 = (int)(width*0.1); - var y2 = (int)(top + height); + var y = (int)(this.firstItemSizeAndPosition[3] / 2.0f); + var width = this.firstItemSizeAndPosition[2]; - var x3 = (int)(width*0.0); - var y3 = (int)(top + height / 2.0f); - var x4 = (int)(width*0.2); - var y4 = (int)(top + height / 2.0f); + var x1 = (int)(width * 0.1); + var x2 = (int)(width * 0.5); - return ($"{x1},{y1},{x2},{y2}",$"{x3},{y3},{x4},{y4}"); + return $"{x1},{y},{x2},{y}"; } } } diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css index 13ac4e56..ed0753aa 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css @@ -6,6 +6,7 @@ width: 100%; display: flex; flex-direction: column; + height:85vh; max-height: 85vh; border: 0px dashed #555; padding: 2.5% 0%; diff --git a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs index c0a9219c..876d4b59 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs @@ -238,7 +238,6 @@ protected override async Task OnIterationChanged() } this.ResetDataToCreate(); - //this.CreateFakeData(); this.IsLoading = false; } @@ -285,113 +284,27 @@ private async Task OnHandleCreateItem() if (this.IsOnBookCreation) { var engineeringModel = this.CurrentIteration.Container as EngineeringModel; - //this.BookToCreate.Container = engineeringModel; var engineeringModelClone = engineeringModel.Clone(false); - //engineeringModelClone?.Book.Add(this.BookToCreate); await this.SessionService.CreateThing(engineeringModelClone, this.BookToCreate); } else if (this.IsOnSectionCreation) { - + var bookClone = this.SelectedBook.Clone(false); + await this.SessionService.CreateThing(bookClone, this.SectionToCreate); } else if (this.IsOnPageCreation) { - + var sectionClone = this.SelectedSection.Clone(false); + await this.SessionService.CreateThing(sectionClone, this.PageToCreate); } else if (this.IsOnNoteCreation) { - + var pageClone = this.SelectedPage.Clone(false); + await this.SessionService.CreateThing(pageClone, this.NoteToCreate); } this.ResetCreationStates(); } - - private void CreateFakeData() - { - var book = new Book - { - Name = "Example Book", - Section = - { - new Section - { - Name = "Example Section", - Page = - { - new Page - { - Name = "Example Page", - Note = - { - new BinaryNote - { - Name = "Binary Note" - }, - new TextualNote - { - Name = "Textual Note" - } - } - }, - new Page - { - Name = "Example Page 2" - } - } - }, - new Section - { - Name = "Section 2", - Page = - { - new Page - { - Name = "Page" - }, - new Page - { - Name = "Page 2" - } - } - }, - new Section - { - Name = "Empty Section" - }, - new Section - { - Name = "Empty Section" - }, - new Section - { - Name = "Empty Section" - }, - new Section - { - Name = "Empty Section" - }, - new Section - { - Name = "Empty Section" - }, - new Section - { - Name = "Empty Section" - }, - new Section - { - Name = "Empty Section" - }, - new Section - { - Name = "Empty Section" - } - } - }; - - this.AvailableBooks.Add(book); - this.AvailableBooks.Add(new Book(){Name = "Book 2"}); - } } } From 1180368beb2f45bd7338029fc3d0624d20d179c0 Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Wed, 20 Sep 2023 09:56:31 +0200 Subject: [PATCH 09/20] Manage to delete things --- COMET.Web.Common/COMET.Web.Common.csproj | 3 + .../Components/BookEditor/InputEditor.razor | 76 ++++++++++++++++++ .../BookEditor/InputEditor.razor.cs | 67 ++++++++++++++++ .../BookEditor/InputEditor.razor.css | 18 +++++ .../SessionManagement/ISessionService.cs | 26 ++++++- .../SessionManagement/SessionService.cs | 77 ++++++++++++++++++- .../BookEditor/BookEditorBody.razor | 12 ++- .../BookEditor/BookEditorColumn.razor | 7 +- .../BookEditor/BookEditorColumn.razor.cs | 33 ++++++++ .../Interoperability/DomDataService.cs | 10 +++ .../Interoperability/IDomDataService.cs | 7 ++ .../BookEditor/BookEditorBodyViewModel.cs | 20 +++++ .../BookEditor/IBookEditorBodyViewModel.cs | 9 +++ COMETwebapp/wwwroot/Scripts/DomData.js | 14 ++-- 14 files changed, 363 insertions(+), 16 deletions(-) create mode 100644 COMET.Web.Common/Components/BookEditor/InputEditor.razor create mode 100644 COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs create mode 100644 COMET.Web.Common/Components/BookEditor/InputEditor.razor.css diff --git a/COMET.Web.Common/COMET.Web.Common.csproj b/COMET.Web.Common/COMET.Web.Common.csproj index 26b8a489..ba33de31 100644 --- a/COMET.Web.Common/COMET.Web.Common.csproj +++ b/COMET.Web.Common/COMET.Web.Common.csproj @@ -43,6 +43,9 @@ + + true + Always diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor b/COMET.Web.Common/Components/BookEditor/InputEditor.razor new file mode 100644 index 00000000..3c9d7b07 --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor @@ -0,0 +1,76 @@ + +@namespace COMET.Web.Common.Components.BookEditor +@inherits DisposableComponent +@using CDP4Common.SiteDirectoryData +@using CDP4Common.CommonData +@using CDP4Common.EngineeringModelData +@typeparam TItem + + + +
+ @if (this.Item is INamedThing namedThing) + { +
+

Name:

+ +
+ } + + @if (this.Item is IShortNamedThing shortNamedThing) + { +
+

ShortName:

+ +
+ } + + @if (this.Item is IOwnedThing ownedThing) + { +
+

Owner:

+ +
+ } +
+
+ +
+ + +
+
+
\ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs new file mode 100644 index 00000000..56efc03d --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs @@ -0,0 +1,67 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.Components.BookEditor +{ + using CDP4Common.SiteDirectoryData; + + using Microsoft.AspNetCore.Components; + + /// + /// Support class for the InputEditor component + /// + public partial class InputEditor + { + /// + /// Gets or sets the item for which the input is being provided + /// + [Parameter] + public TItem Item { get; set; } + + /// + /// Gets or sets the active + /// + [Parameter] + public IEnumerable ActiveDomains { get; set; } + + /// + /// Gets or sets the collection of available + /// + [Parameter] + public IEnumerable AvailableCategories { get; set; } + + /// + /// Handler for when the selected categories changed + /// + /// + private void OnCategoryChange(IEnumerable categories) + { + if (this.Item is ICategorizableThing categorizableThing) + { + categorizableThing.Category = categories.ToList(); + } + } + } +} diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.css b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.css new file mode 100644 index 00000000..8a530845 --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.css @@ -0,0 +1,18 @@ +.tab-content { + display: flex; + flex-direction: column; + align-items: start; + justify-content: flex-start; + row-gap: 5px; + padding:1%; +} + +.editor-row{ + align-items:center; + justify-content:space-between; +} + +.editor-row>p{ + width: 25%; + margin:0; +} \ No newline at end of file diff --git a/COMET.Web.Common/Services/SessionManagement/ISessionService.cs b/COMET.Web.Common/Services/SessionManagement/ISessionService.cs index 97b9c328..944d90b0 100644 --- a/COMET.Web.Common/Services/SessionManagement/ISessionService.cs +++ b/COMET.Web.Common/Services/SessionManagement/ISessionService.cs @@ -161,7 +161,31 @@ public interface ISessionService /// The where the s should be updated /// List of Things to update in the session /// An asynchronous operation - Task UpdateThings(Thing container, IEnumerable thingsToUpdate); + Task UpdateThings(Thing container, IEnumerable thingsToUpdate); + + /// + /// Deletes a from it's container + /// + /// the container clone of the thing to delete + /// the thing to delete in the session + /// An asynchronous operation + Task DeleteThing(Thing containerClone, Thing thingToDelete); + + /// + /// Deletes a collection of from it's container + /// + /// the container clone of the thing to delete + /// the things to delete in the session + /// An asynchronous operation + Task DeleteThings(Thing containerClone, params Thing[] thingsToDelete); + + /// + /// Deletes a collection from it's container + /// + /// the container clone of the thing to delete + /// the things to delete in the session + /// An asynchronous operation + Task DeleteThings(Thing containerClone, IEnumerable thingsToDelete); /// /// Gets the inside an iteration diff --git a/COMET.Web.Common/Services/SessionManagement/SessionService.cs b/COMET.Web.Common/Services/SessionManagement/SessionService.cs index 6e011fc0..e1cf864a 100644 --- a/COMET.Web.Common/Services/SessionManagement/SessionService.cs +++ b/COMET.Web.Common/Services/SessionManagement/SessionService.cs @@ -314,7 +314,7 @@ public async Task UpdateThings(Thing thing, IEnumerable thingsToUpdate) // register all updates with the transaction. thingsToUpdate.ToList().ForEach(transaction.CreateOrUpdate); - + // finalize the transaction, the result is an OperationContainer that the session class uses to write the changes // to the Thing object. var operationContainer = transaction.FinalizeTransaction(); @@ -334,6 +334,81 @@ public async Task UpdateThings(Thing thing, IEnumerable thingsToUpdate) } } + /// + /// Deletes a from it's container + /// + /// the container clone of the thing to delete + /// the cloned thing to delete in the session + /// An asynchronous operation + public async Task DeleteThing(Thing containerClone, Thing thingToDelete) + { + await this.DeleteThings(containerClone, new List { thingToDelete }); + } + + /// + /// Deletes a collection of from it's container + /// + /// the container clone of the thing to delete + /// the cloned things to delete in the session + /// An asynchronous operation + public async Task DeleteThings(Thing containerClone, params Thing[] thingsToDelete) + { + await this.DeleteThings(containerClone, thingsToDelete.ToList()); + } + + /// + /// Deletes a collection from it's container + /// + /// the container clone of the thing to delete + /// the cloned things to delete in the session + /// An asynchronous operation + public async Task DeleteThings(Thing containerClone, IEnumerable thingsToDelete) + { + if (thingsToDelete == null) + { + return; + } + + var sw = Stopwatch.StartNew(); + + // CreateThings a shallow clone of the thing. The cached Thing object should not be changed, so we record the change on a clone. + var thingClone = containerClone; + + if (containerClone.Original == null) + { + thingClone = containerClone.Clone(false); + } + + // set the context of the transaction to the thing changes need to be added to. + var context = TransactionContextResolver.ResolveContext(thingClone); + var transaction = new ThingTransaction(context); + + // register all deletes with the transaction. + foreach (var thingToDelete in thingsToDelete) + { + var thingToDeleteClone = thingToDelete.Clone(false); + transaction.Delete(thingToDeleteClone, containerClone); + } + + // finalize the transaction, the result is an OperationContainer that the session class uses to write the changes + // to the Thing object. + var operationContainer = transaction.FinalizeTransaction(); + + try + { + await this.Session.Write(operationContainer); + Console.WriteLine($"Delete done in {sw.ElapsedMilliseconds} [ms]"); + } + catch (Exception ex) + { + Console.WriteLine($"The delete operation failed: {ex.Message}"); + } + finally + { + sw.Stop(); + } + } + /// /// Gets the inside an iteration /// diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor b/COMETwebapp/Components/BookEditor/BookEditorBody.razor index 97f6428e..5eac757a 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor @@ -61,7 +61,8 @@ DrawLeftLines="false" IsCollapsed="@this.IsBooksColumnCollapsed" CollapseButtonIconClass="no-display" - CssClass="book-nodes"> + CssClass="book-nodes" + OnDeleteClicked="@this.ViewModel.OnDeleteThing"> @context.Name @@ -74,7 +75,8 @@ IsCollapsed="@this.IsSectionColumnCollapsed" OnCollapseClicked="@(() => this.IsBooksColumnCollapsed = !this.IsBooksColumnCollapsed)" CollapseButtonIconClass="@(this.IsBooksColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")" - CssClass="section-nodes"> + CssClass="section-nodes" + OnDeleteClicked="@this.ViewModel.OnDeleteThing"> @context.Name @@ -87,7 +89,8 @@ IsCollapsed="@this.IsPageColumnCollapsed" OnCollapseClicked="@(() => this.IsSectionColumnCollapsed = !this.IsSectionColumnCollapsed)" CollapseButtonIconClass="@(this.IsSectionColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")" - CssClass="page-nodes"> + CssClass="page-nodes" + OnDeleteClicked="@this.ViewModel.OnDeleteThing"> @context.Name @@ -97,7 +100,8 @@ Items="@this.ViewModel.SelectedPage?.Note.ToList()" OnCreateNewItemClick="@(() => this.ViewModel.IsOnNoteCreation = true)" OnCollapseClicked="@(() => this.IsPageColumnCollapsed = !this.IsPageColumnCollapsed)" - CollapseButtonIconClass="@(this.IsPageColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")"> + CollapseButtonIconClass="@(this.IsPageColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")" + OnDeleteClicked="@this.ViewModel.OnDeleteThing"> @context.Name diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor index 544a5f98..8e1553b0 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor @@ -73,16 +73,13 @@ @if (isSelected) {
- - - - + +
} } - diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs index 5a1c598b..9d50cad5 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs @@ -29,6 +29,7 @@ namespace COMETwebapp.Components.BookEditor using DynamicData; using Microsoft.AspNetCore.Components; + using Microsoft.JSInterop; /// /// Support class for the BookEditorColumn component @@ -128,6 +129,32 @@ public partial class BookEditorColumn [Parameter] public string CssClass { get; set; } + /// + /// Gets or sets the callback for when the edit button is clicked + /// + [Parameter] + public EventCallback OnEditClicked { get; set; } + + /// + /// Gets or sets the callback for when the delete button is clicked + /// + [Parameter] + public EventCallback OnDeleteClicked { get; set; } + + /// + /// Method invoked when the component is ready to start, having received its + /// initial parameters from its parent in the render tree. + /// + /// Override this method if you will perform an asynchronous operation and + /// want the component to refresh when that operation is completed. + /// + /// A representing any asynchronous operation. + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + await this.DomDataService.SubscribeToResizeEvent(nameof(OnSizeChanged)); + } + /// /// Hanlder for when the selected value changes /// @@ -222,5 +249,11 @@ private string GenerateHorizontalPathPoints() return $"{x1},{y},{x2},{y}"; } + + [JSInvokable] + public static async Task OnSizeChanged() + { + + } } } diff --git a/COMETwebapp/Services/Interoperability/DomDataService.cs b/COMETwebapp/Services/Interoperability/DomDataService.cs index 895c820c..8268da84 100644 --- a/COMETwebapp/Services/Interoperability/DomDataService.cs +++ b/COMETwebapp/Services/Interoperability/DomDataService.cs @@ -50,5 +50,15 @@ public async Task GetElementSizeAndPosition(int elementIndex, string cs { return await this.JsRuntime.InvokeAsync("GetElementSizeAndPosition", elementIndex, cssSelector, useScroll); } + + /// + /// Subscribes for the resize event with a callback method name + /// + /// the callback method name + /// an asynchronous operation + public async Task SubscribeToResizeEvent(string callbackMethodName) + { + await this.JsRuntime.InvokeVoidAsync("SubscribeToResizeEvent", callbackMethodName); + } } } diff --git a/COMETwebapp/Services/Interoperability/IDomDataService.cs b/COMETwebapp/Services/Interoperability/IDomDataService.cs index 4d9457eb..ea818dbf 100644 --- a/COMETwebapp/Services/Interoperability/IDomDataService.cs +++ b/COMETwebapp/Services/Interoperability/IDomDataService.cs @@ -37,5 +37,12 @@ public interface IDomDataService /// If the scroll must be taken into account for the calculations /// the size and position Task GetElementSizeAndPosition(int elementIndex, string cssSelector, bool useScroll); + + /// + /// Subscribes for the resize event with a callback method name + /// + /// the callback method name + /// an asynchronous operation + Task SubscribeToResizeEvent(string callbackMethodName); } } diff --git a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs index 876d4b59..31eb9328 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs @@ -24,6 +24,7 @@ namespace COMETwebapp.ViewModels.Components.BookEditor { + using CDP4Common.CommonData; using CDP4Common.EngineeringModelData; using CDP4Common.ReportingData; using CDP4Common.SiteDirectoryData; @@ -305,6 +306,25 @@ private async Task OnHandleCreateItem() this.ResetCreationStates(); } + + /// + /// Hanlder for when the user request to delete a thing (Book,Section,Page or Note) + /// + /// the thing to delete + /// an asynchronous operation + public async Task OnDeleteThing(Thing thing) + { + if (thing is not Book && thing is not Section && thing is not Page && thing is not Note) + { + throw new ArgumentException("The thing to delete should be a (Book, Section, Page or Note)", nameof(thing)); + } + + var thingContainer = thing.Container; + var thingContainerClone = thingContainer.Clone(false); + + await this.SessionService.DeleteThing(thingContainerClone, thing.Clone(false)); + await this.OnIterationChanged(); + } } } diff --git a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs index 856fb0d3..7ceb14ca 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs @@ -24,6 +24,7 @@ namespace COMETwebapp.ViewModels.Components.BookEditor { + using CDP4Common.CommonData; using CDP4Common.EngineeringModelData; using CDP4Common.ReportingData; using CDP4Common.SiteDirectoryData; @@ -31,6 +32,7 @@ namespace COMETwebapp.ViewModels.Components.BookEditor using COMET.Web.Common.ViewModels.Components; using DynamicData; + using Microsoft.AspNetCore.Components; /// @@ -122,5 +124,12 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// Resets the states for creation modes /// void ResetCreationStates(); + + /// + /// Hanlder for when the user request to delete a thing (Book,Section,Page or Note) + /// + /// the thing to delete + /// an asynchronous operation + Task OnDeleteThing(Thing thing); } } diff --git a/COMETwebapp/wwwroot/Scripts/DomData.js b/COMETwebapp/wwwroot/Scripts/DomData.js index 977428b5..521aa617 100644 --- a/COMETwebapp/wwwroot/Scripts/DomData.js +++ b/COMETwebapp/wwwroot/Scripts/DomData.js @@ -1,5 +1,5 @@ -function GetElementSizeAndPosition(index, cssSelector, useScroll) { - +function GetElementSizeAndPosition(index, cssSelector, useScroll) +{ var elements = document.getElementsByClassName(cssSelector); var element = elements[index]; @@ -9,20 +9,24 @@ { if (useScroll) { - console.log("Position using scroll...") var offsetTop = element.getBoundingClientRect().top - element.offsetParent.getBoundingClientRect().top; return [element.offsetLeft, offsetTop, element.offsetWidth, element.offsetHeight]; } - console.log("Position NOT using scroll...") return [element.offsetLeft, element.offsetTop, element.offsetWidth, element.offsetHeight]; } catch (error) { - console.log(error); return [0, 0, 0, 0]; } } return [0, 0, 0, 0]; } + +function SubscribeToResizeEvent(callbackMethodName) { + window.addEventListener("resize", () => + { + DotNet.invokeMethodAsync('COMETwebapp', callbackMethodName); + }); +} \ No newline at end of file From 0c0c0e9c764610fe9281c4874424b395770ba2c9 Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Wed, 20 Sep 2023 10:28:38 +0200 Subject: [PATCH 10/20] Add edit items --- .../BookEditor/BookEditorBody.razor | 16 +++++ .../BookEditor/BookEditorBody.razor.cs | 3 +- .../BookEditor/BookEditorBodyViewModel.cs | 59 +++++++++++++++++++ .../BookEditor/IBookEditorBodyViewModel.cs | 22 +++++++ 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor b/COMETwebapp/Components/BookEditor/BookEditorBody.razor index 5eac757a..9c731ab1 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor @@ -51,6 +51,18 @@ + + + + + + + + +
@@ -62,6 +74,7 @@ IsCollapsed="@this.IsBooksColumnCollapsed" CollapseButtonIconClass="no-display" CssClass="book-nodes" + OnEditClicked="@((b) => this.ViewModel.SetThingToEdit(b))" OnDeleteClicked="@this.ViewModel.OnDeleteThing"> @context.Name @@ -76,6 +89,7 @@ OnCollapseClicked="@(() => this.IsBooksColumnCollapsed = !this.IsBooksColumnCollapsed)" CollapseButtonIconClass="@(this.IsBooksColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")" CssClass="section-nodes" + OnEditClicked="@((s) => this.ViewModel.SetThingToEdit(s))" OnDeleteClicked="@this.ViewModel.OnDeleteThing"> @context.Name @@ -90,6 +104,7 @@ OnCollapseClicked="@(() => this.IsSectionColumnCollapsed = !this.IsSectionColumnCollapsed)" CollapseButtonIconClass="@(this.IsSectionColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")" CssClass="page-nodes" + OnEditClicked="@((p) => this.ViewModel.SetThingToEdit(p))" OnDeleteClicked="@this.ViewModel.OnDeleteThing"> @context.Name @@ -101,6 +116,7 @@ OnCreateNewItemClick="@(() => this.ViewModel.IsOnNoteCreation = true)" OnCollapseClicked="@(() => this.IsPageColumnCollapsed = !this.IsPageColumnCollapsed)" CollapseButtonIconClass="@(this.IsPageColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")" + OnEditClicked="@((n) => this.ViewModel.SetThingToEdit(n))" OnDeleteClicked="@this.ViewModel.OnDeleteThing"> @context.Name diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs index a1ef0826..3863e187 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs @@ -69,7 +69,8 @@ protected override void OnInitialized() this.Disposables.Add(this.WhenAnyValue(x => x.ViewModel.IsOnBookCreation, x => x.ViewModel.IsOnSectionCreation, x => x.ViewModel.IsOnPageCreation, - x => x.ViewModel.IsOnNoteCreation) + x => x.ViewModel.IsOnNoteCreation, + x => x.ViewModel.IsOnEditMode) .Subscribe(_ => this.InvokeAsync(this.StateHasChanged))); } diff --git a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs index 31eb9328..c51e0cc0 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs @@ -65,6 +65,11 @@ public class BookEditorBodyViewModel : SingleIterationApplicationBaseViewModel, ///
private bool isOnNodeCreation; + /// + /// Backing field for the property + /// + private bool isOnEditMode; + /// /// Backing field for the property /// @@ -188,6 +193,20 @@ public bool IsOnNoteCreation /// public EventCallback OnCancelCreateItem { get; set; } + /// + /// Gets or sets if the ViewModel is on edit mode + /// + public bool IsOnEditMode + { + get => this.isOnEditMode; + set => this.RaiseAndSetIfChanged(ref this.isOnEditMode, value); + } + + /// + /// Gets or sets the thing to be edited + /// + public Thing ThingToEdit { get; set; } + /// /// Initializes a new instance of the class. /// @@ -325,6 +344,46 @@ public async Task OnDeleteThing(Thing thing) await this.SessionService.DeleteThing(thingContainerClone, thing.Clone(false)); await this.OnIterationChanged(); } + + /// + /// Sets the thing to be edited + /// + /// the thing + public void SetThingToEdit(Thing thing) + { + if (thing is not Book && thing is not Section && thing is not Page && thing is not Note) + { + throw new ArgumentException("The thing to edit should be a (Book, Section, Page or Note)", nameof(thing)); + } + + this.ThingToEdit = thing; + this.IsOnEditMode = true; + } + + /// + /// Handler for when the user request to edit a thing (Book,Section,Page or Note) + /// + /// an asynchronous operation + public async Task OnEditThing() + { + if (this.ThingToEdit == null) + { + throw new InvalidOperationException("The thing to edit can't be null"); + } + + if (this.ThingToEdit is not Book && this.ThingToEdit is not Section && this.ThingToEdit is not Page && this.ThingToEdit is not Note) + { + throw new ArgumentException("The thing to edit should be a (Book, Section, Page or Note)", nameof(this.ThingToEdit)); + } + + var thingContainer = this.ThingToEdit.Container; + var thingContainerClone = thingContainer.Clone(false); + + await this.SessionService.UpdateThing(thingContainerClone, this.ThingToEdit.Clone(false)); + + this.ThingToEdit = null; + this.IsOnEditMode = false; + } } } diff --git a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs index 7ceb14ca..841a8723 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs @@ -90,6 +90,11 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// bool IsOnNoteCreation { get; set; } + /// + /// Gets or sets if the ViewModel is on edit mode + /// + bool IsOnEditMode { get; set; } + /// /// Gets or sets the that's about to be created /// @@ -110,6 +115,11 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// Note NoteToCreate { get; set; } + /// + /// Gets or sets the thing to be edited + /// + Thing ThingToEdit { get; set; } + /// /// Gets or sets the for when an item is created /// @@ -131,5 +141,17 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// the thing to delete /// an asynchronous operation Task OnDeleteThing(Thing thing); + + /// + /// Sets the thing to be edited + /// + /// the thing + void SetThingToEdit(Thing thing); + + /// + /// Handler for when the user request to edit a thing (Book,Section,Page or Note) + /// + /// an asynchronous operation + Task OnEditThing(); } } From bef256a5c23708c6100bc45c0e75969c84611a9c Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Wed, 20 Sep 2023 11:06:20 +0200 Subject: [PATCH 11/20] Change method for creating things --- .../Components/BookEditor/InputEditor.razor | 8 ++ .../BookEditor/BookEditorBody.razor | 39 +++-- .../BookEditor/BookEditorBodyViewModel.cs | 133 ++++++++++++------ .../BookEditor/IBookEditorBodyViewModel.cs | 22 +++ 4 files changed, 136 insertions(+), 66 deletions(-) diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor b/COMET.Web.Common/Components/BookEditor/InputEditor.razor index 3c9d7b07..46d2f7e7 100644 --- a/COMET.Web.Common/Components/BookEditor/InputEditor.razor +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor @@ -24,6 +24,7 @@ @using CDP4Common.SiteDirectoryData @using CDP4Common.CommonData @using CDP4Common.EngineeringModelData +@using CDP4Common.ReportingData @typeparam TItem @@ -57,6 +58,13 @@ CssClass="w-100"/> } + + @if (this.Item is TextualNote textualNote) + { + + } diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor b/COMETwebapp/Components/BookEditor/BookEditorBody.razor index 9c731ab1..304bbb23 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor @@ -27,27 +27,14 @@ - @{ - var popupVisible = this.ViewModel.IsOnBookCreation || this.ViewModel.IsOnSectionCreation || this.ViewModel.IsOnPageCreation || this.ViewModel.IsOnNoteCreation; - - var parameter = this.GetDynamicComponentParameter(); - - var parameters = new Dictionary - { - {parameter.key, parameter.item}, - {"ActiveDomains", this.ViewModel.ActiveDomains}, - {"AvailableCategories", this.ViewModel.AvailableCategories} - }; - } - - + - + @@ -69,11 +56,11 @@ @@ -84,11 +71,11 @@ @@ -99,11 +86,11 @@ @@ -113,13 +100,21 @@ - @context.Name + @if (context is TextualNote textualNote) + { + @textualNote.Content + } + else + { + @context.Name + } diff --git a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs index c51e0cc0..f73e5ddf 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs @@ -70,6 +70,11 @@ public class BookEditorBodyViewModel : SingleIterationApplicationBaseViewModel, /// private bool isOnEditMode; + /// + /// Backing field for the property + /// + private bool isOnCreateMode; + /// /// Backing field for the property /// @@ -202,11 +207,25 @@ public bool IsOnEditMode set => this.RaiseAndSetIfChanged(ref this.isOnEditMode, value); } + /// + /// Gets or sets if the ViewModel is on create mode + /// + public bool IsOnCreateMode + { + get => this.isOnCreateMode; + set => this.RaiseAndSetIfChanged(ref this.isOnCreateMode, value); + } + /// /// Gets or sets the thing to be edited /// public Thing ThingToEdit { get; set; } + /// + /// Gets or sets the thing to be created + /// + public Thing ThingToCreate { get; set; } + /// /// Initializes a new instance of the class. /// @@ -217,10 +236,6 @@ public BookEditorBodyViewModel(ISessionService sessionService) : base(sessionSer this.Disposables.Add(this.WhenAnyValue(x => x.SelectedSection).Subscribe(_ => this.OnSelectedSectionChanged())); this.Disposables.Add(this.WhenAnyValue(x => x.IsOnBookCreation, x=> x.IsOnSectionCreation, x=>x.IsOnPageCreation, x=>x.IsOnNoteCreation).Subscribe(_=>this.ResetDataToCreate())); - - this.OnCreateItem = new EventCallbackFactory().Create(this, this.OnHandleCreateItem); - - this.OnCancelCreateItem = new EventCallbackFactory().Create(this, this.ResetCreationStates); } /// @@ -297,52 +312,69 @@ private void OnSelectedSectionChanged() } /// - /// Handles the creation of an item + /// Validates that the thing is a valid thing for the operations in this ViewModel /// - private async Task OnHandleCreateItem() + /// the thing to validate + private void ValidateThing(Thing thing) { - if (this.IsOnBookCreation) - { - var engineeringModel = this.CurrentIteration.Container as EngineeringModel; - var engineeringModelClone = engineeringModel.Clone(false); - await this.SessionService.CreateThing(engineeringModelClone, this.BookToCreate); - } - else if (this.IsOnSectionCreation) - { - var bookClone = this.SelectedBook.Clone(false); - await this.SessionService.CreateThing(bookClone, this.SectionToCreate); - } - else if (this.IsOnPageCreation) - { - var sectionClone = this.SelectedSection.Clone(false); - await this.SessionService.CreateThing(sectionClone, this.PageToCreate); - } - else if (this.IsOnNoteCreation) + if (thing is not Book && thing is not Section && thing is not Page && thing is not Note) { - var pageClone = this.SelectedPage.Clone(false); - await this.SessionService.CreateThing(pageClone, this.NoteToCreate); + throw new ArgumentException("The thing to should be a (Book, Section, Page or Note)", nameof(thing)); } + } + + /// + /// Sets the thing to be created + /// + /// the thing + public void SetThingToCreate(Thing thing) + { + this.ValidateThing(thing); - this.ResetCreationStates(); + this.ThingToCreate = thing; + this.IsOnCreateMode = true; } /// - /// Hanlder for when the user request to delete a thing (Book,Section,Page or Note) + /// Hanlder for when the user request to create a new thing (Book,Section,Page or Note) /// - /// the thing to delete - /// an asynchronous operation - public async Task OnDeleteThing(Thing thing) + /// + public async Task OnCreateThing() { - if (thing is not Book && thing is not Section && thing is not Page && thing is not Note) + if (this.ThingToCreate == null) { - throw new ArgumentException("The thing to delete should be a (Book, Section, Page or Note)", nameof(thing)); + throw new InvalidOperationException("The thing to create can't be null"); } - var thingContainer = thing.Container; - var thingContainerClone = thingContainer.Clone(false); + this.ValidateThing(this.ThingToCreate); - await this.SessionService.DeleteThing(thingContainerClone, thing.Clone(false)); - await this.OnIterationChanged(); + Thing thingContainer; + + switch (this.ThingToCreate) + { + case Book: + thingContainer = this.CurrentIteration.Container; + break; + case Section: + thingContainer = this.SelectedBook; + break; + case Page: + thingContainer = this.SelectedSection; + break; + case Note: + thingContainer = this.SelectedPage; + break; + + default: + this.ThingToCreate = null; + this.IsOnCreateMode = false; + return; + } + + await this.SessionService.CreateThing(thingContainer.Clone(false), this.ThingToCreate); + + this.ThingToCreate = null; + this.IsOnCreateMode = false; } /// @@ -351,10 +383,7 @@ public async Task OnDeleteThing(Thing thing) /// the thing public void SetThingToEdit(Thing thing) { - if (thing is not Book && thing is not Section && thing is not Page && thing is not Note) - { - throw new ArgumentException("The thing to edit should be a (Book, Section, Page or Note)", nameof(thing)); - } + this.ValidateThing(thing); this.ThingToEdit = thing; this.IsOnEditMode = true; @@ -371,10 +400,7 @@ public async Task OnEditThing() throw new InvalidOperationException("The thing to edit can't be null"); } - if (this.ThingToEdit is not Book && this.ThingToEdit is not Section && this.ThingToEdit is not Page && this.ThingToEdit is not Note) - { - throw new ArgumentException("The thing to edit should be a (Book, Section, Page or Note)", nameof(this.ThingToEdit)); - } + this.ValidateThing(this.ThingToEdit); var thingContainer = this.ThingToEdit.Container; var thingContainerClone = thingContainer.Clone(false); @@ -384,6 +410,25 @@ public async Task OnEditThing() this.ThingToEdit = null; this.IsOnEditMode = false; } + + /// + /// Hanlder for when the user request to delete a thing (Book,Section,Page or Note) + /// + /// the thing to delete + /// an asynchronous operation + public async Task OnDeleteThing(Thing thing) + { + if (thing is not Book && thing is not Section && thing is not Page && thing is not Note) + { + throw new ArgumentException("The thing to delete should be a (Book, Section, Page or Note)", nameof(thing)); + } + + var thingContainer = thing.Container; + var thingContainerClone = thingContainer.Clone(false); + + await this.SessionService.DeleteThing(thingContainerClone, thing.Clone(false)); + await this.OnIterationChanged(); + } } } diff --git a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs index 841a8723..8d50f701 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs @@ -95,6 +95,11 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// bool IsOnEditMode { get; set; } + /// + /// Gets or sets if the ViewModel is on create mode + /// + bool IsOnCreateMode { get; set; } + /// /// Gets or sets the that's about to be created /// @@ -120,6 +125,11 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// Thing ThingToEdit { get; set; } + /// + /// Gets or sets the thing to be created + /// + Thing ThingToCreate { get; set; } + /// /// Gets or sets the for when an item is created /// @@ -135,6 +145,18 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// void ResetCreationStates(); + /// + /// Sets the thing to be created + /// + /// the thing + void SetThingToCreate(Thing thing); + + /// + /// Hanlder for when the user request to create a new thing (Book,Section,Page or Note) + /// + /// + Task OnCreateThing(); + /// /// Hanlder for when the user request to delete a thing (Book,Section,Page or Note) /// From bff36eb4ffcf7b129a6f95fc8a073bf78132807c Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Wed, 20 Sep 2023 12:34:52 +0200 Subject: [PATCH 12/20] Add validation service for properties of thing. Also add editor popup --- COMET.Web.Common/COMET.Web.Common.csproj | 3 - .../Components/BookEditor/BookInput.razor | 62 -------- .../Components/BookEditor/BookInput.razor.cs | 65 -------- .../Components/BookEditor/BookInput.razor.css | 18 --- .../Components/BookEditor/EditorPopup.razor | 41 +++++ .../BookEditor/EditorPopup.razor.cs | 79 ++++++++++ .../Components/BookEditor/InputEditor.razor | 112 ++++++------- .../Components/BookEditor/PageInput.razor | 62 -------- .../Components/BookEditor/PageInput.razor.cs | 65 -------- .../Components/BookEditor/PageInput.razor.css | 18 --- .../Components/BookEditor/SectionInput.razor | 62 -------- .../BookEditor/SectionInput.razor.cs | 65 -------- .../BookEditor/SectionInput.razor.css | 18 --- .../Services/ValidationService.cs | 147 ++++++++++++++++++ .../BookEditor/EditorPopupViewModel.cs | 91 +++++++++++ .../BookEditor/IEditorPopupViewModel.cs | 81 ++++++++++ .../BookEditor/BookEditorBody.razor.cs | 63 +++----- .../BookEditor/BookEditorBodyViewModel.cs | 143 ++--------------- .../BookEditor/IBookEditorBodyViewModel.cs | 57 +------ 19 files changed, 534 insertions(+), 718 deletions(-) delete mode 100644 COMET.Web.Common/Components/BookEditor/BookInput.razor delete mode 100644 COMET.Web.Common/Components/BookEditor/BookInput.razor.cs delete mode 100644 COMET.Web.Common/Components/BookEditor/BookInput.razor.css create mode 100644 COMET.Web.Common/Components/BookEditor/EditorPopup.razor create mode 100644 COMET.Web.Common/Components/BookEditor/EditorPopup.razor.cs delete mode 100644 COMET.Web.Common/Components/BookEditor/PageInput.razor delete mode 100644 COMET.Web.Common/Components/BookEditor/PageInput.razor.cs delete mode 100644 COMET.Web.Common/Components/BookEditor/PageInput.razor.css delete mode 100644 COMET.Web.Common/Components/BookEditor/SectionInput.razor delete mode 100644 COMET.Web.Common/Components/BookEditor/SectionInput.razor.cs delete mode 100644 COMET.Web.Common/Components/BookEditor/SectionInput.razor.css create mode 100644 COMET.Web.Common/Services/ValidationService.cs create mode 100644 COMET.Web.Common/ViewModels/Components/BookEditor/EditorPopupViewModel.cs create mode 100644 COMET.Web.Common/ViewModels/Components/BookEditor/IEditorPopupViewModel.cs diff --git a/COMET.Web.Common/COMET.Web.Common.csproj b/COMET.Web.Common/COMET.Web.Common.csproj index ba33de31..296336e4 100644 --- a/COMET.Web.Common/COMET.Web.Common.csproj +++ b/COMET.Web.Common/COMET.Web.Common.csproj @@ -53,7 +53,4 @@ Always
- - - \ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/BookInput.razor b/COMET.Web.Common/Components/BookEditor/BookInput.razor deleted file mode 100644 index c55b11f3..00000000 --- a/COMET.Web.Common/Components/BookEditor/BookInput.razor +++ /dev/null @@ -1,62 +0,0 @@ - -@namespace COMET.Web.Common.Components.BookEditor -@using CDP4Common.SiteDirectoryData -@inherits DisposableComponent - - - -
-
-

Short Name:

- -
-
-

Name:

- -
-
-

Owner:

- -
-
-
- -
- - -
-
-
\ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/BookInput.razor.cs b/COMET.Web.Common/Components/BookEditor/BookInput.razor.cs deleted file mode 100644 index 1593097b..00000000 --- a/COMET.Web.Common/Components/BookEditor/BookInput.razor.cs +++ /dev/null @@ -1,65 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2023 RHEA System S.A. -// -// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar -// -// This file is part of COMET WEB Community Edition -// The COMET WEB Community Edition is the RHEA 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.Components.BookEditor -{ - using CDP4Common.ReportingData; - using CDP4Common.SiteDirectoryData; - - using Microsoft.AspNetCore.Components; - - /// - /// Support class for the BookInput component - /// - public partial class BookInput - { - /// - /// Gets or sets the book for which the input is being provided - /// - [Parameter] - public Book Book { get; set; } - - /// - /// Gets or sets the active - /// - [Parameter] - public IEnumerable ActiveDomains { get; set; } - - /// - /// Gets or sets the collection of available - /// - [Parameter] - public IEnumerable AvailableCategories { get; set; } - - /// - /// Handler for when the selected categories changed - /// - /// - private void OnCategoryChange(IEnumerable categories) - { - this.Book.Category = categories.ToList(); - } - } -} diff --git a/COMET.Web.Common/Components/BookEditor/BookInput.razor.css b/COMET.Web.Common/Components/BookEditor/BookInput.razor.css deleted file mode 100644 index 8a530845..00000000 --- a/COMET.Web.Common/Components/BookEditor/BookInput.razor.css +++ /dev/null @@ -1,18 +0,0 @@ -.tab-content { - display: flex; - flex-direction: column; - align-items: start; - justify-content: flex-start; - row-gap: 5px; - padding:1%; -} - -.editor-row{ - align-items:center; - justify-content:space-between; -} - -.editor-row>p{ - width: 25%; - margin:0; -} \ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/EditorPopup.razor b/COMET.Web.Common/Components/BookEditor/EditorPopup.razor new file mode 100644 index 00000000..8f2bf844 --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/EditorPopup.razor @@ -0,0 +1,41 @@ + +@namespace COMET.Web.Common.Components.BookEditor +@inherits DisposableComponent + + + + +
+ @foreach (var validationError in this.ViewModel.ValidationErrors.Items) + { + + } +
+
+ + + +
diff --git a/COMET.Web.Common/Components/BookEditor/EditorPopup.razor.cs b/COMET.Web.Common/Components/BookEditor/EditorPopup.razor.cs new file mode 100644 index 00000000..8f74a282 --- /dev/null +++ b/COMET.Web.Common/Components/BookEditor/EditorPopup.razor.cs @@ -0,0 +1,79 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.Components.BookEditor +{ + using CDP4Common.Helpers; + + using COMET.Web.Common.Services; + using COMET.Web.Common.ViewModels.Components.BookEditor; + + using Microsoft.AspNetCore.Components; + + /// + /// Support class for the BookEditorPopup component + /// + public partial class EditorPopup + { + /// + /// Gets or sets the + /// + [Parameter] + public IEditorPopupViewModel ViewModel { get; set; } + + /// + /// Method invoked when the component is ready to start, having received its + /// initial parameters from its parent in the render tree. + /// + /// Override this method if you will perform an asynchronous operation and + /// want the component to refresh when that operation is completed. + /// + /// A representing any asynchronous operation. + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + this.Disposables.Add(this.ViewModel.ValidationErrors.Connect().Subscribe(_ => this.InvokeAsync(this.StateHasChanged))); + } + + /// + /// Handler for when the confirm button has been clicked + /// + private async Task OnConfirmClick() + { + var validationErrors = ValidationService.ValidateThing(this.ViewModel.Item); + + this.ViewModel.ValidationErrors.Edit(inner => + { + inner.Clear(); + inner.AddRange(validationErrors); + }); + + if (!this.ViewModel.ValidationErrors.Items.Any()) + { + await this.ViewModel.OnConfirmClick.InvokeAsync(); + } + } + } +} diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor b/COMET.Web.Common/Components/BookEditor/InputEditor.razor index 46d2f7e7..a0104a83 100644 --- a/COMET.Web.Common/Components/BookEditor/InputEditor.razor +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor @@ -27,58 +27,62 @@ @using CDP4Common.ReportingData @typeparam TItem - - -
- @if (this.Item is INamedThing namedThing) - { -
-

Name:

- -
- } +
+ + +
+ @if (this.Item is INamedThing namedThing) + { +
+

Name:

+ +
+ } - @if (this.Item is IShortNamedThing shortNamedThing) - { -
-

ShortName:

- -
- } - - @if (this.Item is IOwnedThing ownedThing) - { -
-

Owner:

- -
- } - - @if (this.Item is TextualNote textualNote) - { - - } -
-
- -
- - -
-
-
\ No newline at end of file + @if (this.Item is IShortNamedThing shortNamedThing) + { +
+

ShortName:

+ +
+ } + + @if (this.Item is IOwnedThing ownedThing) + { +
+

Owner:

+ +
+ } + + @if (this.Item is TextualNote textualNote) + { + + } +
+ + +
+ + +
+
+ + + +
diff --git a/COMET.Web.Common/Components/BookEditor/PageInput.razor b/COMET.Web.Common/Components/BookEditor/PageInput.razor deleted file mode 100644 index 757b1a85..00000000 --- a/COMET.Web.Common/Components/BookEditor/PageInput.razor +++ /dev/null @@ -1,62 +0,0 @@ - -@namespace COMET.Web.Common.Components.BookEditor -@using CDP4Common.SiteDirectoryData -@inherits DisposableComponent - - - -
-
-

Short Name:

- -
-
-

Name:

- -
-
-

Owner:

- -
-
-
- -
- - -
-
-
\ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/PageInput.razor.cs b/COMET.Web.Common/Components/BookEditor/PageInput.razor.cs deleted file mode 100644 index 3f83f7d5..00000000 --- a/COMET.Web.Common/Components/BookEditor/PageInput.razor.cs +++ /dev/null @@ -1,65 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2023 RHEA System S.A. -// -// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar -// -// This file is part of COMET WEB Community Edition -// The COMET WEB Community Edition is the RHEA 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.Components.BookEditor -{ - using CDP4Common.ReportingData; - using CDP4Common.SiteDirectoryData; - - using Microsoft.AspNetCore.Components; - - /// - /// Support class for the PageInput component - /// - public partial class PageInput - { - /// - /// Gets or sets the page for which the input is being provided - /// - [Parameter] - public Page Page { get; set; } - - /// - /// Gets or sets the active - /// - [Parameter] - public IEnumerable ActiveDomains { get; set; } - - /// - /// Gets or sets the collection of available - /// - [Parameter] - public IEnumerable AvailableCategories { get; set; } - - /// - /// Handler for when the selected categories changed - /// - /// - private void OnCategoryChange(IEnumerable categories) - { - this.Page.Category = categories.ToList(); - } - } -} diff --git a/COMET.Web.Common/Components/BookEditor/PageInput.razor.css b/COMET.Web.Common/Components/BookEditor/PageInput.razor.css deleted file mode 100644 index 8a530845..00000000 --- a/COMET.Web.Common/Components/BookEditor/PageInput.razor.css +++ /dev/null @@ -1,18 +0,0 @@ -.tab-content { - display: flex; - flex-direction: column; - align-items: start; - justify-content: flex-start; - row-gap: 5px; - padding:1%; -} - -.editor-row{ - align-items:center; - justify-content:space-between; -} - -.editor-row>p{ - width: 25%; - margin:0; -} \ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/SectionInput.razor b/COMET.Web.Common/Components/BookEditor/SectionInput.razor deleted file mode 100644 index bbe63166..00000000 --- a/COMET.Web.Common/Components/BookEditor/SectionInput.razor +++ /dev/null @@ -1,62 +0,0 @@ - -@namespace COMET.Web.Common.Components.BookEditor -@using CDP4Common.SiteDirectoryData -@inherits DisposableComponent - - - -
-
-

Short Name:

- -
-
-

Name:

- -
-
-

Owner:

- -
-
-
- -
- - -
-
-
\ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/SectionInput.razor.cs b/COMET.Web.Common/Components/BookEditor/SectionInput.razor.cs deleted file mode 100644 index 8f9f261b..00000000 --- a/COMET.Web.Common/Components/BookEditor/SectionInput.razor.cs +++ /dev/null @@ -1,65 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2023 RHEA System S.A. -// -// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar -// -// This file is part of COMET WEB Community Edition -// The COMET WEB Community Edition is the RHEA 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.Components.BookEditor -{ - using CDP4Common.ReportingData; - using CDP4Common.SiteDirectoryData; - - using Microsoft.AspNetCore.Components; - - /// - /// Support class for the SectionInput component - /// - public partial class SectionInput - { - /// - /// Gets or sets the section for which the input is being provided - /// - [Parameter] - public Section Section { get; set; } - - /// - /// Gets or sets the active - /// - [Parameter] - public IEnumerable ActiveDomains { get; set; } - - /// - /// Gets or sets the collection of available - /// - [Parameter] - public IEnumerable AvailableCategories { get; set; } - - /// - /// Handler for when the selected categories changed - /// - /// - private void OnCategoryChange(IEnumerable categories) - { - this.Section.Category = categories.ToList(); - } - } -} diff --git a/COMET.Web.Common/Components/BookEditor/SectionInput.razor.css b/COMET.Web.Common/Components/BookEditor/SectionInput.razor.css deleted file mode 100644 index 8a530845..00000000 --- a/COMET.Web.Common/Components/BookEditor/SectionInput.razor.css +++ /dev/null @@ -1,18 +0,0 @@ -.tab-content { - display: flex; - flex-direction: column; - align-items: start; - justify-content: flex-start; - row-gap: 5px; - padding:1%; -} - -.editor-row{ - align-items:center; - justify-content:space-between; -} - -.editor-row>p{ - width: 25%; - margin:0; -} \ No newline at end of file diff --git a/COMET.Web.Common/Services/ValidationService.cs b/COMET.Web.Common/Services/ValidationService.cs new file mode 100644 index 00000000..974bbc02 --- /dev/null +++ b/COMET.Web.Common/Services/ValidationService.cs @@ -0,0 +1,147 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.Services +{ + using CDP4Common.CommonData; + + using System.Collections.Generic; + using System.Text.RegularExpressions; + + /// + /// The purpose of the is to check and report on the validity of a field in a + /// + public static class ValidationService + { + /// + /// The validation map provides the mapping between field names and s. + /// + private static readonly Dictionary ValidationMap = new() + { + { "SelectedSource", new ValidationRule { PropertyName = "SelectedSource", Rule = @"^(?!\s*$).+", ErrorText = "The SelectedSource must not be empty." } }, + { "ShortName", new ValidationRule { PropertyName = "ShortName", Rule = @"^[a-zA-Z][a-zA-Z0-9_]*$", ErrorText = "The ShortName must start with a letter and not contain any spaces or non alphanumeric characters." } }, + { "RDLShortName", new ValidationRule { PropertyName = "ShortName", Rule = @"^([^()\s][\S\s]*)$", ErrorText = "The ShortName can not be empty or start with a whitespace." } }, + { "RDLName", new ValidationRule { PropertyName = "Name", Rule = @"^([^()\s][\S\s]*)$", ErrorText = "The Name can not be empty or start with a whitespace." } }, + { "NativeName", new ValidationRule { PropertyName = "NativeName", Rule = @"^[a-zA-Z][a-zA-Z0-9_]*$", ErrorText = "The NativeName must start with a letter and not contain any spaces or non alphanumeric characters." } }, + { "FileRevisionName", new ValidationRule { PropertyName = "Name", Rule = @"^([^()\s][\S\s]*)$", ErrorText = "The Name can not be empty or start with a whitespace." } }, + { "Name", new ValidationRule { PropertyName = "Name", Rule = @"^(\p{L}|\p{L}[^()]*[^()\s])$", ErrorText = "The Name must start with a letter and not contain any parentheses or trailing spaces." } }, + { "EmailAddress", new ValidationRule { PropertyName = "EmailAddress", Rule = @"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$", ErrorText = "The Email must follow the RFC 5321 protocol." } }, + { "TelephoneNumber", new ValidationRule { PropertyName = "TelephoneNumber", Rule = @"^(?!\s*$).+", ErrorText = "The Telephone Number must not be empty." } }, + { "UserPreference", new ValidationRule { PropertyName = "UserPreference", Rule = @"^(?!\s*$).+", ErrorText = "The User Preference must not be empty." } }, + { "LanguageCode", new ValidationRule { PropertyName = "LanguageCode", Rule = @"^(?!\s*$).+", ErrorText = "The Language Code must not be empty." } }, + { "Content", new ValidationRule { PropertyName = "Content", Rule = @"^(?!\s*$).+", ErrorText = "The Content must not be empty." } }, + { "Password", new ValidationRule { PropertyName = "Password", Rule = @"^(?!\s*$).+", ErrorText = "The Password must not be empty." } }, + { "ForwardRelationshipName", new ValidationRule { PropertyName = "ForwardRelationshipName", Rule = @"^(?!\s*$).+", ErrorText = "The Forward Relationship Name must not be empty." } }, + { "InverseRelationshipName", new ValidationRule { PropertyName = "InverseRelationshipName", Rule = @"^(?!\s*$).+", ErrorText = "The Inverse Relationship Name must not be empty." } }, + { "Exponent", new ValidationRule { PropertyName = "Exponent", Rule = @"^(?!\s*$).+", ErrorText = "The Exponent must not be empty." } }, + { "Symbol", new ValidationRule { PropertyName = "Symbol", Rule = @"^(?!\s*$).+", ErrorText = "The Symbol must not be empty." } }, + { "ScaleValueDefinition", new ValidationRule { PropertyName = "ScaleValueDefinition", Rule = @"^(?!\s*$).+", ErrorText = "The Value must not be empty." } }, + { "ScaleReferenceQuantityValue", new ValidationRule { PropertyName = "ScaleReferenceQuantityValue", Rule = @"^(?!\s*$).+", ErrorText = "The Value must not be empty." } }, + { "Factor", new ValidationRule { PropertyName = "Factor", Rule = @"^(?!\s*$).+", ErrorText = "The factor must not be empty." } }, + { "Modulus", new ValidationRule { PropertyName = "Modulus", Rule = @"^(?!\s*$).+", ErrorText = "The Modulus must not be empty." } }, + { "Extension", new ValidationRule { PropertyName = "Extension", Rule = @"^[a-z0-9]+$", ErrorText = "The Extension shall only contain lower cases or digits and cannot be empty." } }, + { "FileTypeName", new ValidationRule { PropertyName = "FileTypeName", Rule = @"^(application|audio|example|image|message|model|multipart|text|video)/\S+$", ErrorText = "The Name or short-name shall be consistent with the IANA standard and starts with either \"application/\", \"audio/\", \"example/\", \"image/\", \"message/\", \"model/\", \"multipart/\", \"text/\" or \"video/\" followed by any characters except white-spaces." } }, + { "Value", new ValidationRule { PropertyName = "Value", Rule = @"^(?!\s*$).+", ErrorText = "The Value must not be empty." } }, + { "ModelSetupShortName", new ValidationRule { PropertyName = "ShortName", Rule = @"^[a-zA-Z0-9_]*$", ErrorText = "The ShortName shall only contain alpha-numerical character." } }, + { "PersonShortName", new ValidationRule { PropertyName = "Value", Rule = @"^(?!\s*$).+", ErrorText = "The User Name must not be empty." } }, + { "PersonGivenName", new ValidationRule { PropertyName = "Value", Rule = @"^(?!\s*$).+", ErrorText = "The Given Name must not be empty." } }, + { "PersonSurname", new ValidationRule { PropertyName = "Value", Rule = @"^(?!\s*$).+", ErrorText = "The Surname must not be empty." } }, + { "RequirementShortName", new ValidationRule { PropertyName = "Value", Rule = @"^\w+[\w-]*$", ErrorText = "The Requirement ShortName cannot be empty and must contain only alphanumeric characters and dashes." } }, + { "ModelSetupName", new ValidationRule { PropertyName = "Name", Rule = @"^([^()\s][\S\s]*)$", ErrorText = "The Name can not be empty or start with a whitespace." } }, + { "ConversionFactor", new ValidationRule { PropertyName = "ConversionFactor", Rule = @"^(?!\s*$).+", ErrorText = "The ConversionFactor must not be empty" } }, + { "Description", new ValidationRule { PropertyName = "Description", Rule = @"^(?!\s*$).+", ErrorText = "The Description must not be empty." } }, + { "Title", new ValidationRule { PropertyName = "Title", Rule = @"^(?!\s*$).+", ErrorText = "The Title must not be empty." } }, + { "EnumerationValueDefinitionShortName", new ValidationRule { PropertyName = "ShortName", Rule = @"^([^()\s][\S]*)$", ErrorText = "The ShortName can not be empty or contain a whitespace." } }, + { "EnumerationValueDefinitionName", new ValidationRule { PropertyName = "Name", Rule = @"^([^()\s][\S]*)$", ErrorText = "The Name can not be empty or contain a whitespace." } } + }; + + /// + /// Validate all the properties of a using REFLECTION! + /// + /// the thing to validate + /// the collection of errors if any + public static IEnumerable ValidateThing(Thing thing) + { + var properties = thing.GetType().GetProperties(); + + var validationErrors = new List(); + + foreach (var property in properties) + { + var propertyValue = property.GetValue(thing); + var validationError = ValidateProperty(property.Name, propertyValue); + validationErrors.Add(validationError); + } + + return validationErrors; + } + + /// + /// Validate a property of a + /// + /// the name of the property to validate + /// the value to validate + /// + /// The with the error text, or null if there is no validation error + /// (either because there is no rule for the given property or because the given value is correct) + /// + public static string ValidateProperty(string propertyName, object value) + { + // if no rule exists just return null in sign of ignorance + if (!ValidationMap.TryGetValue(propertyName, out var rule)) + { + return null; + } + + var newValue = value == null ? string.Empty : value.ToString() ?? string.Empty; + + // get the value, if the value is null set to empty string (assume user entered no value to begin with) and check against that + var validationPass = Regex.IsMatch(newValue, rule.Rule); + + return validationPass ? null : rule.ErrorText; + } + + /// + /// The validation rule contains the regex and the error texts that goes with the rule. + /// + public class ValidationRule + { + /// + /// Gets or sets the name of the property + /// + public string PropertyName { get; set; } + + /// + /// Gets or sets the rule, i.e. the Regex that must be matched for this rule to be satisfied. + /// + public string Rule { get; set; } + + /// + /// Gets or sets the error text of this rule. + /// + public string ErrorText { get; set; } + } + } +} diff --git a/COMET.Web.Common/ViewModels/Components/BookEditor/EditorPopupViewModel.cs b/COMET.Web.Common/ViewModels/Components/BookEditor/EditorPopupViewModel.cs new file mode 100644 index 00000000..596ab538 --- /dev/null +++ b/COMET.Web.Common/ViewModels/Components/BookEditor/EditorPopupViewModel.cs @@ -0,0 +1,91 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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 COMET.Web.Common.ViewModels.Components.BookEditor +{ + using CDP4Common.CommonData; + using CDP4Common.SiteDirectoryData; + + using DynamicData; + + using Microsoft.AspNetCore.Components; + + using ReactiveUI; + + /// + /// ViewModel for the EditorPopup component + /// + public class EditorPopupViewModel : ReactiveObject, IEditorPopupViewModel + { + /// + /// Backing field for the property + /// + private bool isVisible; + + /// + /// Gets or sets the text of the header + /// + public string HeaderText { get; set; } = string.Empty; + + /// + /// Gets or sets if the popup is visible + /// + public bool IsVisible + { + get => this.isVisible; + set => this.RaiseAndSetIfChanged(ref this.isVisible, value); + } + + /// + /// Gets or sets the collection of validation errors + /// + public SourceList ValidationErrors { get; set; } = new(); + + /// + /// Gets or sets the callback for when the confirm button has been clicked + /// + public EventCallback OnConfirmClick { get; set; } + + /// + /// Gets or sets the callback for when the cancel button has been clicked + /// + public EventCallback OnCancelClick { get; set; } + + /// + /// Gets or sets the collection of active domains + /// + public IEnumerable ActiveDomains { get; set; } + + /// + /// Gets or sets the collection of available categories + /// + public IEnumerable AvailableCategories { get; set; } + + /// + /// Gets or sets the item for which this ViewModel is refering to + /// + public Thing Item { get; set; } + } +} + diff --git a/COMET.Web.Common/ViewModels/Components/BookEditor/IEditorPopupViewModel.cs b/COMET.Web.Common/ViewModels/Components/BookEditor/IEditorPopupViewModel.cs new file mode 100644 index 00000000..3f7342ff --- /dev/null +++ b/COMET.Web.Common/ViewModels/Components/BookEditor/IEditorPopupViewModel.cs @@ -0,0 +1,81 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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 COMET.Web.Common.ViewModels.Components.BookEditor +{ + using CDP4Common.CommonData; + using CDP4Common.SiteDirectoryData; + + using DynamicData; + + using Microsoft.AspNetCore.Components; + + /// + /// ViewModel for the EditorPopup component + /// + public interface IEditorPopupViewModel + { + /// + /// Gets or sets the text of the header + /// + string HeaderText { get; set; } + + /// + /// Gets or sets if the popup is visible + /// + bool IsVisible { get; set; } + + /// + /// Gets or sets the collection of validation errors + /// + SourceList ValidationErrors { get; set; } + + /// + /// Gets or sets the callback for when the confirm button has been clicked + /// + EventCallback OnConfirmClick { get; set; } + + /// + /// Gets or sets the callback for when the cancel button has been clicked + /// + EventCallback OnCancelClick { get; set; } + + /// + /// Gets or sets the collection of active domains + /// + IEnumerable ActiveDomains { get; set; } + + /// + /// Gets or sets the collection of available categories + /// + IEnumerable AvailableCategories { get; set; } + + /// + /// Gets or sets the item for which this ViewModel is refering to + /// + Thing Item { get; set; } + } +} + + diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs index 3863e187..b611210c 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs @@ -66,11 +66,8 @@ protected override void OnInitialized() { base.OnInitialized(); - this.Disposables.Add(this.WhenAnyValue(x => x.ViewModel.IsOnBookCreation, - x => x.ViewModel.IsOnSectionCreation, - x => x.ViewModel.IsOnPageCreation, - x => x.ViewModel.IsOnNoteCreation, - x => x.ViewModel.IsOnEditMode) + this.Disposables.Add(this.WhenAnyValue(x => x.ViewModel.IsOnEditMode, + x => x.ViewModel.IsOnCreateMode) .Subscribe(_ => this.InvokeAsync(this.StateHasChanged))); } @@ -80,51 +77,29 @@ protected override void OnInitialized() /// private string GetHeaderText() { - if (this.ViewModel.IsOnBookCreation) + if (this.ViewModel.IsOnCreateMode) { - return "Create a new Book"; + switch (this.ViewModel.ThingToCreate) + { + case Book: return "Create a new Book"; + case Section: return "Create a new Section"; + case Page: return "Create a new Page"; + case Note: return "Create a new Note"; + } } - else if (this.ViewModel.IsOnSectionCreation) - { - return "Create a new Section"; - } - else if (this.ViewModel.IsOnPageCreation) - { - return "Create a new Page"; - } - else if (this.ViewModel.IsOnNoteCreation) - { - return "Create a new Node"; - } - - return string.Empty; - } - /// - /// Gets the parameter for the dynamic component being redered - /// - /// the parameter key and values - private (string key, object item, Type type) GetDynamicComponentParameter() - { - if (this.ViewModel.IsOnBookCreation) + if (this.ViewModel.IsOnEditMode) { - return (nameof(BookInput.Book), this.ViewModel.BookToCreate, typeof(BookInput)); - } - else if (this.ViewModel.IsOnSectionCreation) - { - return (nameof(SectionInput.Section), this.ViewModel.SectionToCreate, typeof(SectionInput)); - } - else if (this.ViewModel.IsOnPageCreation) - { - return (nameof(PageInput.Page), this.ViewModel.PageToCreate, typeof(PageInput)); - } - else if (this.ViewModel.IsOnNoteCreation) - { - return ("", null, null); + switch (this.ViewModel.ThingToEdit) + { + case Book: return "Edit the Book"; + case Section: return "Edit the Section"; + case Page: return "Edit the Page"; + case Note: return "Edit the Note"; + } } - //Return must contain non null values for the dynamic component to work - return (nameof(BookInput.Book), new Book(), typeof(BookInput)); + return string.Empty; } } } diff --git a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs index f73e5ddf..9aafba9d 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs @@ -36,8 +36,6 @@ namespace COMETwebapp.ViewModels.Components.BookEditor using DynamicData; - using Microsoft.AspNetCore.Components; - using ReactiveUI; /// @@ -45,26 +43,6 @@ namespace COMETwebapp.ViewModels.Components.BookEditor /// public class BookEditorBodyViewModel : SingleIterationApplicationBaseViewModel, IBookEditorBodyViewModel { - /// - /// Backing field for the property - /// - private bool isOnBookCreation; - - /// - /// Backing field for the property - /// - private bool isOnSectionCreation; - - /// - /// Backing field for the property - /// - private bool isOnPageCreation; - - /// - /// Backing field for the property - /// - private bool isOnNodeCreation; - /// /// Backing field for the property /// @@ -131,73 +109,7 @@ public Page SelectedPage /// Gets or sets the active /// public List ActiveDomains { get; set; } = new(); - - /// - /// Gets or sets if the ViewModel is on book creation state - /// - public bool IsOnBookCreation - { - get => this.isOnBookCreation; - set => this.RaiseAndSetIfChanged(ref this.isOnBookCreation, value); - } - - /// - /// Gets or sets if the ViewModel is on section creation state - /// - public bool IsOnSectionCreation - { - get => this.isOnSectionCreation; - set => this.RaiseAndSetIfChanged(ref this.isOnSectionCreation, value); - } - - /// - /// Gets or sets if the ViewModel is on page creation state - /// - public bool IsOnPageCreation - { - get => this.isOnPageCreation; - set => this.RaiseAndSetIfChanged(ref this.isOnPageCreation, value); - } - - /// - /// Gets or sets if the ViewModel is on node creation state - /// - public bool IsOnNoteCreation - { - get => this.isOnNodeCreation; - set => this.RaiseAndSetIfChanged(ref this.isOnNodeCreation, value); - } - - /// - /// Gets or sets the that's about to be created - /// - public Book BookToCreate { get; set; } - - /// - /// Gets or sets the that's about to be created - /// - public Section SectionToCreate { get; set; } - - /// - /// Gets or sets the that's about to be created - /// - public Page PageToCreate { get; set; } - - /// - /// Gets or sets the that's about to be created - /// - public Note NoteToCreate { get; set; } - - /// - /// Gets or sets the for when an item is created - /// - public EventCallback OnCreateItem { get; set; } - - /// - /// Gets or sets the for when an item has canceled it's creation - /// - public EventCallback OnCancelCreateItem { get; set; } - + /// /// Gets or sets if the ViewModel is on edit mode /// @@ -234,8 +146,23 @@ public BookEditorBodyViewModel(ISessionService sessionService) : base(sessionSer { this.Disposables.Add(this.WhenAnyValue(x => x.SelectedBook).Subscribe(_ => this.OnSelectedBookChanged())); this.Disposables.Add(this.WhenAnyValue(x => x.SelectedSection).Subscribe(_ => this.OnSelectedSectionChanged())); + } + + /// + /// Handler for when the selected book changed + /// + private void OnSelectedBookChanged() + { + this.SelectedSection = null; + this.SelectedPage = null; + } - this.Disposables.Add(this.WhenAnyValue(x => x.IsOnBookCreation, x=> x.IsOnSectionCreation, x=>x.IsOnPageCreation, x=>x.IsOnNoteCreation).Subscribe(_=>this.ResetDataToCreate())); + /// + /// Handler for when the selected section changed + /// + private void OnSelectedSectionChanged() + { + this.SelectedPage = null; } /// @@ -272,45 +199,9 @@ protected override async Task OnIterationChanged() this.AvailableCategories.AddRange(categories); } - this.ResetDataToCreate(); this.IsLoading = false; } - /// - /// Resets the states for creation modes - /// - public void ResetCreationStates() - { - this.IsOnBookCreation = this.IsOnSectionCreation = this.IsOnPageCreation = this.IsOnNoteCreation = false; - } - - /// - /// Resets the data that is going to be used for creating a book, section or page - /// - private void ResetDataToCreate() - { - this.BookToCreate = new(Guid.NewGuid(), null, null); - this.SectionToCreate = new(Guid.NewGuid(), null, null); - this.PageToCreate = new(Guid.NewGuid(), null, null); - } - - /// - /// Handler for when the selected book changed - /// - private void OnSelectedBookChanged() - { - this.SelectedSection = null; - this.SelectedPage = null; - } - - /// - /// Handler for when the selected section changed - /// - private void OnSelectedSectionChanged() - { - this.SelectedPage = null; - } - /// /// Validates that the thing is a valid thing for the operations in this ViewModel /// diff --git a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs index 8d50f701..f876177c 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs @@ -70,26 +70,6 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// List ActiveDomains { get; set; } - /// - /// Gets or sets if the ViewModel is on book creation state - /// - bool IsOnBookCreation { get; set; } - - /// - /// Gets or sets if the ViewModel is on section creation state - /// - bool IsOnSectionCreation { get; set; } - - /// - /// Gets or sets if the ViewModel is on page creation state - /// - bool IsOnPageCreation { get; set; } - - /// - /// Gets or sets if the ViewModel is on node creation state - /// - bool IsOnNoteCreation { get; set; } - /// /// Gets or sets if the ViewModel is on edit mode /// @@ -100,26 +80,6 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// bool IsOnCreateMode { get; set; } - /// - /// Gets or sets the that's about to be created - /// - Book BookToCreate { get; set; } - - /// - /// Gets or sets the that's about to be created - /// - Section SectionToCreate { get; set; } - - /// - /// Gets or sets the that's about to be created - /// - Page PageToCreate { get; set; } - - /// - /// Gets or sets the that's about to be created - /// - Note NoteToCreate { get; set; } - /// /// Gets or sets the thing to be edited /// @@ -129,22 +89,7 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// Gets or sets the thing to be created /// Thing ThingToCreate { get; set; } - - /// - /// Gets or sets the for when an item is created - /// - EventCallback OnCreateItem { get; set; } - - /// - /// Gets or sets the for when an item has canceled it's creation - /// - EventCallback OnCancelCreateItem { get; set; } - - /// - /// Resets the states for creation modes - /// - void ResetCreationStates(); - + /// /// Sets the thing to be created /// From 1d20b5a73e0feee68cb35e134e1b78105ee30af2 Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Wed, 20 Sep 2023 13:49:39 +0200 Subject: [PATCH 13/20] Add confirm cancel popup. Add validation messages --- .../Components/BookEditor/EditorPopup.razor | 2 +- .../BookEditor/EditorPopup.razor.cs | 54 +++++- .../Components/BookEditor/InputEditor.razor | 11 +- .../BookEditor/InputEditor.razor.cs | 4 +- .../BookEditor/InputEditor.razor.css | 18 -- .../Services/ValidationService.cs | 25 +-- COMET.Web.Common/wwwroot/css/styles.css | 21 ++- .../BookEditor/BookEditorBody.razor | 35 +--- .../BookEditor/BookEditorBody.razor.cs | 39 +---- .../BookEditor/BookEditorBodyViewModel.cs | 165 ++++++++++++++---- .../BookEditor/IBookEditorBodyViewModel.cs | 44 +++-- 11 files changed, 247 insertions(+), 171 deletions(-) delete mode 100644 COMET.Web.Common/Components/BookEditor/InputEditor.razor.css diff --git a/COMET.Web.Common/Components/BookEditor/EditorPopup.razor b/COMET.Web.Common/Components/BookEditor/EditorPopup.razor index 8f2bf844..6603d0c1 100644 --- a/COMET.Web.Common/Components/BookEditor/EditorPopup.razor +++ b/COMET.Web.Common/Components/BookEditor/EditorPopup.razor @@ -22,7 +22,7 @@ @namespace COMET.Web.Common.Components.BookEditor @inherits DisposableComponent - +
diff --git a/COMET.Web.Common/Components/BookEditor/EditorPopup.razor.cs b/COMET.Web.Common/Components/BookEditor/EditorPopup.razor.cs index 8f74a282..3f21c934 100644 --- a/COMET.Web.Common/Components/BookEditor/EditorPopup.razor.cs +++ b/COMET.Web.Common/Components/BookEditor/EditorPopup.razor.cs @@ -24,14 +24,18 @@ // -------------------------------------------------------------------------------------------------------------------- namespace COMET.Web.Common.Components.BookEditor -{ - using CDP4Common.Helpers; - +{ + using CDP4Common.ReportingData; + using COMET.Web.Common.Services; using COMET.Web.Common.ViewModels.Components.BookEditor; using Microsoft.AspNetCore.Components; - + + using INamedThing = CDP4Common.CommonData.INamedThing; + using IOwnedThing = CDP4Common.EngineeringModelData.IOwnedThing; + using IShortNamedThing = CDP4Common.CommonData.IShortNamedThing; + /// /// Support class for the BookEditorPopup component /// @@ -62,7 +66,47 @@ protected override async Task OnInitializedAsync() /// private async Task OnConfirmClick() { - var validationErrors = ValidationService.ValidateThing(this.ViewModel.Item); + var validationErrors = new List(); + + if (this.ViewModel.Item is INamedThing namedThing) + { + var error = ValidationService.ValidateProperty(nameof(namedThing.Name), namedThing.Name); + + if (!string.IsNullOrEmpty(error)) + { + validationErrors.Add(error); + } + } + + if (this.ViewModel.Item is IShortNamedThing shortNamedThing) + { + var error = ValidationService.ValidateProperty(nameof(shortNamedThing.ShortName), shortNamedThing.ShortName); + + if (!string.IsNullOrEmpty(error)) + { + validationErrors.Add(error); + } + } + + if (this.ViewModel.Item is IOwnedThing ownedThing) + { + var error = ownedThing.Owner == null ? "The thing must be owned by a DoE" : string.Empty; + + if (!string.IsNullOrEmpty(error)) + { + validationErrors.Add(error); + } + } + + if (this.ViewModel.Item is TextualNote textualNote) + { + var error = string.IsNullOrEmpty(textualNote.Content)? "The textual note must contain a content" : string.Empty; + + if (!string.IsNullOrEmpty(error)) + { + validationErrors.Add(error); + } + } this.ViewModel.ValidationErrors.Edit(inner => { diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor b/COMET.Web.Common/Components/BookEditor/InputEditor.razor index a0104a83..8ae469bf 100644 --- a/COMET.Web.Common/Components/BookEditor/InputEditor.razor +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor @@ -62,9 +62,12 @@ @if (this.Item is TextualNote textualNote) { - +
+

Content:

+ +
}
@@ -83,6 +86,4 @@
- - diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs index 56efc03d..ae083d22 100644 --- a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs @@ -24,7 +24,9 @@ // -------------------------------------------------------------------------------------------------------------------- namespace COMET.Web.Common.Components.BookEditor -{ +{ + using CDP4Common.CommonData; + using CDP4Common.EngineeringModelData; using CDP4Common.SiteDirectoryData; using Microsoft.AspNetCore.Components; diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.css b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.css deleted file mode 100644 index 8a530845..00000000 --- a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.css +++ /dev/null @@ -1,18 +0,0 @@ -.tab-content { - display: flex; - flex-direction: column; - align-items: start; - justify-content: flex-start; - row-gap: 5px; - padding:1%; -} - -.editor-row{ - align-items:center; - justify-content:space-between; -} - -.editor-row>p{ - width: 25%; - margin:0; -} \ No newline at end of file diff --git a/COMET.Web.Common/Services/ValidationService.cs b/COMET.Web.Common/Services/ValidationService.cs index 974bbc02..07477f78 100644 --- a/COMET.Web.Common/Services/ValidationService.cs +++ b/COMET.Web.Common/Services/ValidationService.cs @@ -31,7 +31,7 @@ namespace COMET.Web.Common.Services using System.Text.RegularExpressions; /// - /// The purpose of the is to check and report on the validity of a field in a + /// The purpose of the is to check and report on the validity of a field in a form /// public static class ValidationService { @@ -76,28 +76,7 @@ public static class ValidationService { "EnumerationValueDefinitionShortName", new ValidationRule { PropertyName = "ShortName", Rule = @"^([^()\s][\S]*)$", ErrorText = "The ShortName can not be empty or contain a whitespace." } }, { "EnumerationValueDefinitionName", new ValidationRule { PropertyName = "Name", Rule = @"^([^()\s][\S]*)$", ErrorText = "The Name can not be empty or contain a whitespace." } } }; - - /// - /// Validate all the properties of a using REFLECTION! - /// - /// the thing to validate - /// the collection of errors if any - public static IEnumerable ValidateThing(Thing thing) - { - var properties = thing.GetType().GetProperties(); - - var validationErrors = new List(); - - foreach (var property in properties) - { - var propertyValue = property.GetValue(thing); - var validationError = ValidateProperty(property.Name, propertyValue); - validationErrors.Add(validationError); - } - - return validationErrors; - } - + /// /// Validate a property of a /// diff --git a/COMET.Web.Common/wwwroot/css/styles.css b/COMET.Web.Common/wwwroot/css/styles.css index 3b455b82..640dd0f8 100644 --- a/COMET.Web.Common/wwwroot/css/styles.css +++ b/COMET.Web.Common/wwwroot/css/styles.css @@ -502,4 +502,23 @@ #dashboard-title { text-align: center; color: #16386f; -} \ No newline at end of file +} + +.tab-content { + display: flex; + flex-direction: column; + align-items: start; + justify-content: flex-start; + row-gap: 5px; + padding: 1%; +} + +.editor-row { + align-items: center; + justify-content: space-between; +} + + .editor-row > p { + width: 25%; + margin: 0; + } \ No newline at end of file diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor b/COMETwebapp/Components/BookEditor/BookEditorBody.razor index 304bbb23..ad2c8dc0 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor @@ -26,30 +26,10 @@ @inherits SingleIterationApplicationBase - - - - - - - - - - - - - - - - - + + +
@@ -62,7 +42,7 @@ CssClass="book-nodes" OnCreateNewItemClick="@(() => this.ViewModel.SetThingToCreate(new Book()))" OnEditClicked="@((b) => this.ViewModel.SetThingToEdit(b))" - OnDeleteClicked="@this.ViewModel.OnDeleteThing"> + OnDeleteClicked="@((b) => this.ViewModel.SetThingToDelete(b))"> @context.Name @@ -77,7 +57,7 @@ CssClass="section-nodes" OnCreateNewItemClick="@(() => this.ViewModel.SetThingToCreate(new Section()))" OnEditClicked="@((s) => this.ViewModel.SetThingToEdit(s))" - OnDeleteClicked="@this.ViewModel.OnDeleteThing"> + OnDeleteClicked="@((s) => this.ViewModel.SetThingToDelete(s))"> @context.Name @@ -92,7 +72,7 @@ CssClass="page-nodes" OnCreateNewItemClick="@(() => this.ViewModel.SetThingToCreate(new Page()))" OnEditClicked="@((p) => this.ViewModel.SetThingToEdit(p))" - OnDeleteClicked="@this.ViewModel.OnDeleteThing"> + OnDeleteClicked="@((p) => this.ViewModel.SetThingToDelete(p))"> @context.Name @@ -100,12 +80,13 @@ + OnDeleteClicked="@((n) => this.ViewModel.SetThingToDelete(n))"> @if (context is TextualNote textualNote) { diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs index b611210c..542360a8 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor.cs @@ -24,10 +24,6 @@ namespace COMETwebapp.Components.BookEditor { - using CDP4Common.ReportingData; - - using COMET.Web.Common.Components.BookEditor; - using ReactiveUI; /// @@ -66,40 +62,9 @@ protected override void OnInitialized() { base.OnInitialized(); - this.Disposables.Add(this.WhenAnyValue(x => x.ViewModel.IsOnEditMode, - x => x.ViewModel.IsOnCreateMode) + this.Disposables.Add(this.WhenAnyValue(x => x.ViewModel.EditorPopupViewModel.IsVisible, + x => x.ViewModel.ConfirmCancelPopupViewModel.IsVisible) .Subscribe(_ => this.InvokeAsync(this.StateHasChanged))); } - - /// - /// Gets the text of the header depending on the state of the ViewModel - /// - /// - private string GetHeaderText() - { - if (this.ViewModel.IsOnCreateMode) - { - switch (this.ViewModel.ThingToCreate) - { - case Book: return "Create a new Book"; - case Section: return "Create a new Section"; - case Page: return "Create a new Page"; - case Note: return "Create a new Note"; - } - } - - if (this.ViewModel.IsOnEditMode) - { - switch (this.ViewModel.ThingToEdit) - { - case Book: return "Edit the Book"; - case Section: return "Edit the Section"; - case Page: return "Edit the Page"; - case Note: return "Edit the Note"; - } - } - - return string.Empty; - } } } diff --git a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs index 9aafba9d..b13b9c88 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/BookEditorBodyViewModel.cs @@ -33,9 +33,12 @@ namespace COMETwebapp.ViewModels.Components.BookEditor using COMET.Web.Common.Services.SessionManagement; using COMET.Web.Common.ViewModels.Components; + using COMET.Web.Common.ViewModels.Components.BookEditor; using DynamicData; + using Microsoft.AspNetCore.Components; + using ReactiveUI; /// @@ -43,16 +46,6 @@ namespace COMETwebapp.ViewModels.Components.BookEditor /// public class BookEditorBodyViewModel : SingleIterationApplicationBaseViewModel, IBookEditorBodyViewModel { - /// - /// Backing field for the property - /// - private bool isOnEditMode; - - /// - /// Backing field for the property - /// - private bool isOnCreateMode; - /// /// Backing field for the property /// @@ -68,6 +61,11 @@ public class BookEditorBodyViewModel : SingleIterationApplicationBaseViewModel, /// private Page selectedPage; + /// + /// Backing field for the property + /// + private Note selectedNote; + /// /// Gets or sets the current selected /// @@ -95,6 +93,15 @@ public Page SelectedPage set => this.RaiseAndSetIfChanged(ref this.selectedPage, value); } + /// + /// Gets or sets the current selected + /// + public Note SelectedNote + { + get => this.selectedNote; + set => this.RaiseAndSetIfChanged(ref this.selectedNote, value); + } + /// /// Gets or sets the collection of available for this /// @@ -111,32 +118,29 @@ public Page SelectedPage public List ActiveDomains { get; set; } = new(); /// - /// Gets or sets if the ViewModel is on edit mode + /// Gets or sets the thing to be edited /// - public bool IsOnEditMode - { - get => this.isOnEditMode; - set => this.RaiseAndSetIfChanged(ref this.isOnEditMode, value); - } + public Thing ThingToEdit { get; set; } /// - /// Gets or sets if the ViewModel is on create mode + /// Gets or sets the thing to be created /// - public bool IsOnCreateMode - { - get => this.isOnCreateMode; - set => this.RaiseAndSetIfChanged(ref this.isOnCreateMode, value); - } + public Thing ThingToCreate { get; set; } /// - /// Gets or sets the thing to be edited + /// Gets or sets the thing to be deleted /// - public Thing ThingToEdit { get; set; } + public Thing ThingToDelete { get; set; } /// - /// Gets or sets the thing to be created + /// Gets or sets the /// - public Thing ThingToCreate { get; set; } + public IEditorPopupViewModel EditorPopupViewModel { get; set; } + + /// + /// Gets or sets the + /// + public IConfirmCancelPopupViewModel ConfirmCancelPopupViewModel { get; set; } /// /// Initializes a new instance of the class. @@ -146,6 +150,22 @@ public BookEditorBodyViewModel(ISessionService sessionService) : base(sessionSer { this.Disposables.Add(this.WhenAnyValue(x => x.SelectedBook).Subscribe(_ => this.OnSelectedBookChanged())); this.Disposables.Add(this.WhenAnyValue(x => x.SelectedSection).Subscribe(_ => this.OnSelectedSectionChanged())); + this.Disposables.Add(this.WhenAnyValue(x => x.SelectedPage).Subscribe(_ => this.OnSelectedPageChanged())); + + this.EditorPopupViewModel = new EditorPopupViewModel(); + + this.ConfirmCancelPopupViewModel = new ConfirmCancelPopupViewModel + { + ContentText = "Are you sure you want to delete the thing?", + }; + + this.ConfirmCancelPopupViewModel.OnCancel = new EventCallbackFactory().Create(this, () => this.ConfirmCancelPopupViewModel.IsVisible = false); + + this.ConfirmCancelPopupViewModel.OnConfirm = new EventCallbackFactory().Create(this, async () => + { + await this.OnDeleteThing(); + this.ConfirmCancelPopupViewModel.IsVisible = false; + }); } /// @@ -154,7 +174,6 @@ public BookEditorBodyViewModel(ISessionService sessionService) : base(sessionSer private void OnSelectedBookChanged() { this.SelectedSection = null; - this.SelectedPage = null; } /// @@ -165,6 +184,14 @@ private void OnSelectedSectionChanged() this.SelectedPage = null; } + /// + /// Handler for when the selected page changed + /// + private void OnSelectedPageChanged() + { + this.SelectedNote = null; + } + /// /// Handles the refresh of the current /// @@ -199,6 +226,9 @@ protected override async Task OnIterationChanged() this.AvailableCategories.AddRange(categories); } + this.EditorPopupViewModel.AvailableCategories = this.AvailableCategories; + this.EditorPopupViewModel.ActiveDomains = this.ActiveDomains; + this.IsLoading = false; } @@ -223,7 +253,30 @@ public void SetThingToCreate(Thing thing) this.ValidateThing(thing); this.ThingToCreate = thing; - this.IsOnCreateMode = true; + + var header = ""; + + switch (this.ThingToCreate) + { + case Book: + header = "Create a new Book"; + break; + case Section: + header = "Create a new Section"; + break; + case Page: + header = "Create a new Page"; + break; + case Note: + header = "Create a new Note"; + break; + } + + this.EditorPopupViewModel.HeaderText = header; + this.EditorPopupViewModel.Item = this.ThingToCreate; + this.EditorPopupViewModel.OnConfirmClick = new EventCallbackFactory().Create(this, this.OnCreateThing); + this.EditorPopupViewModel.OnCancelClick = new EventCallbackFactory().Create(this, () => this.EditorPopupViewModel.IsVisible = false); + this.EditorPopupViewModel.IsVisible = true; } /// @@ -258,14 +311,13 @@ public async Task OnCreateThing() default: this.ThingToCreate = null; - this.IsOnCreateMode = false; return; } await this.SessionService.CreateThing(thingContainer.Clone(false), this.ThingToCreate); this.ThingToCreate = null; - this.IsOnCreateMode = false; + this.EditorPopupViewModel.IsVisible = false; } /// @@ -277,7 +329,30 @@ public void SetThingToEdit(Thing thing) this.ValidateThing(thing); this.ThingToEdit = thing; - this.IsOnEditMode = true; + + var header = ""; + + switch (this.ThingToEdit) + { + case Book: + header = "Edit the Book"; + break; + case Section: + header = "Edit the Section"; + break; + case Page: + header = "Edit the Page"; + break; + case Note: + header = "Edit the Note"; + break; + } + + this.EditorPopupViewModel.HeaderText = header; + this.EditorPopupViewModel.Item = this.ThingToEdit; + this.EditorPopupViewModel.OnConfirmClick = new EventCallbackFactory().Create(this, this.OnEditThing); + this.EditorPopupViewModel.OnCancelClick = new EventCallbackFactory().Create(this, () => this.EditorPopupViewModel.IsVisible = false); + this.EditorPopupViewModel.IsVisible = true; } /// @@ -299,25 +374,39 @@ public async Task OnEditThing() await this.SessionService.UpdateThing(thingContainerClone, this.ThingToEdit.Clone(false)); this.ThingToEdit = null; - this.IsOnEditMode = false; + this.EditorPopupViewModel.IsVisible = false; + } + + /// + /// Sets the thing to be deleted + /// + /// the thing + public void SetThingToDelete(Thing thingToDelete) + { + this.ValidateThing(thingToDelete); + + this.ThingToDelete = thingToDelete; + + this.ConfirmCancelPopupViewModel.IsVisible = true; } /// /// Hanlder for when the user request to delete a thing (Book,Section,Page or Note) /// - /// the thing to delete /// an asynchronous operation - public async Task OnDeleteThing(Thing thing) + public async Task OnDeleteThing() { - if (thing is not Book && thing is not Section && thing is not Page && thing is not Note) + if (this.ThingToDelete == null) { - throw new ArgumentException("The thing to delete should be a (Book, Section, Page or Note)", nameof(thing)); + throw new InvalidOperationException("The thing to delete can't be null"); } - var thingContainer = thing.Container; + this.ValidateThing(this.ThingToDelete); + + var thingContainer = this.ThingToDelete.Container; var thingContainerClone = thingContainer.Clone(false); - await this.SessionService.DeleteThing(thingContainerClone, thing.Clone(false)); + await this.SessionService.DeleteThing(thingContainerClone, this.ThingToDelete.Clone(false)); await this.OnIterationChanged(); } } diff --git a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs index f876177c..3cca665e 100644 --- a/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs +++ b/COMETwebapp/ViewModels/Components/BookEditor/IBookEditorBodyViewModel.cs @@ -30,10 +30,9 @@ namespace COMETwebapp.ViewModels.Components.BookEditor using CDP4Common.SiteDirectoryData; using COMET.Web.Common.ViewModels.Components; - + using COMET.Web.Common.ViewModels.Components.BookEditor; + using DynamicData; - - using Microsoft.AspNetCore.Components; /// /// ViewModel for the BookEditorBody component @@ -55,6 +54,11 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// Page SelectedPage { get; set; } + /// + /// Gets or sets the current selected + /// + Note SelectedNote { get; set; } + /// /// Gets or sets the collection of available for this /// @@ -71,25 +75,30 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM List ActiveDomains { get; set; } /// - /// Gets or sets if the ViewModel is on edit mode + /// Gets or sets the thing to be edited /// - bool IsOnEditMode { get; set; } + Thing ThingToEdit { get; set; } /// - /// Gets or sets if the ViewModel is on create mode + /// Gets or sets the thing to be created /// - bool IsOnCreateMode { get; set; } + Thing ThingToCreate { get; set; } /// - /// Gets or sets the thing to be edited + /// Gets or sets the thing to be deleted /// - Thing ThingToEdit { get; set; } + Thing ThingToDelete { get; set; } /// - /// Gets or sets the thing to be created + /// Gets or sets the /// - Thing ThingToCreate { get; set; } - + IEditorPopupViewModel EditorPopupViewModel { get; set; } + + /// + /// Gets or sets the + /// + IConfirmCancelPopupViewModel ConfirmCancelPopupViewModel { get; set; } + /// /// Sets the thing to be created /// @@ -102,13 +111,18 @@ public interface IBookEditorBodyViewModel : ISingleIterationApplicationBaseViewM /// Task OnCreateThing(); + /// + /// Sets the thing to be deleted + /// + /// the thing + void SetThingToDelete(Thing thingToDelete); + /// /// Hanlder for when the user request to delete a thing (Book,Section,Page or Note) /// - /// the thing to delete /// an asynchronous operation - Task OnDeleteThing(Thing thing); - + Task OnDeleteThing(); + /// /// Sets the thing to be edited /// From 67592065a3f99d5fea19bb2c528efe9471613cb8 Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Wed, 20 Sep 2023 14:03:49 +0200 Subject: [PATCH 14/20] Disable button is previous item is not selected --- .../BookEditor/BookEditorBody.razor | 9 ++- .../BookEditor/BookEditorColumn.razor | 7 +- .../BookEditor/BookEditorColumn.razor.cs | 8 ++- .../BookEditor/BookEditorColumn.razor.css | 68 ++----------------- 4 files changed, 19 insertions(+), 73 deletions(-) diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor b/COMETwebapp/Components/BookEditor/BookEditorBody.razor index ad2c8dc0..189c6028 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor @@ -57,7 +57,8 @@ CssClass="section-nodes" OnCreateNewItemClick="@(() => this.ViewModel.SetThingToCreate(new Section()))" OnEditClicked="@((s) => this.ViewModel.SetThingToEdit(s))" - OnDeleteClicked="@((s) => this.ViewModel.SetThingToDelete(s))"> + OnDeleteClicked="@((s) => this.ViewModel.SetThingToDelete(s))" + IsAddButtonDisabled="@(this.ViewModel.SelectedBook == null)"> @context.Name @@ -72,7 +73,8 @@ CssClass="page-nodes" OnCreateNewItemClick="@(() => this.ViewModel.SetThingToCreate(new Page()))" OnEditClicked="@((p) => this.ViewModel.SetThingToEdit(p))" - OnDeleteClicked="@((p) => this.ViewModel.SetThingToDelete(p))"> + OnDeleteClicked="@((p) => this.ViewModel.SetThingToDelete(p))" + IsAddButtonDisabled="@(this.ViewModel.SelectedSection == null)"> @context.Name @@ -86,7 +88,8 @@ CssClass="note-nodes" OnCreateNewItemClick="@(() => this.ViewModel.SetThingToCreate(new TextualNote(){ LanguageCode = "es" }))" OnEditClicked="@((n) => this.ViewModel.SetThingToEdit(n))" - OnDeleteClicked="@((n) => this.ViewModel.SetThingToDelete(n))"> + OnDeleteClicked="@((n) => this.ViewModel.SetThingToDelete(n))" + IsAddButtonDisabled="@(this.ViewModel.SelectedPage == null)"> @if (context is TextualNote textualNote) { diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor index 8e1553b0..b9203249 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor @@ -25,13 +25,14 @@ @{ var columnConditionalClass = this.IsCollapsed ? "collapsed" : ""; + var buttonDisabledClass = this.IsAddButtonDisabled ? "disabled-button" : ""; }
@this.HeaderTitle
- +
@if (this.Items != null && this.Items.Any()) @@ -41,11 +42,7 @@ @{ var isSelected = item.Equals(this.SelectedValue); - var isFirst = item.Equals(this.Items.First()) && this.Items.Count > 1; - var isLast = item.Equals(this.Items.Last()) && this.Items.Count > 1; var conditionalClass = isSelected ? "selected" : ""; - var firstClass = isFirst ? "first" : ""; - var lastClass = isLast ? "last" : ""; var index = this.Items.IndexOf(item); diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs index 9d50cad5..7e34cfcb 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs @@ -140,7 +140,13 @@ public partial class BookEditorColumn ///
[Parameter] public EventCallback OnDeleteClicked { get; set; } - + + /// + /// Gets or sets if the add button is disabled + /// + [Parameter] + public bool IsAddButtonDisabled { get; set; } + /// /// Method invoked when the component is ready to start, having received its /// initial parameters from its parent in the render tree. diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css index ed0753aa..5493157c 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.css @@ -42,9 +42,13 @@ .add-item-button,.collapse-button { border: 0; + border-radius: 5px; background-color: transparent; } +.add-item-button.disabled-button{ + background-color: #DDD; +} .column-content { width: 100%; @@ -96,70 +100,6 @@ box-shadow: 0px 0px 0px 2px #333388 !important; } -.vertical-line { - position:absolute; - left:10%; - background-color: transparent; - height: 100%; - border: 0; - border-left: 2px dashed #555; -} - - .vertical-line.first { - height: 50%; - transform: translateY(50%); - } - - .vertical-line.right-side{ - left:85%; - } - - .vertical-line.last { - height: 50%; - transform: translateY(-50%); - } - -.horizontal-line { - background-color: transparent; - margin: 0; - position: absolute; - border: 0; - border-top: 2px dashed #555; - width: 50%; - left: 10%; -} - - .horizontal-line.first { - width: 50%; - left: 0%; - } - - .horizontal-line.right-side { - left: 35%; - } - - .horizontal-line.first.full-width{ - width:50%; - } - - .horizontal-line.first.right-side { - left: 50%; - } - - .horizontal-line.first-slice { - display:none; - width: 50%; - left: 0%; - border-top: 2px dashed red; - } - - .horizontal-line.first.first-slice { - display:block; - width: 20%; - left: 0%; - border-top: 2px dashed red; - } - .edit-node-container{ display: flex; flex-direction: column; From 800ee051254eeb6770df9fd0dd2d4efe1f1bfc23 Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Wed, 20 Sep 2023 15:53:44 +0200 Subject: [PATCH 15/20] call on size changed when window resizes --- .../BookEditor/BookEditorColumn.razor.cs | 15 ++++++++++--- COMETwebapp/Pages/BookEditor/BookEditor.razor | 4 ++-- .../Interoperability/DomDataService.cs | 12 +++++++++++ .../Interoperability/IDomDataService.cs | 11 ++++++++++ COMETwebapp/wwwroot/Scripts/DomData.js | 21 ++++++++++++++++--- 5 files changed, 55 insertions(+), 8 deletions(-) diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs index 7e34cfcb..6cdac23e 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor.cs @@ -158,7 +158,9 @@ public partial class BookEditorColumn protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - await this.DomDataService.SubscribeToResizeEvent(nameof(OnSizeChanged)); + var dotnetReference = DotNetObjectReference.Create(this); + await this.DomDataService.LoadDotNetHelper(dotnetReference); + await this.DomDataService.SubscribeToResizeEvent("OnSizeChanged"); } /// @@ -169,6 +171,7 @@ protected override async Task OnInitializedAsync() /// an asynchronous operation private async Task OnSelectedValueChanged(TItem item, int itemIndex) { + this.SelectedValue = item; this.firstItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(0, this.CssClass, false); this.lastItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(this.Items.Count - 1, this.CssClass, false); this.selectedItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(itemIndex, this.CssClass, true); @@ -256,10 +259,16 @@ private string GenerateHorizontalPathPoints() return $"{x1},{y},{x2},{y}"; } + /// + /// Method called when the size of the window changes + /// + /// an asynchronous operation [JSInvokable] - public static async Task OnSizeChanged() + public async Task OnSizeChanged() { - + this.firstItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(0, this.CssClass, false); + this.lastItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(this.Items.Count - 1, this.CssClass, false); + this.selectedItemSizeAndPosition = await this.DomDataService.GetElementSizeAndPosition(0, this.CssClass, true); } } } diff --git a/COMETwebapp/Pages/BookEditor/BookEditor.razor b/COMETwebapp/Pages/BookEditor/BookEditor.razor index 07a71800..a94b5ff3 100644 --- a/COMETwebapp/Pages/BookEditor/BookEditor.razor +++ b/COMETwebapp/Pages/BookEditor/BookEditor.razor @@ -26,6 +26,6 @@ Copyright (c) 2023 RHEA System S.A. - + - \ No newline at end of file + diff --git a/COMETwebapp/Services/Interoperability/DomDataService.cs b/COMETwebapp/Services/Interoperability/DomDataService.cs index 8268da84..bec8f988 100644 --- a/COMETwebapp/Services/Interoperability/DomDataService.cs +++ b/COMETwebapp/Services/Interoperability/DomDataService.cs @@ -24,6 +24,8 @@ namespace COMETwebapp.Services.Interoperability { + using COMETwebapp.Components.BookEditor; + using COMETwebapp.Components.ModelEditor; using Microsoft.JSInterop; /// @@ -39,6 +41,16 @@ public DomDataService(IJSRuntime jsRuntime) : base(jsRuntime) { } + /// + /// Set the dotnet helper + /// + /// the dotnet helper + /// A + public async Task LoadDotNetHelper(DotNetObjectReference> dotNetHelper) + { + await this.JsRuntime.InvokeVoidAsync("setDotNetHelper", dotNetHelper); + } + /// /// Gets an element size and position /// diff --git a/COMETwebapp/Services/Interoperability/IDomDataService.cs b/COMETwebapp/Services/Interoperability/IDomDataService.cs index ea818dbf..80a94463 100644 --- a/COMETwebapp/Services/Interoperability/IDomDataService.cs +++ b/COMETwebapp/Services/Interoperability/IDomDataService.cs @@ -24,6 +24,10 @@ namespace COMETwebapp.Services.Interoperability { + using COMETwebapp.Components.BookEditor; + + using Microsoft.JSInterop; + /// /// The service used to retrieve several data from the DOM /// @@ -44,5 +48,12 @@ public interface IDomDataService /// the callback method name /// an asynchronous operation Task SubscribeToResizeEvent(string callbackMethodName); + + /// + /// Set the dotnet helper + /// + /// the dotnet helper + /// A + Task LoadDotNetHelper(DotNetObjectReference> dotNetHelper); } } diff --git a/COMETwebapp/wwwroot/Scripts/DomData.js b/COMETwebapp/wwwroot/Scripts/DomData.js index 521aa617..80c7ce2b 100644 --- a/COMETwebapp/wwwroot/Scripts/DomData.js +++ b/COMETwebapp/wwwroot/Scripts/DomData.js @@ -1,4 +1,12 @@ -function GetElementSizeAndPosition(index, cssSelector, useScroll) +/** + * Sets the dotnet helper + * @param {any} helper + */ +function setDotNetHelper(helper) { + dotNetHelper = helper; +} + +function GetElementSizeAndPosition(index, cssSelector, useScroll) { var elements = document.getElementsByClassName(cssSelector); var element = elements[index]; @@ -24,9 +32,16 @@ return [0, 0, 0, 0]; } -function SubscribeToResizeEvent(callbackMethodName) { +function SubscribeToResizeEvent(callbackMethodName) +{ window.addEventListener("resize", () => { - DotNet.invokeMethodAsync('COMETwebapp', callbackMethodName); + try + { + dotNetHelper.invokeMethodAsync(callbackMethodName); + } + catch (error) { + console.log(error); + } }); } \ No newline at end of file From 52d79776712d0170654b3a018e37a2ddbf6703da Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Thu, 21 Sep 2023 09:49:01 +0200 Subject: [PATCH 16/20] Add webapp tests --- .../BookEditor/BookEditorBodyTestFixture.cs | 117 +++++++++ .../BookEditor/BookEditorColumnTestFixture.cs | 150 +++++++++++ .../BookEditorBodyViewModelTestFixture.cs | 243 ++++++++++++++++++ .../BookEditor/BookEditorBody.razor | 16 +- .../BookEditor/BookEditorColumn.razor | 4 +- .../BookEditor/BookEditorColumn.razor.css | 4 +- .../BookEditor/BookEditorBodyViewModel.cs | 3 +- 7 files changed, 524 insertions(+), 13 deletions(-) create mode 100644 COMETwebapp.Tests/Components/BookEditor/BookEditorBodyTestFixture.cs create mode 100644 COMETwebapp.Tests/Components/BookEditor/BookEditorColumnTestFixture.cs create mode 100644 COMETwebapp.Tests/ViewModels/Components/BookEditor/BookEditorBodyViewModelTestFixture.cs diff --git a/COMETwebapp.Tests/Components/BookEditor/BookEditorBodyTestFixture.cs b/COMETwebapp.Tests/Components/BookEditor/BookEditorBodyTestFixture.cs new file mode 100644 index 00000000..d992312c --- /dev/null +++ b/COMETwebapp.Tests/Components/BookEditor/BookEditorBodyTestFixture.cs @@ -0,0 +1,117 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.Tests.Components.BookEditor +{ + using Bunit; + + using CDP4Common.EngineeringModelData; + using CDP4Common.ReportingData; + using CDP4Common.SiteDirectoryData; + + using COMET.Web.Common.Services.SessionManagement; + using COMET.Web.Common.Test.Helpers; + using COMET.Web.Common.ViewModels.Components; + using COMET.Web.Common.ViewModels.Components.BookEditor; + + using COMETwebapp.Components.BookEditor; + using COMETwebapp.Services.Interoperability; + using COMETwebapp.ViewModels.Components.BookEditor; + + using DynamicData; + + using Microsoft.Extensions.DependencyInjection; + + using Moq; + + using NUnit.Framework; + + using TestContext = Bunit.TestContext; + + [TestFixture] + public class BookEditorBodyTestFixture + { + private TestContext context; + private IRenderedComponent component; + private Mock viewModel; + private Mock sessionService; + + [SetUp] + public void Setup() + { + this.context = new TestContext(); + this.context.ConfigureDevExpressBlazor(); + this.sessionService = new Mock(); + + this.viewModel = new Mock(); + this.viewModel.Setup(x => x.CurrentIteration).Returns(new Iteration()); + this.viewModel.Setup(x => x.CurrentDomain).Returns(new DomainOfExpertise()); + this.viewModel.Setup(x => x.AvailableBooks).Returns(new SourceList()); + + var editorPopupViewModel = new Mock(); + editorPopupViewModel.Setup(x => x.ValidationErrors).Returns(new SourceList()); + editorPopupViewModel.Setup(x => x.Item).Returns(new Book()); + editorPopupViewModel.Setup(x => x.ActiveDomains).Returns(new List()); + editorPopupViewModel.Setup(x => x.AvailableCategories).Returns(new List()); + + this.viewModel.Setup(x => x.EditorPopupViewModel).Returns(editorPopupViewModel.Object); + + var confirmCancelPopupViewModel = new Mock(); + + this.viewModel.Setup(x => x.ConfirmCancelPopupViewModel).Returns(confirmCancelPopupViewModel.Object); + + var domDataService = new Mock(); + + this.context.Services.AddSingleton(this.viewModel.Object); + this.context.Services.AddSingleton(this.sessionService.Object); + this.context.Services.AddSingleton(domDataService.Object); + + this.component = this.context.RenderComponent(); + } + + [TearDown] + public void Teardown() + { + this.context.CleanContext(); + } + + [Test] + public void VerifyComponent() + { + var bookEditorColumn = this.component.FindComponent>(); + var sectionEditorColumn = this.component.FindComponent>(); + var pageEditorColumn = this.component.FindComponent>(); + var noteEditorColumn = this.component.FindComponent>(); + + Assert.Multiple(() => + { + Assert.That(bookEditorColumn.Instance, Is.Not.Null); + Assert.That(sectionEditorColumn.Instance, Is.Not.Null); + Assert.That(pageEditorColumn.Instance, Is.Not.Null); + Assert.That(noteEditorColumn.Instance, Is.Not.Null); + }); + } + } +} + diff --git a/COMETwebapp.Tests/Components/BookEditor/BookEditorColumnTestFixture.cs b/COMETwebapp.Tests/Components/BookEditor/BookEditorColumnTestFixture.cs new file mode 100644 index 00000000..21d4ec93 --- /dev/null +++ b/COMETwebapp.Tests/Components/BookEditor/BookEditorColumnTestFixture.cs @@ -0,0 +1,150 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.Tests.Components.BookEditor +{ + using Bunit; + + using CDP4Common.ReportingData; + using CDP4Common.SiteDirectoryData; + + using COMET.Web.Common.Test.Helpers; + + using COMETwebapp.Components.BookEditor; + using COMETwebapp.Services.Interoperability; + + using Microsoft.AspNetCore.Components; + using Microsoft.Extensions.DependencyInjection; + + using Moq; + + using NUnit.Framework; + + using TestContext = Bunit.TestContext; + + [TestFixture] + public class BookEditorColumnTestFixture + { + private TestContext context; + private IRenderedComponent> component; + private Mock domDataService; + private bool editIsClicked; + private bool deleteIsClicked; + private bool isCollapsed; + private bool addNewItemIsClicked; + private Book selectedBook; + + [SetUp] + public void Setup() + { + this.context = new TestContext(); + this.context.ConfigureDevExpressBlazor(); + + this.domDataService = new Mock(); + + this.context.Services.AddSingleton(this.domDataService.Object); + + var book = new Book() + { + Name = "Book Example", + ShortName = "BookExample", + Owner = new DomainOfExpertise + { + Name = "Sys", + ShortName = "sys" + } + }; + + var onEditClicked = new EventCallbackFactory().Create(this, () => { this.editIsClicked = true;}); + var onDeleteClicked = new EventCallbackFactory().Create(this, () => { this.deleteIsClicked = true; }); + var onSelectedValueClicked = new EventCallbackFactory().Create(this, (b) => { this.selectedBook = b; }); + var onCollapseClicked = new EventCallbackFactory().Create(this, () => this.isCollapsed = true); + var onAddNewItemClicked = new EventCallbackFactory().Create(this, () => this.addNewItemIsClicked = true); + + this.component = this.context.RenderComponent>(parameters => + { + parameters.Add(p => p.CollapseButtonIconClass, "icon-class"); + parameters.Add(p => p.HeaderTitle, "TestColumn"); + parameters.Add(p => p.HeaderHexColor, "#CCC"); + parameters.Add(p => p.Items, new List() { book }); + parameters.Add(p => p.OnEditClicked, onEditClicked); + parameters.Add(p => p.OnDeleteClicked, onDeleteClicked); + parameters.Add(p => p.SelectedValueChanged, onSelectedValueClicked); + parameters.Add(p => p.CssClass, "node"); + parameters.Add(p => p.OnCollapseClicked, onCollapseClicked); + parameters.Add(p => p.OnCreateNewItemClick, onAddNewItemClicked); + }); + } + + [TearDown] + public void Teardown() + { + this.context.CleanContext(); + } + + [Test] + public void VerifyComponent() + { + var collapseButton = this.component.Find(".collapse-button"); + collapseButton.Click(); + + Assert.That(this.isCollapsed, Is.True); + + var addItemButton = this.component.Find(".add-item-button"); + addItemButton.Click(); + + Assert.That(this.addNewItemIsClicked, Is.True); + + var header = this.component.Find(".header-text"); + + Assert.Multiple(() => + { + Assert.That(header.Attributes["class"].Value, Is.EqualTo("header-text")); + Assert.That(header.HasAttribute("style"), Is.True); + }); + + var nodeButton = this.component.Find(".node-button"); + nodeButton.Click(); + + Assert.Multiple(() => + { + Assert.That(this.selectedBook, Is.Not.Null); + Assert.That(this.component.Instance.SelectedValue, Is.Not.Null); + }); + + var editButton = this.component.Find(".icon-edit"); + var deleteButton = this.component.Find(".icon-trash"); + + editButton.Click(); + deleteButton.Click(); + + Assert.Multiple(() => + { + Assert.That(this.editIsClicked, Is.True); + Assert.That(this.deleteIsClicked, Is.True); + }); + } + } +} + diff --git a/COMETwebapp.Tests/ViewModels/Components/BookEditor/BookEditorBodyViewModelTestFixture.cs b/COMETwebapp.Tests/ViewModels/Components/BookEditor/BookEditorBodyViewModelTestFixture.cs new file mode 100644 index 00000000..94a331d5 --- /dev/null +++ b/COMETwebapp.Tests/ViewModels/Components/BookEditor/BookEditorBodyViewModelTestFixture.cs @@ -0,0 +1,243 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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.Tests.ViewModels.Components.BookEditor +{ + using CDP4Common.CommonData; + using CDP4Common.EngineeringModelData; + using CDP4Common.ReportingData; + + using COMET.Web.Common.Services.SessionManagement; + + using COMETwebapp.ViewModels.Components.BookEditor; + + using Moq; + + using NUnit.Framework; + + [TestFixture] + public class BookEditorBodyViewModelTestFixture + { + private IBookEditorBodyViewModel viewModel; + private Mock sessionService; + + [SetUp] + public void SetUp() + { + this.sessionService = new Mock(); + + this.viewModel = new BookEditorBodyViewModel(this.sessionService.Object); + } + + [Test] + public void VerifyProperties() + { + Assert.Multiple(() => + { + Assert.That(this.viewModel.SelectedBook, Is.Null); + Assert.That(this.viewModel.SelectedSection, Is.Null); + Assert.That(this.viewModel.SelectedPage, Is.Null); + Assert.That(this.viewModel.SelectedNote, Is.Null); + Assert.That(this.viewModel.AvailableBooks, Is.Not.Null); + Assert.That(this.viewModel.AvailableBooks, Is.Empty); + Assert.That(this.viewModel.AvailableCategories, Is.Not.Null); + Assert.That(this.viewModel.AvailableCategories, Is.Empty); + Assert.That(this.viewModel.ActiveDomains, Is.Not.Null); + Assert.That(this.viewModel.ActiveDomains, Is.Empty); + Assert.That(this.viewModel.ThingToCreate, Is.Null); + Assert.That(this.viewModel.ThingToDelete, Is.Null); + Assert.That(this.viewModel.ThingToEdit, Is.Null); + Assert.That(this.viewModel.EditorPopupViewModel, Is.Not.Null); + Assert.That(this.viewModel.ConfirmCancelPopupViewModel, Is.Not.Null); + }); + } + + private void FillSelectedItems() + { + var book = new Book(); + var section = new Section(); + var page = new Page(); + var note = new TextualNote(); + + this.viewModel.SelectedBook = book; + this.viewModel.SelectedSection = section; + this.viewModel.SelectedPage = page; + this.viewModel.SelectedNote = note; + } + + [Test] + public void VerifySelectedItemsAreReset() + { + this.FillSelectedItems(); + this.viewModel.SelectedBook = new Book(); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.SelectedSection, Is.Null); + Assert.That(this.viewModel.SelectedPage, Is.Null); + Assert.That(this.viewModel.SelectedNote, Is.Null); + }); + + this.FillSelectedItems(); + this.viewModel.SelectedSection = new Section(); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.SelectedBook, Is.Not.Null); + Assert.That(this.viewModel.SelectedPage, Is.Null); + Assert.That(this.viewModel.SelectedNote, Is.Null); + }); + + this.FillSelectedItems(); + this.viewModel.SelectedPage = new Page(); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.SelectedBook, Is.Not.Null); + Assert.That(this.viewModel.SelectedSection, Is.Not.Null); + Assert.That(this.viewModel.SelectedNote, Is.Null); + }); + } + + [Test] + public void VerifySetThingToCreate() + { + //Try to set a thing that is not a book, section, page or note + Assert.That(() => this.viewModel.SetThingToCreate(new Iteration()), Throws.ArgumentException); + + var book = new Book(); + this.viewModel.SetThingToCreate(book); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.ThingToCreate, Is.Not.Null); + Assert.That(this.viewModel.ThingToCreate, Is.EqualTo(book)); + Assert.That(this.viewModel.EditorPopupViewModel.HeaderText, Is.EqualTo("Create a new Book")); + Assert.That(this.viewModel.EditorPopupViewModel.OnConfirmClick, Is.Not.Null); + Assert.That(this.viewModel.EditorPopupViewModel.OnCancelClick, Is.Not.Null); + Assert.That(this.viewModel.EditorPopupViewModel.IsVisible = true); + }); + } + + [Test] + public void VerifyOnCreateThing() + { + //Try to create a thing when the thing to create has not been set + Assert.That(() => this.viewModel.OnCreateThing(), Throws.InvalidOperationException); + + var section = new Section(); + this.viewModel.SelectedBook = new Book(); + this.viewModel.SetThingToCreate(section); + + Assert.That(() => this.viewModel.OnCreateThing(), Throws.Nothing); + + this.sessionService.Verify(x => x.CreateThing(It.IsAny(), It.IsAny()), Times.Once); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.ThingToCreate, Is.Null); + Assert.That(this.viewModel.EditorPopupViewModel.IsVisible, Is.False); + }); + } + + [Test] + public void VerifySetThingToEdit() + { + //Try to set a thing that is not a book, section, page or note + Assert.That(() => this.viewModel.SetThingToEdit(new Iteration()), Throws.ArgumentException); + + var book = new Book(); + this.viewModel.SetThingToEdit(book); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.ThingToEdit, Is.Not.Null); + Assert.That(this.viewModel.ThingToEdit, Is.EqualTo(book)); + Assert.That(this.viewModel.EditorPopupViewModel.HeaderText, Is.EqualTo("Edit the Book")); + Assert.That(this.viewModel.EditorPopupViewModel.OnConfirmClick, Is.Not.Null); + Assert.That(this.viewModel.EditorPopupViewModel.OnCancelClick, Is.Not.Null); + Assert.That(this.viewModel.EditorPopupViewModel.IsVisible = true); + }); + } + + [Test] + public void VerifyOnEditThing() + { + //Try to edit a thing when the thing to edit has not been set + Assert.That(() => this.viewModel.OnEditThing(), Throws.InvalidOperationException); + + var section = new Section(); + section.Container = new Book(); + this.viewModel.SetThingToEdit(section); + + Assert.That(() => this.viewModel.OnEditThing(), Throws.Nothing); + + this.sessionService.Verify(x => x.UpdateThing(It.IsAny(), It.IsAny()), Times.Once); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.ThingToEdit, Is.Null); + Assert.That(this.viewModel.EditorPopupViewModel.IsVisible, Is.False); + }); + } + + [Test] + public void VerifySetThingToDelete() + { + //Try to set a thing that is not a book, section, page or note + Assert.That(() => this.viewModel.SetThingToDelete(new Iteration()), Throws.ArgumentException); + + var book = new Book(); + this.viewModel.SetThingToDelete(book); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.ThingToDelete, Is.Not.Null); + Assert.That(this.viewModel.ThingToDelete, Is.EqualTo(book)); + Assert.That(this.viewModel.ConfirmCancelPopupViewModel.IsVisible, Is.True); + }); + } + + [Test] + public void VerifyOnDeleteThing() + { + //Try to delete a thing when the thing to delete has not been set + Assert.That(() => this.viewModel.OnDeleteThing(), Throws.InvalidOperationException); + + var section = new Section(); + section.Container = new Book(); + this.viewModel.SetThingToDelete(section); + + Assert.That(() => this.viewModel.OnDeleteThing(), Throws.Nothing); + + this.sessionService.Verify(x => x.DeleteThing(It.IsAny(), It.IsAny()), Times.Once); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.ThingToDelete, Is.Null); + Assert.That(this.viewModel.EditorPopupViewModel.IsVisible, Is.False); + }); + } + } +} diff --git a/COMETwebapp/Components/BookEditor/BookEditorBody.razor b/COMETwebapp/Components/BookEditor/BookEditorBody.razor index 189c6028..fe8404a3 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorBody.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorBody.razor @@ -27,11 +27,11 @@ - - - -
+ + + + @@ -54,7 +54,7 @@ IsCollapsed="@this.IsSectionColumnCollapsed" OnCollapseClicked="@(() => this.IsBooksColumnCollapsed = !this.IsBooksColumnCollapsed)" CollapseButtonIconClass="@(this.IsBooksColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")" - CssClass="section-nodes" + CssClass="section-node" OnCreateNewItemClick="@(() => this.ViewModel.SetThingToCreate(new Section()))" OnEditClicked="@((s) => this.ViewModel.SetThingToEdit(s))" OnDeleteClicked="@((s) => this.ViewModel.SetThingToDelete(s))" @@ -70,7 +70,7 @@ IsCollapsed="@this.IsPageColumnCollapsed" OnCollapseClicked="@(() => this.IsSectionColumnCollapsed = !this.IsSectionColumnCollapsed)" CollapseButtonIconClass="@(this.IsSectionColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")" - CssClass="page-nodes" + CssClass="page-node" OnCreateNewItemClick="@(() => this.ViewModel.SetThingToCreate(new Page()))" OnEditClicked="@((p) => this.ViewModel.SetThingToEdit(p))" OnDeleteClicked="@((p) => this.ViewModel.SetThingToDelete(p))" @@ -85,7 +85,7 @@ @bind-SelectedValue="@this.ViewModel.SelectedNote" OnCollapseClicked="@(() => this.IsPageColumnCollapsed = !this.IsPageColumnCollapsed)" CollapseButtonIconClass="@(this.IsPageColumnCollapsed ? "icon-arrow-right" : "icon-arrow-left")" - CssClass="note-nodes" + CssClass="note-node" OnCreateNewItemClick="@(() => this.ViewModel.SetThingToCreate(new TextualNote(){ LanguageCode = "es" }))" OnEditClicked="@((n) => this.ViewModel.SetThingToEdit(n))" OnDeleteClicked="@((n) => this.ViewModel.SetThingToDelete(n))" diff --git a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor index b9203249..8ea5d3a3 100644 --- a/COMETwebapp/Components/BookEditor/BookEditorColumn.razor +++ b/COMETwebapp/Components/BookEditor/BookEditorColumn.razor @@ -31,7 +31,7 @@
-
@this.HeaderTitle
+
@this.HeaderTitle
@@ -56,7 +56,7 @@ } - - + +
}