From 6cfe52d566430575aba093fb29f10f59bcd0f52e Mon Sep 17 00:00:00 2001 From: sven-n Date: Tue, 5 Nov 2024 07:28:53 +0100 Subject: [PATCH] Improvements for the admin panel * Toasts instead of Modal dialogs * Added button to create new obects in EditConfigGrid component * Added button to delete obects in EditConfigGrid component --- .../MUnique.OpenMU.Web.AdminPanel.csproj | 1 + .../Pages/CreateConnectServerConfig.razor.cs | 18 ++--- .../Pages/CreateGameServerConfig.razor.cs | 18 +++-- src/Web/AdminPanel/Pages/EditBase.cs | 18 +++-- src/Web/AdminPanel/Pages/EditConfigGrid.razor | 5 +- .../AdminPanel/Pages/EditConfigGrid.razor.cs | 76 +++++++++++++++++++ src/Web/AdminPanel/Pages/EditMap.cs | 15 ++-- src/Web/AdminPanel/Pages/Merchants.razor.cs | 24 +++--- src/Web/AdminPanel/Shared/MainLayout.razor | 2 +- src/Web/AdminPanel/Startup.cs | 2 + .../AdminPanel/WebApplicationExtensions.cs | 2 + src/Web/AdminPanel/_Imports.razor | 2 + 12 files changed, 147 insertions(+), 36 deletions(-) diff --git a/src/Web/AdminPanel/MUnique.OpenMU.Web.AdminPanel.csproj b/src/Web/AdminPanel/MUnique.OpenMU.Web.AdminPanel.csproj index 7a0399ed5..c95e0ccb1 100644 --- a/src/Web/AdminPanel/MUnique.OpenMU.Web.AdminPanel.csproj +++ b/src/Web/AdminPanel/MUnique.OpenMU.Web.AdminPanel.csproj @@ -37,6 +37,7 @@ + diff --git a/src/Web/AdminPanel/Pages/CreateConnectServerConfig.razor.cs b/src/Web/AdminPanel/Pages/CreateConnectServerConfig.razor.cs index 469b1505c..f1ed59acd 100644 --- a/src/Web/AdminPanel/Pages/CreateConnectServerConfig.razor.cs +++ b/src/Web/AdminPanel/Pages/CreateConnectServerConfig.razor.cs @@ -6,7 +6,7 @@ namespace MUnique.OpenMU.Web.AdminPanel.Pages; using System.ComponentModel.DataAnnotations; using System.Threading; -using Blazored.Modal.Services; +using Blazored.Toast.Services; using Microsoft.AspNetCore.Components; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.Interfaces; @@ -42,10 +42,10 @@ public partial class CreateConnectServerConfig : ComponentBase, IAsyncDisposable public IDataSource DataSource { get; set; } = null!; /// - /// Gets or sets the modal service. + /// Gets or sets the toast service. /// [Inject] - public IModalService ModalService { get; set; } = null!; + public IToastService ToastService { get; set; } = null!; /// /// Gets or sets the navigation manager. @@ -131,7 +131,6 @@ private async ValueTask CreateDefinitionByViewModelAsyn private async Task OnSaveButtonClickAsync() { - string text; try { var gameConfiguration = await this.DataSource.GetOwnerAsync().ConfigureAwait(false); @@ -141,13 +140,13 @@ private async Task OnSaveButtonClickAsync() var existingServerDefinitions = (await saveContext.GetAsync().ConfigureAwait(false)).ToList(); if (existingServerDefinitions.Any(def => def.ServerId == this._viewModel?.ServerId)) { - await this.ModalService.ShowMessageAsync("Save", $"Server with Id {this._viewModel?.ServerId} already exists. Please use another value.").ConfigureAwait(true); + this.ToastService.ShowError($"Server with Id {this._viewModel?.ServerId} already exists. Please use another value."); return; } if (existingServerDefinitions.Any(def => def.ClientListenerPort == this._viewModel?.NetworkPort)) { - await this.ModalService.ShowMessageAsync("Save", $"A server with tcp port {this._viewModel?.NetworkPort} already exists. Please use another tcp port.").ConfigureAwait(true); + this.ToastService.ShowError($"A server with tcp port {this._viewModel?.NetworkPort} already exists. Please use another tcp port."); return; } @@ -157,24 +156,25 @@ private async Task OnSaveButtonClickAsync() this._initState = "Saving Configuration ..."; await this.InvokeAsync(this.StateHasChanged); var success = await saveContext.SaveChangesAsync().ConfigureAwait(true); - text = success ? "The changes have been saved." : "There were no changes to save."; // if success, init new game server instance if (success) { + this.ToastService.ShowSuccess("The connection server configuration has been saved. Initializing connect server ..."); this._initState = "Initializing Connect Server ..."; await this.InvokeAsync(this.StateHasChanged); await this.ServerInstanceManager.InitializeConnectServerAsync(connectServerDefinition.ConfigurationId); this.NavigationManager.NavigateTo("servers"); return; } + + this.ToastService.ShowError("No changes have been saved."); } catch (Exception ex) { - text = $"An unexpected error occurred: {ex.Message}."; + this.ToastService.ShowError($"An unexpected error occurred: {ex.Message}."); } - await this.ModalService.ShowMessageAsync("Save", text).ConfigureAwait(true); this._initState = null; } diff --git a/src/Web/AdminPanel/Pages/CreateGameServerConfig.razor.cs b/src/Web/AdminPanel/Pages/CreateGameServerConfig.razor.cs index 1ccfe23fc..1edca1b82 100644 --- a/src/Web/AdminPanel/Pages/CreateGameServerConfig.razor.cs +++ b/src/Web/AdminPanel/Pages/CreateGameServerConfig.razor.cs @@ -9,6 +9,7 @@ namespace MUnique.OpenMU.Web.AdminPanel.Pages; using System.ComponentModel.DataAnnotations; using System.Threading; using Blazored.Modal.Services; +using Blazored.Toast.Services; using Microsoft.AspNetCore.Components; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.Persistence; @@ -48,6 +49,12 @@ public partial class CreateGameServerConfig : ComponentBase, IAsyncDisposable [Inject] public IModalService ModalService { get; set; } = null!; + /// + /// Gets or sets the toast service. + /// + [Inject] + public IToastService ToastService { get; set; } = null!; + /// /// Gets or sets the navigation manager. /// @@ -152,13 +159,13 @@ private async Task OnSaveButtonClickAsync() var existingServerDefinitions = (await saveContext.GetAsync().ConfigureAwait(false)).ToList(); if (existingServerDefinitions.Any(def => def.ServerID == this._viewModel?.ServerId)) { - await this.ModalService.ShowMessageAsync("Save", $"Server with Id {this._viewModel?.ServerId} already exists. Please use another value.").ConfigureAwait(true); + this.ToastService.ShowError($"Server with Id {this._viewModel?.ServerId} already exists. Please use another value."); return; } if (existingServerDefinitions.Any(def => def.Endpoints.Any(endpoint => endpoint.NetworkPort == this._viewModel?.NetworkPort))) { - await this.ModalService.ShowMessageAsync("Save", $"A server with tcp port {this._viewModel?.NetworkPort} already exists. Please use another tcp port.").ConfigureAwait(true); + this.ToastService.ShowError($"A server with tcp port {this._viewModel?.NetworkPort} already exists. Please use another tcp port."); return; } @@ -168,24 +175,25 @@ private async Task OnSaveButtonClickAsync() this._initState = "Saving Configuration ..."; await this.InvokeAsync(this.StateHasChanged); var success = await saveContext.SaveChangesAsync().ConfigureAwait(true); - text = success ? "The changes have been saved." : "There were no changes to save."; // if success, init new game server instance if (success) { + this.ToastService.ShowSuccess("The game server configuration has been saved. Initializing game server ..."); this._initState = "Initializing Game Server ..."; await this.InvokeAsync(this.StateHasChanged); await this.ServerInstanceManager.InitializeGameServerAsync(gameServerDefinition.ServerID); this.NavigationManager.NavigateTo("servers"); return; } + + this.ToastService.ShowError("No changes have been saved."); } catch (Exception ex) { - text = $"An unexpected error occurred: {ex.Message}."; + this.ToastService.ShowError($"An unexpected error occurred: {ex.Message}."); } - await this.ModalService.ShowMessageAsync("Save", text).ConfigureAwait(true); this._initState = null; } diff --git a/src/Web/AdminPanel/Pages/EditBase.cs b/src/Web/AdminPanel/Pages/EditBase.cs index ea13cb4d1..47723e205 100644 --- a/src/Web/AdminPanel/Pages/EditBase.cs +++ b/src/Web/AdminPanel/Pages/EditBase.cs @@ -7,6 +7,7 @@ namespace MUnique.OpenMU.Web.AdminPanel.Pages; using System.Reflection; using System.Threading; using Blazored.Modal.Services; +using Blazored.Toast.Services; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; using Microsoft.AspNetCore.Components.Routing; @@ -71,6 +72,12 @@ private enum DataLoadingState [Inject] public IModalService ModalService { get; set; } = null!; + /// + /// Gets or sets the toast service. + /// + [Inject] + public IToastService ToastService { get; set; } = null!; + /// /// Gets or sets the configuration data source. /// @@ -221,26 +228,25 @@ await this.InvokeAsync(() => /// protected async Task SaveChangesAsync() { - string text; try { if (this._persistenceContext is { } context) { var success = await context.SaveChangesAsync().ConfigureAwait(true); - text = success ? "The changes have been saved." : "There were no changes to save."; + var text = success ? "The changes have been saved." : "There were no changes to save."; + this.ToastService.ShowSuccess(text); } else { - text = "Failed, context not initialized"; + this.ToastService.ShowError("Failed, context not initialized"); } } catch (Exception ex) { this.Logger?.LogError(ex, $"Error during saving {this.Id}"); - text = $"An unexpected error occured: {ex.Message}."; + var text = $"An unexpected error occured: {ex.Message}."; + this.ToastService.ShowError(text); } - - await this.ModalService.ShowMessageAsync("Save", text).ConfigureAwait(true); } /// diff --git a/src/Web/AdminPanel/Pages/EditConfigGrid.razor b/src/Web/AdminPanel/Pages/EditConfigGrid.razor index 48264f550..0c08f574e 100644 --- a/src/Web/AdminPanel/Pages/EditConfigGrid.razor +++ b/src/Web/AdminPanel/Pages/EditConfigGrid.razor @@ -33,7 +33,10 @@ var targetUrl = $"edit-config/{this.TypeString}/" + context.Id; } + - \ No newline at end of file + +
+ \ No newline at end of file diff --git a/src/Web/AdminPanel/Pages/EditConfigGrid.razor.cs b/src/Web/AdminPanel/Pages/EditConfigGrid.razor.cs index 6ad2a77a6..7c2be7f1c 100644 --- a/src/Web/AdminPanel/Pages/EditConfigGrid.razor.cs +++ b/src/Web/AdminPanel/Pages/EditConfigGrid.razor.cs @@ -7,10 +7,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Pages; using System.Collections; using System.ComponentModel; using System.Threading; +using Blazored.Modal; +using Blazored.Modal.Services; +using Blazored.Toast.Services; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.QuickGrid; +using Microsoft.Extensions.Logging; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.Persistence; +using MUnique.OpenMU.Web.AdminPanel.Components.Form; /// /// Razor page which shows objects of the specified type in a grid. @@ -48,6 +53,24 @@ public partial class EditConfigGrid : ComponentBase, IAsyncDisposable [Inject] public IPersistenceContextProvider PersistenceContextProvider { get; set; } = null!; + /// + /// Gets or sets the modal service. + /// + [Inject] + public IModalService ModalService { get; set; } = null!; + + /// + /// Gets or sets the toast service. + /// + [Inject] + public IToastService ToastService { get; set; } = null!; + + /// + /// Gets or sets the logger. + /// + [Inject] + public ILogger Logger { get; set; } = null!; + /// /// Gets or sets the type. /// @@ -142,6 +165,59 @@ await this.InvokeAsync(async () => .Select(assembly => assembly.GetType(this.TypeString)).FirstOrDefault(t => t != null); } + private async Task OnDeleteButtonClickAsync(ViewModel viewModel) + { + try + { + var dialogResult = await this.ModalService.ShowQuestionAsync("Are you sure?", $"You're about to delete '{viewModel.Name}. Are you sure?"); + if (!dialogResult) + { + return; + } + + var cancellationToken = this._disposeCts?.Token ?? default; + var gameConfiguration = await this.DataSource.GetOwnerAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + using var deleteContext = this.PersistenceContextProvider.CreateNewContext(gameConfiguration); + deleteContext.Attach(viewModel.Parent); + await deleteContext.DeleteAsync(viewModel.Parent).ConfigureAwait(false); + await deleteContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + this.ToastService.ShowSuccess($"Deleted '{viewModel.Name}' successfully."); + this._viewModels = null; + this._loadTask = Task.Run(() => this.LoadDataAsync(cancellationToken), cancellationToken); + } + catch (Exception ex) + { + this.Logger.LogError(ex, $"Couldn't delete '{viewModel.Name}', probably because it's referenced by another object."); + this.ToastService.ShowError($"Couldn't delete '{viewModel.Name}', probably because it's referenced by another object. For details, see log"); + } + } + + private async Task OnCreateButtonClickAsync() + { + var cancellationToken = this._disposeCts?.Token ?? default; + var gameConfiguration = await this.DataSource.GetOwnerAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + using var creationContext = this.PersistenceContextProvider.CreateNewContext(gameConfiguration); + var newObject = creationContext.CreateNew(this.Type!); + var parameters = new ModalParameters(); + var modalType = typeof(ModalCreateNew<>).MakeGenericType(this.Type!); + + parameters.Add(nameof(ModalCreateNew.Item), newObject); + var options = new ModalOptions + { + DisableBackgroundCancel = true, + }; + + var modal = this.ModalService.Show(modalType, $"Create", parameters, options); + var result = await modal.Result.ConfigureAwait(false); + if (!result.Cancelled) + { + await creationContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + this.ToastService.ShowSuccess("New object successfully created."); + this._viewModels = null; + this._loadTask = Task.Run(() => this.LoadDataAsync(cancellationToken), cancellationToken); + } + } + /// /// The view model for the grid. /// We use this instead of the objects, because it makes the code simpler. diff --git a/src/Web/AdminPanel/Pages/EditMap.cs b/src/Web/AdminPanel/Pages/EditMap.cs index beaa7f22e..70124d88b 100644 --- a/src/Web/AdminPanel/Pages/EditMap.cs +++ b/src/Web/AdminPanel/Pages/EditMap.cs @@ -7,6 +7,7 @@ namespace MUnique.OpenMU.Web.AdminPanel.Pages; using System.Reflection; using System.Threading; using Blazored.Modal.Services; +using Blazored.Toast.Services; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; using Microsoft.AspNetCore.Components.Routing; @@ -41,6 +42,12 @@ public sealed class EditMap : ComponentBase, IDisposable [Inject] private IModalService ModalService { get; set; } = null!; + /// + /// Gets or sets the toast service. + /// + [Inject] + private IToastService ToastService { get; set; } = null!; + /// /// Gets or sets the game configuration. /// @@ -209,19 +216,17 @@ private async Task LoadDataAsync(CancellationToken cancellationToken) private async Task SaveChangesAsync() { - string text; try { var context = await this.GameConfigurationSource.GetContextAsync().ConfigureAwait(true); var success = await context.SaveChangesAsync().ConfigureAwait(true); - text = success ? "The changes have been saved." : "There were no changes to save."; + var text = success ? "The changes have been saved." : "There were no changes to save."; + this.ToastService.ShowSuccess(text); } catch (Exception ex) { this.Logger.LogError(ex, $"Error during saving"); - text = $"An unexpected error occured: {ex.Message}."; + this.ToastService.ShowError($"An unexpected error occured: {ex.Message}. See logs for more details."); } - - await this.ModalService.ShowMessageAsync("Save", text).ConfigureAwait(true); } } \ No newline at end of file diff --git a/src/Web/AdminPanel/Pages/Merchants.razor.cs b/src/Web/AdminPanel/Pages/Merchants.razor.cs index 78a27fc3e..70fde1235 100644 --- a/src/Web/AdminPanel/Pages/Merchants.razor.cs +++ b/src/Web/AdminPanel/Pages/Merchants.razor.cs @@ -6,10 +6,11 @@ namespace MUnique.OpenMU.Web.AdminPanel.Pages; using System.ComponentModel; using System.Threading; -using Blazored.Modal.Services; +using Blazored.Toast.Services; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.QuickGrid; using Microsoft.AspNetCore.Components.Routing; +using Microsoft.Extensions.Logging; using Microsoft.JSInterop; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.DataModel.Entities; @@ -43,10 +44,10 @@ public partial class Merchants : ComponentBase, IAsyncDisposable public IPersistenceContextProvider ContextProvider { get; set; } = null!; /// - /// Gets or sets the modal service. + /// Gets or sets the toast service. /// [Inject] - public IModalService ModalService { get; set; } = null!; + public IToastService ToastService { get; set; } = null!; /// /// Gets or sets the navigation manager. @@ -60,6 +61,12 @@ public partial class Merchants : ComponentBase, IAsyncDisposable [Inject] public IJSRuntime JavaScript { get; set; } = null!; + /// + /// Gets or sets the logger. + /// + [Inject] + public ILogger Logger { get; set; } = null!; + private IQueryable? ViewModels => this._viewModels?.AsQueryable(); /// @@ -149,25 +156,24 @@ private async Task OnMerchantEditClickAsync(MerchantStorageViewModel context) private async Task OnSaveButtonClickAsync() { - string text; try { if (this._persistenceContext is { } context) { var success = await context.SaveChangesAsync().ConfigureAwait(true); - text = success ? "The changes have been saved." : "There were no changes to save."; + var text = success ? "The changes have been saved." : "There were no changes to save."; + this.ToastService.ShowSuccess(text); } else { - text = "Failed, context not initialized"; + this.ToastService.ShowError("Failed, context not initialized."); } } catch (Exception ex) { - text = $"An unexpected error occurred: {ex.Message}."; + this.Logger.LogError(ex, $"An unexpected error occurred on save: {ex.Message}"); + this.ToastService.ShowError($"An unexpected error occurred: {ex.Message}"); } - - await this.ModalService.ShowMessageAsync("Save", text).ConfigureAwait(true); } private async Task OnCancelButtonClickAsync() diff --git a/src/Web/AdminPanel/Shared/MainLayout.razor b/src/Web/AdminPanel/Shared/MainLayout.razor index 968ad6795..d24de4437 100644 --- a/src/Web/AdminPanel/Shared/MainLayout.razor +++ b/src/Web/AdminPanel/Shared/MainLayout.razor @@ -1,7 +1,7 @@ @inherits LayoutComponentBase - +
About diff --git a/src/Web/AdminPanel/Startup.cs b/src/Web/AdminPanel/Startup.cs index 6d4f4f85d..7d27cfcae 100644 --- a/src/Web/AdminPanel/Startup.cs +++ b/src/Web/AdminPanel/Startup.cs @@ -6,6 +6,7 @@ namespace MUnique.OpenMU.Web.AdminPanel; using System.IO; using Blazored.Modal; +using Blazored.Toast; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; @@ -58,6 +59,7 @@ public void ConfigureServices(IServiceCollection services) setup.FeatureProviders.Add(new GenericControllerFeatureProvider())); services.AddBlazoredModal(); + services.AddBlazoredToast(); services.AddScoped(); services.AddScoped>(serviceProvider => serviceProvider.GetService()!); diff --git a/src/Web/AdminPanel/WebApplicationExtensions.cs b/src/Web/AdminPanel/WebApplicationExtensions.cs index 134f70db1..819dc7c52 100644 --- a/src/Web/AdminPanel/WebApplicationExtensions.cs +++ b/src/Web/AdminPanel/WebApplicationExtensions.cs @@ -6,6 +6,7 @@ namespace MUnique.OpenMU.Web.AdminPanel; using System.IO; using Blazored.Modal; +using Blazored.Toast; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting.StaticWebAssets; using Microsoft.Extensions.DependencyInjection; @@ -53,6 +54,7 @@ public static WebApplicationBuilder AddAdminPanel(this WebApplicationBuilder bui setup.FeatureProviders.Add(new GenericControllerFeatureProvider())); services.AddBlazoredModal(); + services.AddBlazoredToast(); services.AddScoped(); diff --git a/src/Web/AdminPanel/_Imports.razor b/src/Web/AdminPanel/_Imports.razor index 6255a7589..b649e8c5b 100644 --- a/src/Web/AdminPanel/_Imports.razor +++ b/src/Web/AdminPanel/_Imports.razor @@ -9,6 +9,8 @@ @using Blazored @using Blazored.Modal @using Blazored.Modal.Services +@using Blazored.Toast +@using Blazored.Toast.Services @using Blazored.Typeahead @using BlazorInputFile