From 075ca2286f80fb7b45dcf679767aa15c868e4225 Mon Sep 17 00:00:00 2001 From: Leon Hoppe Date: Mon, 7 Oct 2024 17:39:05 +0200 Subject: [PATCH] Worked on admin page listing --- .../Attributes/Members/AdminBoldAttribute.cs | 6 ++ .../Generators/IAdminPageGenerator.cs | 2 +- .../Generators/IAdminPropertyGenerator.cs | 1 + .../Implementation/AdminContextGenerator.cs | 6 +- .../Implementation/AdminPageGenerator.cs | 2 +- .../Implementation/AdminPropertyGenerator.cs | 11 +++ src/HopFrame.Web.Admin/IModelRepository.cs | 8 -- src/HopFrame.Web.Admin/ModelRepository.cs | 33 +++++++ .../Models/AdminPageProperty.cs | 1 + .../ServiceCollectionExtensions.cs | 2 +- src/HopFrame.Web/HopAdminContext.cs | 3 + .../Pages/Administration/AdminPageList.razor | 85 ++++++++++++++++++- .../Administration/AdminPageList.razor.css | 4 - .../Pages/Administration/GroupsPage.razor | 2 +- .../Pages/Administration/UsersPage.razor | 2 +- .../Repositories/GroupProvider.cs | 24 ++++++ src/HopFrame.Web/Repositories/UserProvider.cs | 24 ++++++ 17 files changed, 195 insertions(+), 21 deletions(-) create mode 100644 src/HopFrame.Web.Admin/Attributes/Members/AdminBoldAttribute.cs delete mode 100644 src/HopFrame.Web.Admin/IModelRepository.cs create mode 100644 src/HopFrame.Web.Admin/ModelRepository.cs create mode 100644 src/HopFrame.Web/Repositories/GroupProvider.cs create mode 100644 src/HopFrame.Web/Repositories/UserProvider.cs diff --git a/src/HopFrame.Web.Admin/Attributes/Members/AdminBoldAttribute.cs b/src/HopFrame.Web.Admin/Attributes/Members/AdminBoldAttribute.cs new file mode 100644 index 0000000..ffc3798 --- /dev/null +++ b/src/HopFrame.Web.Admin/Attributes/Members/AdminBoldAttribute.cs @@ -0,0 +1,6 @@ +namespace HopFrame.Web.Admin.Attributes.Members; + +[AttributeUsage(AttributeTargets.Property)] +public class AdminBoldAttribute(bool bold = true) : Attribute { + public bool Bold { get; set; } = bold; +} \ No newline at end of file diff --git a/src/HopFrame.Web.Admin/Generators/IAdminPageGenerator.cs b/src/HopFrame.Web.Admin/Generators/IAdminPageGenerator.cs index f9902fb..0e112f1 100644 --- a/src/HopFrame.Web.Admin/Generators/IAdminPageGenerator.cs +++ b/src/HopFrame.Web.Admin/Generators/IAdminPageGenerator.cs @@ -20,7 +20,7 @@ public interface IAdminPageGenerator { IAdminPageGenerator DefaultSort(Expression> propertyExpression, ListSortDirection direction); - IAdminPageGenerator ConfigureRepository() where TRepository : IModelRepository; + IAdminPageGenerator ConfigureRepository() where TRepository : ModelRepository; IAdminPropertyGenerator Property(Expression> propertyExpression); diff --git a/src/HopFrame.Web.Admin/Generators/IAdminPropertyGenerator.cs b/src/HopFrame.Web.Admin/Generators/IAdminPropertyGenerator.cs index d32fe15..1e0091a 100644 --- a/src/HopFrame.Web.Admin/Generators/IAdminPropertyGenerator.cs +++ b/src/HopFrame.Web.Admin/Generators/IAdminPropertyGenerator.cs @@ -8,6 +8,7 @@ public interface IAdminPropertyGenerator { IAdminPropertyGenerator DisplayInListing(bool display = true); IAdminPropertyGenerator Ignore(bool ignore = true); IAdminPropertyGenerator Generated(bool generated = true); + IAdminPropertyGenerator Bold(bool bold = true); IAdminPropertyGenerator DisplayName(string displayName); IAdminPropertyGenerator Description(string description); diff --git a/src/HopFrame.Web.Admin/Generators/Implementation/AdminContextGenerator.cs b/src/HopFrame.Web.Admin/Generators/Implementation/AdminContextGenerator.cs index 0446be9..aa90737 100644 --- a/src/HopFrame.Web.Admin/Generators/Implementation/AdminContextGenerator.cs +++ b/src/HopFrame.Web.Admin/Generators/Implementation/AdminContextGenerator.cs @@ -1,5 +1,6 @@ using HopFrame.Web.Admin.Models; using HopFrame.Web.Admin.Providers; +using Microsoft.Extensions.DependencyInjection; namespace HopFrame.Web.Admin.Generators.Implementation; @@ -58,7 +59,7 @@ internal class AdminContextGenerator : IAdminContextGenerator { - public static void RegisterPages(AdminPagesContext context, IAdminPagesProvider provider) { + public static void RegisterPages(AdminPagesContext context, IAdminPagesProvider provider, IServiceCollection services) { var properties = context.GetType().GetProperties(); foreach (var property in properties) { @@ -66,6 +67,9 @@ internal class AdminContextGenerator : IAdminContextGenerator { if (page is null) continue; provider.RegisterAdminPage(page.Title.ToLower(), page); + + if (page.RepositoryProvider is not null) + services.AddScoped(page.RepositoryProvider); } } diff --git a/src/HopFrame.Web.Admin/Generators/Implementation/AdminPageGenerator.cs b/src/HopFrame.Web.Admin/Generators/Implementation/AdminPageGenerator.cs index 2975892..33db4d7 100644 --- a/src/HopFrame.Web.Admin/Generators/Implementation/AdminPageGenerator.cs +++ b/src/HopFrame.Web.Admin/Generators/Implementation/AdminPageGenerator.cs @@ -94,7 +94,7 @@ internal sealed class AdminPageGenerator : IAdminPageGenerator, return this; } - public IAdminPageGenerator ConfigureRepository() where TRepository : IModelRepository { + public IAdminPageGenerator ConfigureRepository() where TRepository : ModelRepository { Page.RepositoryProvider = typeof(TRepository); return this; } diff --git a/src/HopFrame.Web.Admin/Generators/Implementation/AdminPropertyGenerator.cs b/src/HopFrame.Web.Admin/Generators/Implementation/AdminPropertyGenerator.cs index 68fea2b..92a755a 100644 --- a/src/HopFrame.Web.Admin/Generators/Implementation/AdminPropertyGenerator.cs +++ b/src/HopFrame.Web.Admin/Generators/Implementation/AdminPropertyGenerator.cs @@ -45,6 +45,11 @@ internal sealed class AdminPropertyGenerator(string name, Type type) : IAdminPro return this; } + public IAdminPropertyGenerator Bold(bool bold = true) { + _property.Bold = bold; + return this; + } + public IAdminPropertyGenerator DisplayName(string displayName) { _property.DisplayName = displayName; return this; @@ -69,6 +74,7 @@ internal sealed class AdminPropertyGenerator(string name, Type type) : IAdminPro if (attributes.Any(a => a is KeyAttribute)) { pageGenerator.Page.DefaultSortPropertyName = property.Name; Editable(false); + Bold(); } if (attributes.Any(a => a is AdminUnsortableAttribute)) @@ -99,6 +105,11 @@ internal sealed class AdminPropertyGenerator(string name, Type type) : IAdminPro var attribute = attributes.Single(a => a is AdminDescriptionAttribute) as AdminDescriptionAttribute; Description(attribute?.Description); } + + if (attributes.Any(a => a is AdminBoldAttribute)) { + var attribute = attributes.Single(a => a is AdminBoldAttribute) as AdminBoldAttribute; + Bold(attribute?.Bold == true); + } if (attributes.Any(a => a is AdminPrefixAttribute)) { var attribute = attributes.Single(a => a is AdminPrefixAttribute) as AdminPrefixAttribute; diff --git a/src/HopFrame.Web.Admin/IModelRepository.cs b/src/HopFrame.Web.Admin/IModelRepository.cs deleted file mode 100644 index 6bdffe0..0000000 --- a/src/HopFrame.Web.Admin/IModelRepository.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace HopFrame.Web.Admin; - -public interface IModelRepository { - Task> ReadAll(); - Task Create(TModel model); - Task Update(TModel model); - Task Delete(TModel model); -} diff --git a/src/HopFrame.Web.Admin/ModelRepository.cs b/src/HopFrame.Web.Admin/ModelRepository.cs new file mode 100644 index 0000000..45de247 --- /dev/null +++ b/src/HopFrame.Web.Admin/ModelRepository.cs @@ -0,0 +1,33 @@ +namespace HopFrame.Web.Admin; + +public abstract class ModelRepository : IModelRepository { + public abstract Task> ReadAll(); + public abstract Task Create(TModel model); + public abstract Task Update(TModel model); + public abstract Task Delete(TModel model); + + + public async Task> ReadAllO() { + var models = await ReadAll(); + return models.Select(m => (object)m); + } + + public async Task CreateO(object model) { + return await Create((TModel)model); + } + + public async Task UpdateO(object model) { + return await Update((TModel)model); + } + + public Task DeleteO(object model) { + return Delete((TModel)model); + } +} + +public interface IModelRepository { + Task> ReadAllO(); + Task CreateO(object model); + Task UpdateO(object model); + Task DeleteO(object model); +} diff --git a/src/HopFrame.Web.Admin/Models/AdminPageProperty.cs b/src/HopFrame.Web.Admin/Models/AdminPageProperty.cs index eeaf3a2..cf5bbc6 100644 --- a/src/HopFrame.Web.Admin/Models/AdminPageProperty.cs +++ b/src/HopFrame.Web.Admin/Models/AdminPageProperty.cs @@ -13,6 +13,7 @@ public sealed class AdminPageProperty { public bool Editable { get; set; } = true; public bool EditDisplayValue { get; set; } = true; public bool Generated { get; set; } + public bool Bold { get; set; } = false; public bool Ignore { get; set; } [JsonIgnore] public Type Type { get; set; } diff --git a/src/HopFrame.Web.Admin/ServiceCollectionExtensions.cs b/src/HopFrame.Web.Admin/ServiceCollectionExtensions.cs index b8ee37e..f3e8370 100644 --- a/src/HopFrame.Web.Admin/ServiceCollectionExtensions.cs +++ b/src/HopFrame.Web.Admin/ServiceCollectionExtensions.cs @@ -16,7 +16,7 @@ public static class ServiceCollectionExtensions { var generator = new AdminContextGenerator(); var context = generator.CompileContext(); - AdminContextGenerator.RegisterPages(context, provider); + AdminContextGenerator.RegisterPages(context, provider, services); services.AddSingleton(context); return services; diff --git a/src/HopFrame.Web/HopAdminContext.cs b/src/HopFrame.Web/HopAdminContext.cs index 3a5c067..e4914c9 100644 --- a/src/HopFrame.Web/HopAdminContext.cs +++ b/src/HopFrame.Web/HopAdminContext.cs @@ -3,6 +3,7 @@ using HopFrame.Security; using HopFrame.Web.Admin; using HopFrame.Web.Admin.Generators; using HopFrame.Web.Admin.Models; +using HopFrame.Web.Repositories; namespace HopFrame.Web; @@ -14,6 +15,7 @@ public class HopAdminContext : AdminPagesContext { public override void OnModelCreating(IAdminContextGenerator generator) { generator.Page() .Description("On this page you can manage all user accounts.") + .ConfigureRepository() .ViewPermission(AdminPermissions.ViewUsers) .CreatePermission(AdminPermissions.AddUser) .UpdatePermission(AdminPermissions.EditUser) @@ -35,6 +37,7 @@ public class HopAdminContext : AdminPagesContext { generator.Page() .Description("On this page you can view, create, edit and delete permission groups.") + .ConfigureRepository() .ViewPermission(AdminPermissions.ViewGroups) .CreatePermission(AdminPermissions.AddGroup) .UpdatePermission(AdminPermissions.EditGroup) diff --git a/src/HopFrame.Web/Pages/Administration/AdminPageList.razor b/src/HopFrame.Web/Pages/Administration/AdminPageList.razor index af1bbd4..c72a1fd 100644 --- a/src/HopFrame.Web/Pages/Administration/AdminPageList.razor +++ b/src/HopFrame.Web/Pages/Administration/AdminPageList.razor @@ -11,6 +11,9 @@ @using static Microsoft.AspNetCore.Components.Web.RenderMode @using HopFrame.Web.Components.Administration @using BlazorStrap.V5 +@using HopFrame.Database.Repositories +@using HopFrame.Security.Claims +@using HopFrame.Web.Admin @using HopFrame.Web.Components @_pageData.Title @@ -48,25 +51,66 @@ } } + + @if (_hasEditPermission || _hasDeletePermission) { + Actions + } - + @foreach (var entry in _displayedModels) { + + @foreach (var prop in GetListingProperties()) { + + @GetValue(entry, prop).GetAwaiter().GetResult() + + } + + @if (_hasEditPermission || _hasDeletePermission) { + + + @if (_hasEditPermission) { + Edit + } + + @if (_hasDeletePermission) { + Delete + } + + + } + + } + + @inject IAdminPagesProvider Pages +@inject IServiceProvider Provider +@inject ITokenContext Auth +@inject IPermissionRepository Permissions @code { [Parameter] public string Url { get; set; } private AdminPage _pageData; + private IModelRepository _modelRepository; + private IEnumerable _modelBuffer; + + private bool _hasEditPermission; + private bool _hasDeletePermission; private string _currentSortProperty; private ListSortDirection _currentSortDirection; private DateTime _lastSearch; + private IList _displayedModels; protected override void OnInitialized() { _pageData = Pages.LoadAdminPage(Url); @@ -75,6 +119,17 @@ _currentSortDirection = _pageData.DefaultSortDirection; } + protected override async Task OnInitializedAsync() { + _modelRepository = Provider.GetService(_pageData.RepositoryProvider) as IModelRepository; + if (_modelRepository is null) + throw new ArgumentException($"AdminPage '{_pageData.Title}' does not specify a model repository!'"); + + _hasEditPermission = await Permissions.HasPermission(Auth.User, _pageData.Permissions.Update); + _hasDeletePermission = await Permissions.HasPermission(Auth.User, _pageData.Permissions.Delete); + + Reload(); + } + private IList GetListingProperties() { return _pageData.Properties .Where(p => p.Ignore == false) @@ -82,8 +137,11 @@ .ToList(); } - private void Reload() { + private async void Reload() { + _modelBuffer = await _modelRepository.ReadAllO(); + _currentSortDirection = _pageData.DefaultSortDirection; + OrderBy(_pageData.DefaultSortPropertyName, false); } private void OrderBy(string property, bool changeDir = true) { @@ -93,6 +151,7 @@ _currentSortDirection = ListSortDirection.Ascending; //TODO: Handle ordering + _displayedModels = _modelBuffer.ToList(); _currentSortProperty = property; } @@ -109,6 +168,26 @@ if (timeSinceLastKeyPress < TimeSpan.FromMilliseconds(500)) return; //TODO: Handle searching - Console.WriteLine(search); + OrderBy(_currentSortProperty, false); + } + + private async Task GetValue(object entry, AdminPageProperty property) { + object propValue = entry.GetType().GetProperty(property.Name)?.GetValue(entry); + + return propValue?.ToString(); + } + + private string GetClass(AdminPageProperty property) { + if (property.Bold) + return "bold"; + return ""; + } + + private async void Edit(object entry) { + + } + + private async void Delete(object entry) { + } } \ No newline at end of file diff --git a/src/HopFrame.Web/Pages/Administration/AdminPageList.razor.css b/src/HopFrame.Web/Pages/Administration/AdminPageList.razor.css index cf49df0..6f4b803 100644 --- a/src/HopFrame.Web/Pages/Administration/AdminPageList.razor.css +++ b/src/HopFrame.Web/Pages/Administration/AdminPageList.razor.css @@ -20,7 +20,3 @@ h3 { .reload, .sorter { cursor: pointer; } - -.bold { - font-weight: bold; -} diff --git a/src/HopFrame.Web/Pages/Administration/GroupsPage.razor b/src/HopFrame.Web/Pages/Administration/GroupsPage.razor index 4591f60..42269da 100644 --- a/src/HopFrame.Web/Pages/Administration/GroupsPage.razor +++ b/src/HopFrame.Web/Pages/Administration/GroupsPage.razor @@ -1,4 +1,4 @@ -@page "/administration/groups" +@page "/administration/group" @rendermode InteractiveServer @layout AdminLayout diff --git a/src/HopFrame.Web/Pages/Administration/UsersPage.razor b/src/HopFrame.Web/Pages/Administration/UsersPage.razor index 47b871c..767dc98 100644 --- a/src/HopFrame.Web/Pages/Administration/UsersPage.razor +++ b/src/HopFrame.Web/Pages/Administration/UsersPage.razor @@ -1,4 +1,4 @@ -@page "/administration/users" +@page "/administration/user" @rendermode InteractiveServer @layout AdminLayout diff --git a/src/HopFrame.Web/Repositories/GroupProvider.cs b/src/HopFrame.Web/Repositories/GroupProvider.cs new file mode 100644 index 0000000..953a9d7 --- /dev/null +++ b/src/HopFrame.Web/Repositories/GroupProvider.cs @@ -0,0 +1,24 @@ +using HopFrame.Database.Models; +using HopFrame.Database.Repositories; +using HopFrame.Web.Admin; + +namespace HopFrame.Web.Repositories; + +internal sealed class GroupProvider(IGroupRepository repo) : ModelRepository { + public override async Task> ReadAll() { + return await repo.GetPermissionGroups(); + } + + public override async Task Create(PermissionGroup model) { + return await repo.CreatePermissionGroup(model); + } + + public override async Task Update(PermissionGroup model) { + await repo.EditPermissionGroup(model); + return model; + } + + public override Task Delete(PermissionGroup model) { + return repo.DeletePermissionGroup(model); + } +} \ No newline at end of file diff --git a/src/HopFrame.Web/Repositories/UserProvider.cs b/src/HopFrame.Web/Repositories/UserProvider.cs new file mode 100644 index 0000000..49ca30f --- /dev/null +++ b/src/HopFrame.Web/Repositories/UserProvider.cs @@ -0,0 +1,24 @@ +using HopFrame.Database.Models; +using HopFrame.Database.Repositories; +using HopFrame.Web.Admin; + +namespace HopFrame.Web.Repositories; + +internal sealed class UserProvider(IUserRepository repo) : ModelRepository { + public override async Task> ReadAll() { + return await repo.GetUsers(); + } + + public override Task Create(User model) { + return repo.AddUser(model); + } + + public override async Task Update(User model) { + await repo.UpdateUser(model); + return model; + } + + public override Task Delete(User model) { + return repo.DeleteUser(model); + } +} \ No newline at end of file