From 5b0533981474bff7cf453fd6462b2ff0b0638519 Mon Sep 17 00:00:00 2001 From: Andreas Weishaupt Date: Mon, 27 Oct 2025 09:46:08 +0100 Subject: [PATCH 1/3] rework ContentFilesView.razor use same component in other components --- .../CreateEditReferenceActionDialogIt.cs | 4 + .../CreateEditReferenceActionDialog.razor | 108 ++-- .../ContentFiles/ContentFilesContainer.razor | 2 +- .../ContentFiles/ContentFilesView.razor | 494 ++++++++++++------ .../Forms/Content/LearningContentDialog.razor | 195 +------ .../ContentFiles/ContentFilesViewUt.cs | 6 +- 6 files changed, 419 insertions(+), 390 deletions(-) diff --git a/IntegrationTest/Components/Adaptivity/Dialogues/CreateEditReferenceActionDialogIt.cs b/IntegrationTest/Components/Adaptivity/Dialogues/CreateEditReferenceActionDialogIt.cs index 245b6a2fb..4843bb53a 100644 --- a/IntegrationTest/Components/Adaptivity/Dialogues/CreateEditReferenceActionDialogIt.cs +++ b/IntegrationTest/Components/Adaptivity/Dialogues/CreateEditReferenceActionDialogIt.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading.Tasks; using Bunit; +using Bunit.TestDoubles; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using Microsoft.Extensions.DependencyInjection; @@ -10,6 +11,7 @@ using NSubstitute; using NUnit.Framework; using Presentation.Components.Adaptivity.Dialogues; +using Presentation.Components.ContentFiles; using Presentation.Components.Forms; using Presentation.Components.Forms.Element; using Presentation.PresentationLogic.API; @@ -20,6 +22,7 @@ using Presentation.PresentationLogic.LearningContent.AdaptivityContent.Trigger; using Presentation.PresentationLogic.LearningElement; using Presentation.PresentationLogic.LearningWorld; +using Presentation.PresentationLogic.Mediator; using Presentation.PresentationLogic.SelectedViewModels; using Presentation.View.LearningElement; using Shared.Adaptivity; @@ -58,6 +61,7 @@ public class CreateEditReferenceActionDialogIt : MudDialogTestFixture>(); + Context.ComponentFactories.AddStub(); Context.RenderComponent(); await GetDialogAsync(); } diff --git a/Presentation/Components/Adaptivity/Dialogues/CreateEditReferenceActionDialog.razor b/Presentation/Components/Adaptivity/Dialogues/CreateEditReferenceActionDialog.razor index 68adbd44d..f6f22c320 100644 --- a/Presentation/Components/Adaptivity/Dialogues/CreateEditReferenceActionDialog.razor +++ b/Presentation/Components/Adaptivity/Dialogues/CreateEditReferenceActionDialog.razor @@ -1,5 +1,6 @@ @using System.Diagnostics.CodeAnalysis @using Microsoft.Extensions.Localization +@using Presentation.Components.ContentFiles @using Presentation.Components.Forms @using Presentation.PresentationLogic.API @using Presentation.PresentationLogic.LearningContent @@ -16,7 +17,8 @@
@Localizer["Reference.Text"]
- @Localizer["Note.Label.Text"] + @Localizer["Note.Label.Text"]
- @Localizer["Reference.Element.Label.Text"] - - + @Localizer["Reference.Element.Label.Text"] + +
- @Localizer["Reference.Material.Element.Text.Selected"] - @LearningElement?.Name + @Localizer["Reference.Material.Element.Text.Selected"] + @LearningElement?.Name
- @Localizer["Reference.Content.Label.Text"] - - + @Localizer["Reference.Content.Label.Text"] + +
- @Localizer["Reference.Material.Element.Text.Selected"] - @LearningContent?.Name + @Localizer["Reference.Material.Element.Text.Selected"] + @LearningContent?.Name
- - - - - - - - - @context.Name - - @if (context is FileContentViewModel fileContentContext) - { - @fileContentContext.Type - } - else - { - Link - } - - - + +
@@ -129,9 +124,9 @@ [Inject, AllowNull] internal IStringLocalizer Localizer { get; set; } [Parameter] public bool? IsSelected { get; set; } - + [Parameter] public IAdaptivityActionViewModel? ExistingAction { get; set; } - + [Parameter] public IAdaptivityRuleViewModel? ExistingRule { get; set; } [Parameter, EditorRequired, AllowNull] public IAdaptivityQuestionViewModel Question { get; set; } @@ -149,6 +144,7 @@ private MudTabPanel _contentPanel = null!; private MudTabPanel _elementPanel = null!; private ILearningContentViewModel? _learningContent; + internal ILearningContentViewModel? LearningContent { get => _learningContent; @@ -159,8 +155,9 @@ { _learningElement = null; } - } + } } + private ILearningElementViewModel? _learningElement; private ILearningElementViewModel? LearningElement @@ -175,22 +172,25 @@ } } } + internal string Comment = ""; - + private bool _initialized; - + private string GetTabPanelClass(MudTabPanel panel) { var isSelected = _tabs.ActivePanel == panel; - - @if(panel == _elementPanel) + + @if (panel == _elementPanel) { return isSelected ? "text-adlertextgrey bg-adlergrey-200 hover:bg-adlergrey-300 font-bold panel-element" : "text-adlergrey-300 bg-adlergrey-100 panel-element"; } - @if(panel == _contentPanel) + + @if (panel == _contentPanel) { return isSelected ? "text-adlertextgrey bg-adlergrey-200 hover:bg-adlergrey-300 font-bold rounded-tr panel-content" : "text-adlergrey-300 bg-adlergrey-100 rounded-tr panel-content"; } + return "border-2 flex rounded-lg"; } @@ -210,6 +210,7 @@ Comment = eravm.Comment; break; } + _initialized = true; } @@ -237,16 +238,16 @@ private async Task SubmitAsync() { - if(!(_tabs.ActivePanel == _elementPanel && LearningContent != null || _tabs.ActivePanel == _contentPanel && LearningElement != null)) + if (!(_tabs.ActivePanel == _elementPanel && LearningContent != null || _tabs.ActivePanel == _contentPanel && LearningElement != null)) { await _form.Validate(); if (!_form.IsValid) return; } - + try { switch (ExistingAction) - { + { case ContentReferenceActionViewModel when LearningContent == null: PresentationLogic.ReplaceContentReferenceActionByElementReferenceAction(Question, ExistingRule!, new ElementReferenceActionViewModel(LearningElement!.Id, Comment), @@ -297,6 +298,7 @@ { throw new InvalidOperationException("Either LearningElement or LearningContent must be set"); } + PresentationLogic.CreateAdaptivityRule( Question, new CorrectnessTriggerViewModel(AnswerResult.Incorrect), diff --git a/Presentation/Components/ContentFiles/ContentFilesContainer.razor b/Presentation/Components/ContentFiles/ContentFilesContainer.razor index bf9c91591..6da7fed87 100644 --- a/Presentation/Components/ContentFiles/ContentFilesContainer.razor +++ b/Presentation/Components/ContentFiles/ContentFilesContainer.razor @@ -5,7 +5,7 @@
- +
diff --git a/Presentation/Components/ContentFiles/ContentFilesView.razor b/Presentation/Components/ContentFiles/ContentFilesView.razor index a035c458e..a8f252adf 100644 --- a/Presentation/Components/ContentFiles/ContentFilesView.razor +++ b/Presentation/Components/ContentFiles/ContentFilesView.razor @@ -1,6 +1,5 @@ @using System.Diagnostics.CodeAnalysis @using System.Runtime.Serialization -@using BusinessLogic.Entities.LearningContent.FileContent @using Microsoft.Extensions.Localization @using MudBlazor.Extensions @using Presentation.Components.Dialogues @@ -19,147 +18,196 @@ @inject NavigationManager Navigation
-
-

@Localizer["Header"]

-
-
+ @if (ShowHeader) + { +
+

@Localizer["Header"]

+
+ } +
+ OnRowMouseLeave=" _ => { if (!_cursorInsidePopover) _rowShowingPopover = null; }"> - - - -
- - - @_deletionCandidates.Count() - - - - - - @Localizer["Content.Select.Text"] - - @Localizer["Content.Select.Option.All"] - @Localizer["Content.Select.Option.AllUnused"] - - - - -
-
- - - @Localizer["Content.MudTh.Name"] - - - - - @Localizer["Content.MudTh.Type"] - - - @if (_showFilepath) - { - @Localizer["Content.MudTh.FilepathOrLink"] - } - - - Zustand - - -
+ @if (ShowHeadRow) + { + + @if (ShowPreviewButton) + { + + } + @if (MultipleSelection) + { + +
+ @if (DeleteButtonVisible) + { + + } + + @_deletionCandidates.Count() + + + + + + @Localizer["Content.Select.Text"] + + @Localizer["Content.Select.Option.All"] + @Localizer["Content.Select.Option.AllUnused"] + + +
+
+ } + + + @Localizer["Content.MudTh.Name"] + + + @if (_showFilepath) + { + @Localizer["Content.MudTh.FilepathOrLink"] + } + + + @Localizer["Content.MudTh.Type"] + + + @if (ShowContentState) + { + + + Zustand + + + } +
+ }
- + @if (ShowPreviewButton) + { + + + + } + @if (MultipleSelection) + { + + +
+ @if (GetUsage(context).Any()) + { + + + + + + + + + + + + } +
+
+ +
+
+ } -
- @if (GetUsage(context).Any()) - { - - - - - - - - - - - - } -
-
- -
-
- -

@context.Name

-
- - - - - - @if (SelectedViewModelsProvider.LearningWorld?.Name != null) - { - + +

+ @context.Name +

+
+
+ @if (ShowHoverMenu) + { + + + + title=@Localizer["Content.MudTd.Preview"]> - } - - - - + @if (SelectedViewModelsProvider.LearningWorld?.Name != null) + { + + + } + + @if (DeleteButtonVisible) + { + + + } + + } + @if (_showFilepath) { @@ -188,54 +236,44 @@
} - - @if (context is FileContentViewModel fileContentContext) - { - var fileContent = context as IFileContentViewModel; -
- @if (fileContentContext.Type == "h5p") - { - - } - else - { + @if (ShowContentState) + { + + @if (context is FileContentViewModel fileContentContext) + { + var fileContent = context as IFileContentViewModel; +
+ @if (fileContentContext.Type == "h5p") + { + + } + else + { + +
+ @((MarkupString)H5PStateIcon.InlineSvg(H5PStateIcon.Svg.Lockable)) +

nutzbar

+
+
+ } +
+ } + else + { +
@((MarkupString)H5PStateIcon.InlineSvg(H5PStateIcon.Svg.Lockable))

nutzbar

- } -
- } - else - { -
- -
- @((MarkupString)H5PStateIcon.InlineSvg(H5PStateIcon.Svg.Lockable)) -

nutzbar

-
-
-
- } -
+
+ } +
+ } + - - -
-
- -

@Localizer["PagerContent.Filepath.Text"]

-
- -
-
+
@@ -266,6 +304,67 @@ [CascadingParameter, AllowNull] //injected by MudDialog public IMudDialogInstance DialogInstance { get; set; } + [Parameter] public EventCallback OnRowClick { get; set; } + + [Parameter] public string TableHeight { get; set; } = "220px"; + [Parameter] public string TableWidthClass { get; set; } = "w-full"; + [Parameter] public string SurroundingDivClass { get; set; } = "px-4"; + [Parameter] public bool ShowHeadRow { get; set; } = true; + [Parameter] public bool ShowContentState { get; set; } = true; + [Parameter] public bool ShowHeader { get; set; } = false; + [Parameter] public ILearningContentViewModel? SelectedLearningContent { get; set; } + [Parameter] public EventCallback SelectedLearningContentChanged { get; set; } + + /// + /// If true, there are 10 items per page and a pager is shown. + /// If false, all items are shown in one scrollable table without a pager. + /// Default: true + /// + [Parameter] + public bool ShowPager { get; set; } = true; + + /// + /// If true, the delete button is visible. + /// Default: true + /// + [Parameter] + public bool DeleteButtonVisible { get; set; } = true; + + /// + /// If true the multiple selection is enabled. + /// Default: true + /// + [Parameter] + public bool MultipleSelection { get; set; } = true; + + /// + /// If true, a preview button is shown on the left for each item. + /// Default: false + /// + [Parameter] + public bool ShowPreviewButton { get; set; } = false; + + /// + /// If true, a hover menu is shown for each item. + /// Default: true + /// + [Parameter] + public bool ShowHoverMenu { get; set; } = true; + + /// + /// If true, the unusable content is greyed out and not selectable. + /// Default: false + /// + [Parameter] + public bool DisableUnusableContent { get; set; } = false; + + /// + /// If true, the search field is focused on opening the view. + /// Default: false + /// + [Parameter] + public bool FocusSearch { get; set; } = false; + private IEnumerable Items => PresentationLogic.GetAllContent(); internal string? SearchString { get; set; } @@ -274,6 +373,28 @@ private MudTable? _table; private IEnumerable _deletionCandidates = new List(); + private string TableClass => TableWidthClass + " flex flex-col flex-nowrap shadow-none bg-adlerbgbright rounded border-adlergrey-100 border-2 cursor-pointer"; + + private RenderFragment MyPagerContent => __builder => + { + +
+
+ +

@Localizer["PagerContent.Filepath.Text"]

+
+ @* ReSharper disable once CSharpWarnings::CS8974 *@ + +
+ }; + private bool? MultipleSelected { get @@ -288,6 +409,19 @@ private ILearningContentViewModel? _rowShowingPopover = null; private bool _cursorInsidePopover = false; + private MudTextField _searchField = null!; + private bool _focusCalled = false; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + if (!_focusCalled) + { + _focusCalled = true; + await _searchField.FocusAsync(); + } + } + private object TypeSortBy(ILearningContentViewModel content) => content is FileContentViewModel fc ? fc.Type : "Link"; @@ -306,6 +440,30 @@ return $"{element.Name}.{elementType}".ToLowerInvariant().Contains(searchString); } + private async void RowClickEvent(TableRowClickEventArgs tableRowClickEventArgs) + { + if (tableRowClickEventArgs.Item != null && IsNameDisabled(tableRowClickEventArgs.Item)) + { + return; + } + + if (SelectedLearningContentChanged.HasDelegate) + { + SelectedLearningContent = tableRowClickEventArgs.Item; + await SelectedLearningContentChanged.InvokeAsync(SelectedLearningContent); + } + + if (OnRowClick.HasDelegate) + { + await OnRowClick.InvokeAsync(tableRowClickEventArgs.Item); + } + } + + private string SelectedRowClassFunc(ILearningContentViewModel element, int rowNumber) + { + return element.Equals(SelectedLearningContent) ? "bg-adlerblue-200" : "bg-white"; + } + private async Task Delete(ILearningContentViewModel item) { //present "Delete/Cancel" dialog @@ -532,6 +690,18 @@ [Inject, AllowNull] //can never be null, DI will throw exception on unresolved types - n.stich internal IH5PPlayerPluginManager H5PPlayerPluginManager { get; set; } + private bool IsNameDisabled(ILearningContentViewModel item) + { + if (!DisableUnusableContent) return false; + if (item is FileContentViewModel file) + { + return file.Type == "h5p" && + !(file.H5PState == H5PContentState.Completable || file.H5PState == H5PContentState.Primitive); + } + + return false; + } + private async Task ShowLearningContent(ILearningContentViewModel context) { if (context is FileContentViewModel fileContentVm && fileContentVm.Type == "h5p") diff --git a/Presentation/Components/Forms/Content/LearningContentDialog.razor b/Presentation/Components/Forms/Content/LearningContentDialog.razor index f247b99aa..9e9c17954 100644 --- a/Presentation/Components/Forms/Content/LearningContentDialog.razor +++ b/Presentation/Components/Forms/Content/LearningContentDialog.razor @@ -3,130 +3,22 @@ @using Microsoft.Extensions.Localization @using Presentation.Components.ContentFiles @using Presentation.Components.Forms.Models -@using Presentation.PresentationLogic.LearningWorld -@using Shared.H5P -@using Presentation.H5P @using Presentation.PresentationLogic.API -@using Presentation.PresentationLogic.LearningContent.FileContent - - @using Presentation.PresentationLogic.LearningContent -@inherits MudBaseInput - -@inject NavigationManager Navigation - +@using Presentation.PresentationLogic.LearningWorld +@using Shared.H5P

@Localizer["LearningContentDialog.LearningMaterial.Dialog.Subtitle"]

-
- -
- -
-
- - - - - - @Localizer["LearningContentDialog.LearningMaterial.Name"] - - - @Localizer["LearningContentDialog.LearningMaterial.Type"] - - - - @Localizer["LearningContentDialog.LearningMaterial.State"] - - - - - - - - - - -
- -

- @context.Name -

-
-
-
- - - @if (context is FileContentFormModel fileContentContext) - { -
- @fileContentContext.Type -
- } - else - { -
- @Localizer["LearningContentDialog.Type.Video"] -
- } -
- - - @if (context is FileContentFormModel fileContentContext2) - { - if (fileContentContext2.Type == "h5p") - { - - } - else - { - -
- @((MarkupString)H5PStateIcon.InlineSvg(H5PStateIcon.Svg.Lockable)) -

@Localizer["LearningContentDialog.State.Usable"]

-
-
- } - } - else - { -
- -
- @((MarkupString)H5PStateIcon.InlineSvg(H5PStateIcon.Svg.Lockable)) -

@Localizer["LearningContentDialog.State.Usable"]

-
-
-
- } -
-
-
-
+ @code { [CascadingParameter] public IMudDialogInstance? MudDialog { get; set; } @@ -136,66 +28,29 @@ [Inject, AllowNull] internal IStringLocalizer Localizer { get; set; } [Inject, AllowNull] internal ILearningWorldPresenter WorldPresenter { get; set; } [Inject, AllowNull] internal IMapper Mapper { get; set; } + [Inject, AllowNull] internal IPresentationLogic PresentationLogic { get; set; } - private string? _searchString; - - private bool IsNameDisabled(ILearningContentFormModel item) + private ILearningContentViewModel? GetInitialSelectedLearningContent() { - if (item is FileContentFormModel file) - return file.Type == "h5p" && !(file.H5PState == H5PContentState.Completable || file.H5PState == H5PContentState.Primitive); - return false; + var learningContentVm = Mapper.Map(LearningContent); + return PresentationLogic.GetAllContent().FirstOrDefault(vm => vm.Equals(learningContentVm)); } - private void RowClickEvent(TableRowClickEventArgs e) + private void HandleRowClick(ILearningContentViewModel selectedContent) { - if (e.Item is FileContentFormModel fileContent) - { - if (fileContent.Type == "h5p" && - (fileContent.H5PState == H5PContentState.Completable || fileContent.H5PState == H5PContentState.Primitive)) - MudDialog?.Close(DialogResult.Ok(e.Item)); - else if (fileContent.Type != "h5p") - MudDialog?.Close(DialogResult.Ok(e.Item)); - } - else MudDialog?.Close(DialogResult.Ok(e.Item)); + RowClickEvent(Mapper.Map(selectedContent)); } - private string SelectedRowClassFunc(ILearningContentFormModel element, int rowNumber) - => element.Equals(LearningContent) ? "bg-adlerblue-200" : string.Empty; - - private bool Filter(ILearningContentFormModel element) => FilterInternal(element, _searchString); - private bool FilterInternal(ILearningContentFormModel element, string? s) => FilterInternal(ILearningContentFormModel.GetSearchableStrings(element), s); - - private bool FilterInternal(IEnumerable strings, string? s) - => string.IsNullOrWhiteSpace(s) || strings.Any(str => str.ToLowerInvariant().Contains(s.ToLower())); - - [Inject, AllowNull] internal IH5PPlayerPluginManager H5PPlayerPluginManager { get; set; } - [Inject, AllowNull] internal IPresentationLogic PresentationLogic { get; set; } - - private async Task ShowLearningContent(ILearningContentFormModel context) + private void RowClickEvent(ILearningContentFormModel learningContentFormModel) { - if (context is FileContentFormModel fileContentFm && fileContentFm.Type == "h5p") + if (learningContentFormModel is FileContentFormModel fileContent) { - var fileContentVm = FileContentFormModelToViewModel(fileContentFm); - var startH5PPlayerTO = new StartH5PPlayerTO { FileContentVm = fileContentVm, NavigationManager = Navigation }; - await H5PPlayerPluginManager.StartH5pPlayerToValidateAsync(startH5PPlayerTO); - } - else - { - await PresentationLogic.ShowLearningContentAsync(context); + if ((fileContent.Type == "h5p" && + (fileContent.H5PState == H5PContentState.Completable || fileContent.H5PState == H5PContentState.Primitive)) + || fileContent.Type != "h5p") + MudDialog?.Close(DialogResult.Ok(learningContentFormModel)); } + else MudDialog?.Close(DialogResult.Ok(learningContentFormModel)); } - private IFileContentViewModel FileContentFormModelToViewModel(FileContentFormModel fileContentFm) - { - var vm = new FileContentViewModel(fileContentFm.Name, fileContentFm.Type, fileContentFm.Filepath) - { - H5PState = fileContentFm.H5PState, - IsH5P = true - }; - return vm; - } - - private object SortByH5pState(ILearningContentFormModel context) - => (context is FileContentFormModel f && f.Type == "h5p") ? f.H5PState : H5PContentState.Completable; - } \ No newline at end of file diff --git a/PresentationTest/Components/ContentFiles/ContentFilesViewUt.cs b/PresentationTest/Components/ContentFiles/ContentFilesViewUt.cs index 5a9b680b1..4766c9b1f 100644 --- a/PresentationTest/Components/ContentFiles/ContentFilesViewUt.cs +++ b/PresentationTest/Components/ContentFiles/ContentFilesViewUt.cs @@ -122,14 +122,12 @@ public void Render_PresentationLogicReturnsSomeItems_ItemsAreRendered() var nameTd = tableRows[i].Children .FirstOrDefault(child => child.Attributes["data-label"]?.Value == "Name"); Assert.That(nameTd, Is.Not.Null); - var name = nameTd!.Children.First(child => child.Matches("div")).Children - .First(child => child.Matches("p")) - .GetInnerText(); + var name = nameTd.GetInnerText().Trim(); Assert.That(name, Is.EqualTo(item.Name)); var typeTd = tableRows[i].Children .FirstOrDefault(child => child.Attributes["data-label"]?.Value == "Type"); - var type = typeTd.GetInnerText(); + var type = typeTd.GetInnerText().Trim(); Assert.That(type, Is.EqualTo(item is FileContentViewModel fc ? fc.Type : "video")); var pathTd = tableRows[i].Children From a65c3eb2958aa471156f178d20844dada828e234 Mon Sep 17 00:00:00 2001 From: Andreas Weishaupt Date: Thu, 6 Nov 2025 16:23:34 +0100 Subject: [PATCH 2/3] add localization for H5PState --- .../CreateEditReferenceActionDialogIt.cs | 19 +++++----- .../ContentFiles/ContentFilesView.razor | 37 +++++++------------ Presentation/H5P/H5PStateIcon.razor | 28 +++++++++----- .../ContentFiles/ContentFilesView.de.resx | 3 ++ .../ContentFiles/ContentFilesView.en.resx | 3 ++ .../Dialogues/ImportH5PDialog.en.resx | 4 +- .../Resources/H5P/H5PStateIcon.de.resx | 35 ++++++++++++++++++ .../Resources/H5P/H5PStateIcon.en.resx | 35 ++++++++++++++++++ 8 files changed, 118 insertions(+), 46 deletions(-) create mode 100644 Presentation/Resources/H5P/H5PStateIcon.de.resx create mode 100644 Presentation/Resources/H5P/H5PStateIcon.en.resx diff --git a/IntegrationTest/Components/Adaptivity/Dialogues/CreateEditReferenceActionDialogIt.cs b/IntegrationTest/Components/Adaptivity/Dialogues/CreateEditReferenceActionDialogIt.cs index 4843bb53a..37b5eadb4 100644 --- a/IntegrationTest/Components/Adaptivity/Dialogues/CreateEditReferenceActionDialogIt.cs +++ b/IntegrationTest/Components/Adaptivity/Dialogues/CreateEditReferenceActionDialogIt.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Threading.Tasks; using Bunit; -using Bunit.TestDoubles; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using Microsoft.Extensions.DependencyInjection; @@ -22,7 +21,6 @@ using Presentation.PresentationLogic.LearningContent.AdaptivityContent.Trigger; using Presentation.PresentationLogic.LearningElement; using Presentation.PresentationLogic.LearningWorld; -using Presentation.PresentationLogic.Mediator; using Presentation.PresentationLogic.SelectedViewModels; using Presentation.View.LearningElement; using Shared.Adaptivity; @@ -80,7 +78,7 @@ public void Teardown() private ILearningWorldPresenter LearningWorldPresenter { get; set; } private ILearningWorldViewModel World { get; set; } private ILearningContentViewModel[] Contents { get; set; } - + private ISelectedViewModelsProvider _selectedViewModelsProvider; private IElementModelHandler _elementModelHandler; private IStringLocalizer _stringLocalizer; @@ -96,6 +94,7 @@ private async Task GetDialogAsync() dialogParameters.Add(nameof(CreateEditReferenceActionDialog.ExistingAction), ExistingAction); //dialogParameters.Add(nameof(CreateEditReferenceActionDialog.ExistingRule), ExistingRule); } + Dialog = await OpenDialogAndGetDialogReferenceAsync("title", new DialogOptions(), dialogParameters); } @@ -105,7 +104,7 @@ private async Task GetDialogAsync() public async Task NoExistingAction_ContentSelected_CallsCreateAdaptivityRuleWithContentReferenceAction() { await DialogProvider.Find("div.panel-content").ClickAsync(new MouseEventArgs()); - + var componentUnderTest = DialogProvider.FindComponent(); componentUnderTest.Instance.LearningContent = Contents[0]; componentUnderTest.Instance.Comment = "foo"; @@ -201,7 +200,7 @@ public async Task ExistingAction_ContentSelected_NoChange_CallsNothing() PresentationLogic.DidNotReceiveWithAnyArgs().EditElementReferenceAction(eravm, eravm.ElementId, "foo"); } - + [Test] // ANF-ID: [AWA0027] public async Task ExistingAction_ElementSelected_CallsReplaceElementReferenceActionByContentReferenceAction() @@ -214,9 +213,9 @@ public async Task ExistingAction_ElementSelected_CallsReplaceElementReferenceAct await DialogProvider.FindComponent>().Find("textarea") .ChangeAsync(new ChangeEventArgs { Value = "foo" }); - + await DialogProvider.Find("div.panel-content").ClickAsync(new MouseEventArgs()); - + var componentUnderTest = DialogProvider.FindComponent(); componentUnderTest.Instance.LearningContent = Contents[0]; @@ -228,7 +227,7 @@ await DialogProvider.FindComponent>().Find("textarea") Arg.Any(), Arg.Any()); } - + [Test] // ANF-ID: [AWA0027] public async Task ExistingAction_ContentSelected_CallsReplaceContentReferenceActionByElementReferenceAction() @@ -241,9 +240,9 @@ public async Task ExistingAction_ContentSelected_CallsReplaceContentReferenceAct await DialogProvider.FindComponent>().Find("textarea") .ChangeAsync(new ChangeEventArgs { Value = "foo" }); - + await DialogProvider.Find("div.panel-element").ClickAsync(new MouseEventArgs()); - + await DialogProvider.Find("div.mud-paper").ClickAsync(new MouseEventArgs()); await DialogProvider.FindComponent().Find("button").ClickAsync(new MouseEventArgs()); diff --git a/Presentation/Components/ContentFiles/ContentFilesView.razor b/Presentation/Components/ContentFiles/ContentFilesView.razor index a8f252adf..28591bbcb 100644 --- a/Presentation/Components/ContentFiles/ContentFilesView.razor +++ b/Presentation/Components/ContentFiles/ContentFilesView.razor @@ -3,6 +3,7 @@ @using Microsoft.Extensions.Localization @using MudBlazor.Extensions @using Presentation.Components.Dialogues +@using Presentation.H5P @using Presentation.PresentationLogic.API @using Presentation.PresentationLogic.AuthoringToolWorkspace @using Presentation.PresentationLogic.LearningContent @@ -11,7 +12,6 @@ @using Presentation.PresentationLogic.LearningElement @using Presentation.PresentationLogic.LearningWorld @using Presentation.PresentationLogic.Mediator -@using Presentation.H5P @using Presentation.PresentationLogic.SelectedViewModels @using Shared.H5P @inject ISnackbar Snackbar @@ -239,36 +239,25 @@ @if (ShowContentState) { - @if (context is FileContentViewModel fileContentContext) - { + @{ var fileContent = context as IFileContentViewModel; -
- @if (fileContentContext.Type == "h5p") - { - - } - else - { - -
- @((MarkupString)H5PStateIcon.InlineSvg(H5PStateIcon.Svg.Lockable)) -

nutzbar

-
-
- } -
+ var isH5P = context is FileContentViewModel { Type: "h5p" }; } - else - { -
+
+ @if (isH5P) + { + + } + else + {
@((MarkupString)H5PStateIcon.InlineSvg(H5PStateIcon.Svg.Lockable)) -

nutzbar

+

@Localizer["ContentFilesView.H5P.Usable"]

-
- } + } +
} diff --git a/Presentation/H5P/H5PStateIcon.razor b/Presentation/H5P/H5PStateIcon.razor index d78a6a70a..dbccc2405 100644 --- a/Presentation/H5P/H5PStateIcon.razor +++ b/Presentation/H5P/H5PStateIcon.razor @@ -1,10 +1,10 @@ @using System.ComponentModel.DataAnnotations @using System.Diagnostics.CodeAnalysis +@using Microsoft.Extensions.Localization @using Presentation.Components.ContentFiles @using Presentation.PresentationLogic.API @using Presentation.PresentationLogic.LearningContent.FileContent @using Shared.H5P - @inject NavigationManager Navigation @switch (FileContentVm.H5PState) @@ -15,10 +15,10 @@ -

ungeprüft

+

@Localizer["H5PState.NotValidated"]

- break; + break; case H5PContentState.Primitive: case H5PContentState.Completable: @@ -26,17 +26,17 @@ -

nutzbar

+

@Localizer["H5PState.Usable"]

break; -case H5PContentState.NotUsable: + case H5PContentState.NotUsable:
-

nicht nutzbar

+

@Localizer["H5PState.NotUsable"]

break; @@ -46,12 +46,15 @@ case H5PContentState.NotUsable: @code { [Parameter, Required] public required IFileContentViewModel FileContentVm { get; set; } - + [Inject, AllowNull] //can never be null, DI will throw exception on unresolved types - n.stich - internal IH5PPlayerPluginManager H5PPlayerPluginManager { get; set; } - + internal IH5PPlayerPluginManager H5PPlayerPluginManager { get; set; } + [Inject, AllowNull] //can never be null, DI will throw exception on unresolved types - n.stich internal IPresentationLogic PresentationLogic { get; set; } + + [Inject, AllowNull] internal IStringLocalizer Localizer { get; set; } + private async Task ShowH5PContent() { if (FileContentVm.Type == "h5p") @@ -70,11 +73,13 @@ case H5PContentState.NotUsable: "; + public static readonly string NotLockable = @" "; + public static readonly string Unchecked = @" @@ -100,5 +105,8 @@ case H5PContentState.NotUsable: return before + after; } } + return raw; - }} + } + +} diff --git a/Presentation/Resources/Components/ContentFiles/ContentFilesView.de.resx b/Presentation/Resources/Components/ContentFiles/ContentFilesView.de.resx index 0b47b4b95..a7f4227ab 100644 --- a/Presentation/Resources/Components/ContentFiles/ContentFilesView.de.resx +++ b/Presentation/Resources/Components/ContentFiles/ContentFilesView.de.resx @@ -105,4 +105,7 @@ Fehler beim Löschen von externem Lernmaterial: Datei in Gebrauch + + nutzbar + \ No newline at end of file diff --git a/Presentation/Resources/Components/ContentFiles/ContentFilesView.en.resx b/Presentation/Resources/Components/ContentFiles/ContentFilesView.en.resx index 800ba4937..215b550a3 100644 --- a/Presentation/Resources/Components/ContentFiles/ContentFilesView.en.resx +++ b/Presentation/Resources/Components/ContentFiles/ContentFilesView.en.resx @@ -96,4 +96,7 @@ Error deleting external learning material: File in use + + usable + \ No newline at end of file diff --git a/Presentation/Resources/Components/Dialogues/ImportH5PDialog.en.resx b/Presentation/Resources/Components/Dialogues/ImportH5PDialog.en.resx index f6bd4837f..3e004ad88 100644 --- a/Presentation/Resources/Components/Dialogues/ImportH5PDialog.en.resx +++ b/Presentation/Resources/Components/Dialogues/ImportH5PDialog.en.resx @@ -31,10 +31,10 @@ You can find out more <a href="https://projektadler.github.io/Documentation/m Validate later - useable + usable - not useable + not usable unchecked diff --git a/Presentation/Resources/H5P/H5PStateIcon.de.resx b/Presentation/Resources/H5P/H5PStateIcon.de.resx new file mode 100644 index 000000000..c9953b6fb --- /dev/null +++ b/Presentation/Resources/H5P/H5PStateIcon.de.resx @@ -0,0 +1,35 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + ungeprüft + + + nutzbar + + + nicht nutzbar + + \ No newline at end of file diff --git a/Presentation/Resources/H5P/H5PStateIcon.en.resx b/Presentation/Resources/H5P/H5PStateIcon.en.resx new file mode 100644 index 000000000..622ea524b --- /dev/null +++ b/Presentation/Resources/H5P/H5PStateIcon.en.resx @@ -0,0 +1,35 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + usable + + + not usable + + + not validated + + \ No newline at end of file From 3735c8804f0fae3d272af13abfd197f093da4503 Mon Sep 17 00:00:00 2001 From: Andreas Weishaupt Date: Wed, 12 Nov 2025 11:01:00 +0100 Subject: [PATCH 3/3] make text in H5PStateIcon also clickable --- Presentation/H5P/H5PStateIcon.razor | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Presentation/H5P/H5PStateIcon.razor b/Presentation/H5P/H5PStateIcon.razor index dbccc2405..c696ed503 100644 --- a/Presentation/H5P/H5PStateIcon.razor +++ b/Presentation/H5P/H5PStateIcon.razor @@ -14,8 +14,8 @@
-

@Localizer["H5PState.NotValidated"]

break; @@ -25,8 +25,8 @@
-

@Localizer["H5PState.Usable"]

break; @@ -35,8 +35,8 @@
-

@Localizer["H5PState.NotUsable"]

break;