Worked on admin page listing
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ public interface IAdminPageGenerator<TModel> {
|
|||||||
|
|
||||||
IAdminPageGenerator<TModel> DefaultSort<TProperty>(Expression<Func<TModel, TProperty>> propertyExpression, ListSortDirection direction);
|
IAdminPageGenerator<TModel> DefaultSort<TProperty>(Expression<Func<TModel, TProperty>> propertyExpression, ListSortDirection direction);
|
||||||
|
|
||||||
IAdminPageGenerator<TModel> ConfigureRepository<TRepository>() where TRepository : IModelRepository<TModel>;
|
IAdminPageGenerator<TModel> ConfigureRepository<TRepository>() where TRepository : ModelRepository<TModel>;
|
||||||
|
|
||||||
IAdminPropertyGenerator Property<TProperty>(Expression<Func<TModel, TProperty>> propertyExpression);
|
IAdminPropertyGenerator Property<TProperty>(Expression<Func<TModel, TProperty>> propertyExpression);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ public interface IAdminPropertyGenerator {
|
|||||||
IAdminPropertyGenerator DisplayInListing(bool display = true);
|
IAdminPropertyGenerator DisplayInListing(bool display = true);
|
||||||
IAdminPropertyGenerator Ignore(bool ignore = true);
|
IAdminPropertyGenerator Ignore(bool ignore = true);
|
||||||
IAdminPropertyGenerator Generated(bool generated = true);
|
IAdminPropertyGenerator Generated(bool generated = true);
|
||||||
|
IAdminPropertyGenerator Bold(bool bold = true);
|
||||||
|
|
||||||
IAdminPropertyGenerator DisplayName(string displayName);
|
IAdminPropertyGenerator DisplayName(string displayName);
|
||||||
IAdminPropertyGenerator Description(string description);
|
IAdminPropertyGenerator Description(string description);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using HopFrame.Web.Admin.Models;
|
using HopFrame.Web.Admin.Models;
|
||||||
using HopFrame.Web.Admin.Providers;
|
using HopFrame.Web.Admin.Providers;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace HopFrame.Web.Admin.Generators.Implementation;
|
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();
|
var properties = context.GetType().GetProperties();
|
||||||
|
|
||||||
foreach (var property in properties) {
|
foreach (var property in properties) {
|
||||||
@@ -66,6 +67,9 @@ internal class AdminContextGenerator : IAdminContextGenerator {
|
|||||||
if (page is null) continue;
|
if (page is null) continue;
|
||||||
|
|
||||||
provider.RegisterAdminPage(page.Title.ToLower(), page);
|
provider.RegisterAdminPage(page.Title.ToLower(), page);
|
||||||
|
|
||||||
|
if (page.RepositoryProvider is not null)
|
||||||
|
services.AddScoped(page.RepositoryProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ internal sealed class AdminPageGenerator<TModel> : IAdminPageGenerator<TModel>,
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAdminPageGenerator<TModel> ConfigureRepository<TRepository>() where TRepository : IModelRepository<TModel> {
|
public IAdminPageGenerator<TModel> ConfigureRepository<TRepository>() where TRepository : ModelRepository<TModel> {
|
||||||
Page.RepositoryProvider = typeof(TRepository);
|
Page.RepositoryProvider = typeof(TRepository);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,11 @@ internal sealed class AdminPropertyGenerator(string name, Type type) : IAdminPro
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IAdminPropertyGenerator Bold(bool bold = true) {
|
||||||
|
_property.Bold = bold;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public IAdminPropertyGenerator DisplayName(string displayName) {
|
public IAdminPropertyGenerator DisplayName(string displayName) {
|
||||||
_property.DisplayName = displayName;
|
_property.DisplayName = displayName;
|
||||||
return this;
|
return this;
|
||||||
@@ -69,6 +74,7 @@ internal sealed class AdminPropertyGenerator(string name, Type type) : IAdminPro
|
|||||||
if (attributes.Any(a => a is KeyAttribute)) {
|
if (attributes.Any(a => a is KeyAttribute)) {
|
||||||
pageGenerator.Page.DefaultSortPropertyName = property.Name;
|
pageGenerator.Page.DefaultSortPropertyName = property.Name;
|
||||||
Editable(false);
|
Editable(false);
|
||||||
|
Bold();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attributes.Any(a => a is AdminUnsortableAttribute))
|
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;
|
var attribute = attributes.Single(a => a is AdminDescriptionAttribute) as AdminDescriptionAttribute;
|
||||||
Description(attribute?.Description);
|
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)) {
|
if (attributes.Any(a => a is AdminPrefixAttribute)) {
|
||||||
var attribute = attributes.Single(a => a is AdminPrefixAttribute) as AdminPrefixAttribute;
|
var attribute = attributes.Single(a => a is AdminPrefixAttribute) as AdminPrefixAttribute;
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace HopFrame.Web.Admin;
|
|
||||||
|
|
||||||
public interface IModelRepository<TModel> {
|
|
||||||
Task<IEnumerable<TModel>> ReadAll();
|
|
||||||
Task<TModel> Create(TModel model);
|
|
||||||
Task<TModel> Update(TModel model);
|
|
||||||
Task Delete(TModel model);
|
|
||||||
}
|
|
||||||
33
src/HopFrame.Web.Admin/ModelRepository.cs
Normal file
33
src/HopFrame.Web.Admin/ModelRepository.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
namespace HopFrame.Web.Admin;
|
||||||
|
|
||||||
|
public abstract class ModelRepository<TModel> : IModelRepository {
|
||||||
|
public abstract Task<IEnumerable<TModel>> ReadAll();
|
||||||
|
public abstract Task<TModel> Create(TModel model);
|
||||||
|
public abstract Task<TModel> Update(TModel model);
|
||||||
|
public abstract Task Delete(TModel model);
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<IEnumerable<object>> ReadAllO() {
|
||||||
|
var models = await ReadAll();
|
||||||
|
return models.Select(m => (object)m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<object> CreateO(object model) {
|
||||||
|
return await Create((TModel)model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<object> UpdateO(object model) {
|
||||||
|
return await Update((TModel)model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DeleteO(object model) {
|
||||||
|
return Delete((TModel)model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IModelRepository {
|
||||||
|
Task<IEnumerable<object>> ReadAllO();
|
||||||
|
Task<object> CreateO(object model);
|
||||||
|
Task<object> UpdateO(object model);
|
||||||
|
Task DeleteO(object model);
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ public sealed class AdminPageProperty {
|
|||||||
public bool Editable { get; set; } = true;
|
public bool Editable { get; set; } = true;
|
||||||
public bool EditDisplayValue { get; set; } = true;
|
public bool EditDisplayValue { get; set; } = true;
|
||||||
public bool Generated { get; set; }
|
public bool Generated { get; set; }
|
||||||
|
public bool Bold { get; set; } = false;
|
||||||
public bool Ignore { get; set; }
|
public bool Ignore { get; set; }
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public Type Type { get; set; }
|
public Type Type { get; set; }
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public static class ServiceCollectionExtensions {
|
|||||||
|
|
||||||
var generator = new AdminContextGenerator();
|
var generator = new AdminContextGenerator();
|
||||||
var context = generator.CompileContext<TContext>();
|
var context = generator.CompileContext<TContext>();
|
||||||
AdminContextGenerator.RegisterPages(context, provider);
|
AdminContextGenerator.RegisterPages(context, provider, services);
|
||||||
services.AddSingleton(context);
|
services.AddSingleton(context);
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using HopFrame.Security;
|
|||||||
using HopFrame.Web.Admin;
|
using HopFrame.Web.Admin;
|
||||||
using HopFrame.Web.Admin.Generators;
|
using HopFrame.Web.Admin.Generators;
|
||||||
using HopFrame.Web.Admin.Models;
|
using HopFrame.Web.Admin.Models;
|
||||||
|
using HopFrame.Web.Repositories;
|
||||||
|
|
||||||
namespace HopFrame.Web;
|
namespace HopFrame.Web;
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ public class HopAdminContext : AdminPagesContext {
|
|||||||
public override void OnModelCreating(IAdminContextGenerator generator) {
|
public override void OnModelCreating(IAdminContextGenerator generator) {
|
||||||
generator.Page<User>()
|
generator.Page<User>()
|
||||||
.Description("On this page you can manage all user accounts.")
|
.Description("On this page you can manage all user accounts.")
|
||||||
|
.ConfigureRepository<UserProvider>()
|
||||||
.ViewPermission(AdminPermissions.ViewUsers)
|
.ViewPermission(AdminPermissions.ViewUsers)
|
||||||
.CreatePermission(AdminPermissions.AddUser)
|
.CreatePermission(AdminPermissions.AddUser)
|
||||||
.UpdatePermission(AdminPermissions.EditUser)
|
.UpdatePermission(AdminPermissions.EditUser)
|
||||||
@@ -35,6 +37,7 @@ public class HopAdminContext : AdminPagesContext {
|
|||||||
|
|
||||||
generator.Page<PermissionGroup>()
|
generator.Page<PermissionGroup>()
|
||||||
.Description("On this page you can view, create, edit and delete permission groups.")
|
.Description("On this page you can view, create, edit and delete permission groups.")
|
||||||
|
.ConfigureRepository<GroupProvider>()
|
||||||
.ViewPermission(AdminPermissions.ViewGroups)
|
.ViewPermission(AdminPermissions.ViewGroups)
|
||||||
.CreatePermission(AdminPermissions.AddGroup)
|
.CreatePermission(AdminPermissions.AddGroup)
|
||||||
.UpdatePermission(AdminPermissions.EditGroup)
|
.UpdatePermission(AdminPermissions.EditGroup)
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||||
@using HopFrame.Web.Components.Administration
|
@using HopFrame.Web.Components.Administration
|
||||||
@using BlazorStrap.V5
|
@using BlazorStrap.V5
|
||||||
|
@using HopFrame.Database.Repositories
|
||||||
|
@using HopFrame.Security.Claims
|
||||||
|
@using HopFrame.Web.Admin
|
||||||
@using HopFrame.Web.Components
|
@using HopFrame.Web.Components
|
||||||
|
|
||||||
<PageTitle>@_pageData.Title</PageTitle>
|
<PageTitle>@_pageData.Title</PageTitle>
|
||||||
@@ -48,25 +51,66 @@
|
|||||||
}
|
}
|
||||||
</BSTD>
|
</BSTD>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (_hasEditPermission || _hasDeletePermission) {
|
||||||
|
<BSTD>Actions</BSTD>
|
||||||
|
}
|
||||||
</BSTR>
|
</BSTR>
|
||||||
</BSTHead>
|
</BSTHead>
|
||||||
|
|
||||||
<BSTBody>
|
<BSTBody>
|
||||||
|
@foreach (var entry in _displayedModels) {
|
||||||
|
<BSTR>
|
||||||
|
@foreach (var prop in GetListingProperties()) {
|
||||||
|
<BSTD Class="@GetClass(prop)">
|
||||||
|
@GetValue(entry, prop).GetAwaiter().GetResult()
|
||||||
|
</BSTD>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (_hasEditPermission || _hasDeletePermission) {
|
||||||
|
<BSTD>
|
||||||
|
<BSButtonGroup>
|
||||||
|
@if (_hasEditPermission) {
|
||||||
|
<BSButton Color="BSColor.Warning" OnClick="() => Edit(entry)">Edit</BSButton>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (_hasDeletePermission) {
|
||||||
|
<BSButton Color="BSColor.Danger" OnClick="() => Delete(entry)">Delete</BSButton>
|
||||||
|
}
|
||||||
|
</BSButtonGroup>
|
||||||
|
</BSTD>
|
||||||
|
}
|
||||||
|
</BSTR>
|
||||||
|
}
|
||||||
</BSTBody>
|
</BSTBody>
|
||||||
</BSTable>
|
</BSTable>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@inject IAdminPagesProvider Pages
|
@inject IAdminPagesProvider Pages
|
||||||
|
@inject IServiceProvider Provider
|
||||||
|
@inject ITokenContext Auth
|
||||||
|
@inject IPermissionRepository Permissions
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
|
|
||||||
private AdminPage _pageData;
|
private AdminPage _pageData;
|
||||||
|
private IModelRepository _modelRepository;
|
||||||
|
private IEnumerable<object> _modelBuffer;
|
||||||
|
|
||||||
|
private bool _hasEditPermission;
|
||||||
|
private bool _hasDeletePermission;
|
||||||
|
|
||||||
private string _currentSortProperty;
|
private string _currentSortProperty;
|
||||||
private ListSortDirection _currentSortDirection;
|
private ListSortDirection _currentSortDirection;
|
||||||
private DateTime _lastSearch;
|
private DateTime _lastSearch;
|
||||||
|
private IList<object> _displayedModels;
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
protected override void OnInitialized() {
|
||||||
_pageData = Pages.LoadAdminPage(Url);
|
_pageData = Pages.LoadAdminPage(Url);
|
||||||
@@ -75,6 +119,17 @@
|
|||||||
_currentSortDirection = _pageData.DefaultSortDirection;
|
_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<AdminPageProperty> GetListingProperties() {
|
private IList<AdminPageProperty> GetListingProperties() {
|
||||||
return _pageData.Properties
|
return _pageData.Properties
|
||||||
.Where(p => p.Ignore == false)
|
.Where(p => p.Ignore == false)
|
||||||
@@ -82,8 +137,11 @@
|
|||||||
.ToList();
|
.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) {
|
private void OrderBy(string property, bool changeDir = true) {
|
||||||
@@ -93,6 +151,7 @@
|
|||||||
_currentSortDirection = ListSortDirection.Ascending;
|
_currentSortDirection = ListSortDirection.Ascending;
|
||||||
|
|
||||||
//TODO: Handle ordering
|
//TODO: Handle ordering
|
||||||
|
_displayedModels = _modelBuffer.ToList();
|
||||||
|
|
||||||
_currentSortProperty = property;
|
_currentSortProperty = property;
|
||||||
}
|
}
|
||||||
@@ -109,6 +168,26 @@
|
|||||||
if (timeSinceLastKeyPress < TimeSpan.FromMilliseconds(500)) return;
|
if (timeSinceLastKeyPress < TimeSpan.FromMilliseconds(500)) return;
|
||||||
|
|
||||||
//TODO: Handle searching
|
//TODO: Handle searching
|
||||||
Console.WriteLine(search);
|
OrderBy(_currentSortProperty, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> 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) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,3 @@ h3 {
|
|||||||
.reload, .sorter {
|
.reload, .sorter {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bold {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@page "/administration/groups"
|
@page "/administration/group"
|
||||||
@rendermode InteractiveServer
|
@rendermode InteractiveServer
|
||||||
@layout AdminLayout
|
@layout AdminLayout
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@page "/administration/users"
|
@page "/administration/user"
|
||||||
@rendermode InteractiveServer
|
@rendermode InteractiveServer
|
||||||
@layout AdminLayout
|
@layout AdminLayout
|
||||||
|
|
||||||
|
|||||||
24
src/HopFrame.Web/Repositories/GroupProvider.cs
Normal file
24
src/HopFrame.Web/Repositories/GroupProvider.cs
Normal file
@@ -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<PermissionGroup> {
|
||||||
|
public override async Task<IEnumerable<PermissionGroup>> ReadAll() {
|
||||||
|
return await repo.GetPermissionGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<PermissionGroup> Create(PermissionGroup model) {
|
||||||
|
return await repo.CreatePermissionGroup(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<PermissionGroup> Update(PermissionGroup model) {
|
||||||
|
await repo.EditPermissionGroup(model);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task Delete(PermissionGroup model) {
|
||||||
|
return repo.DeletePermissionGroup(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/HopFrame.Web/Repositories/UserProvider.cs
Normal file
24
src/HopFrame.Web/Repositories/UserProvider.cs
Normal file
@@ -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<User> {
|
||||||
|
public override async Task<IEnumerable<User>> ReadAll() {
|
||||||
|
return await repo.GetUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<User> Create(User model) {
|
||||||
|
return repo.AddUser(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<User> Update(User model) {
|
||||||
|
await repo.UpdateUser(model);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task Delete(User model) {
|
||||||
|
return repo.DeleteUser(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user