diff --git a/src/HopFrame.Database/Attributes/ListingPropertyAttribute.cs b/src/HopFrame.Database/Attributes/ListingPropertyAttribute.cs new file mode 100644 index 0000000..b5fc86a --- /dev/null +++ b/src/HopFrame.Database/Attributes/ListingPropertyAttribute.cs @@ -0,0 +1,4 @@ +namespace HopFrame.Database.Attributes; + +[AttributeUsage(AttributeTargets.Property)] +public sealed class ListingPropertyAttribute : Attribute; diff --git a/src/HopFrame.Database/Models/Permission.cs b/src/HopFrame.Database/Models/Permission.cs index db111ba..475632e 100644 --- a/src/HopFrame.Database/Models/Permission.cs +++ b/src/HopFrame.Database/Models/Permission.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; +using HopFrame.Database.Attributes; namespace HopFrame.Database.Models; @@ -9,7 +10,7 @@ public class Permission { [Key, Required, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public long Id { get; init; } - [Required, MaxLength(255)] + [Required, MaxLength(255), ListingProperty] public string PermissionName { get; set; } [Required] diff --git a/src/HopFrame.Database/Models/PermissionGroup.cs b/src/HopFrame.Database/Models/PermissionGroup.cs index 7a70ebd..b6613bb 100644 --- a/src/HopFrame.Database/Models/PermissionGroup.cs +++ b/src/HopFrame.Database/Models/PermissionGroup.cs @@ -1,11 +1,12 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using HopFrame.Database.Attributes; namespace HopFrame.Database.Models; public class PermissionGroup : IPermissionOwner { - [Key, Required, MaxLength(50)] + [Key, Required, MaxLength(50), ListingProperty] public string Name { get; init; } [Required, DefaultValue(false)] diff --git a/src/HopFrame.Database/Models/User.cs b/src/HopFrame.Database/Models/User.cs index 971d899..2fe6c8e 100644 --- a/src/HopFrame.Database/Models/User.cs +++ b/src/HopFrame.Database/Models/User.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; +using HopFrame.Database.Attributes; namespace HopFrame.Database.Models; @@ -8,7 +9,7 @@ public class User : IPermissionOwner { [Key, Required, MinLength(36), MaxLength(36)] public Guid Id { get; init; } - [MaxLength(50)] + [MaxLength(50), ListingProperty] public string Username { get; set; } [Required, MaxLength(50), EmailAddress] diff --git a/src/HopFrame.Web.Admin/Generators/IAdminPageGenerator.cs b/src/HopFrame.Web.Admin/Generators/IAdminPageGenerator.cs index 0e112f1..0e7a72c 100644 --- a/src/HopFrame.Web.Admin/Generators/IAdminPageGenerator.cs +++ b/src/HopFrame.Web.Admin/Generators/IAdminPageGenerator.cs @@ -23,5 +23,6 @@ public interface IAdminPageGenerator { IAdminPageGenerator ConfigureRepository() where TRepository : ModelRepository; IAdminPropertyGenerator Property(Expression> propertyExpression); + IAdminPageGenerator ListingProperty(Expression> propertyExpression); } diff --git a/src/HopFrame.Web.Admin/Generators/IAdminPropertyGenerator.cs b/src/HopFrame.Web.Admin/Generators/IAdminPropertyGenerator.cs index b62e7af..2d75abe 100644 --- a/src/HopFrame.Web.Admin/Generators/IAdminPropertyGenerator.cs +++ b/src/HopFrame.Web.Admin/Generators/IAdminPropertyGenerator.cs @@ -14,5 +14,6 @@ public interface IAdminPropertyGenerator { IAdminPropertyGenerator Description(string description); IAdminPropertyGenerator Prefix(string prefix); IAdminPropertyGenerator Validator(Func validator); + IAdminPropertyGenerator IsSelector(); } \ No newline at end of file diff --git a/src/HopFrame.Web.Admin/Generators/Implementation/AdminPageGenerator.cs b/src/HopFrame.Web.Admin/Generators/Implementation/AdminPageGenerator.cs index fab851a..4ea22e5 100644 --- a/src/HopFrame.Web.Admin/Generators/Implementation/AdminPageGenerator.cs +++ b/src/HopFrame.Web.Admin/Generators/Implementation/AdminPageGenerator.cs @@ -113,6 +113,12 @@ internal sealed class AdminPageGenerator : IAdminPageGenerator, return generator; } + public IAdminPageGenerator ListingProperty(Expression> propertyExpression) { + var property = GetPropertyInfo(propertyExpression); + Page.ListingProperty = property.Name; + return this; + } + public AdminPage Compile() { var properties = new List(); diff --git a/src/HopFrame.Web.Admin/Generators/Implementation/AdminPropertyGenerator.cs b/src/HopFrame.Web.Admin/Generators/Implementation/AdminPropertyGenerator.cs index e14ff75..92df826 100644 --- a/src/HopFrame.Web.Admin/Generators/Implementation/AdminPropertyGenerator.cs +++ b/src/HopFrame.Web.Admin/Generators/Implementation/AdminPropertyGenerator.cs @@ -70,6 +70,11 @@ internal sealed class AdminPropertyGenerator(string name, Type type) : IAdminPro return this; } + public IAdminPropertyGenerator IsSelector() { + _property.SelectorType = typeof(TSelector); + return this; + } + public AdminPageProperty Compile() { _property.DisplayName ??= _property.Name; return _property; diff --git a/src/HopFrame.Web.Admin/Models/AdminPage.cs b/src/HopFrame.Web.Admin/Models/AdminPage.cs index 509fed8..eedd4a5 100644 --- a/src/HopFrame.Web.Admin/Models/AdminPage.cs +++ b/src/HopFrame.Web.Admin/Models/AdminPage.cs @@ -11,6 +11,7 @@ public class AdminPage { public string Url { get; set; } public AdminPagePermissions Permissions { get; set; } public IList Properties { get; set; } + public string ListingProperty { get; set; } [JsonIgnore] public Type RepositoryProvider { get; set; } diff --git a/src/HopFrame.Web.Admin/Models/AdminPageProperty.cs b/src/HopFrame.Web.Admin/Models/AdminPageProperty.cs index db2f743..6707458 100644 --- a/src/HopFrame.Web.Admin/Models/AdminPageProperty.cs +++ b/src/HopFrame.Web.Admin/Models/AdminPageProperty.cs @@ -19,6 +19,8 @@ public sealed class AdminPageProperty { [JsonIgnore] public Type Type { get; set; } + public Type SelectorType { get; set; } + public Func Validator { get; set; } public object GetValue(object entry) { diff --git a/src/HopFrame.Web.Admin/Providers/IAdminPagesProvider.cs b/src/HopFrame.Web.Admin/Providers/IAdminPagesProvider.cs index d20c66c..564f52a 100644 --- a/src/HopFrame.Web.Admin/Providers/IAdminPagesProvider.cs +++ b/src/HopFrame.Web.Admin/Providers/IAdminPagesProvider.cs @@ -7,5 +7,6 @@ public interface IAdminPagesProvider { internal void RegisterAdminPage(string url, AdminPage page); AdminPage LoadAdminPage(string url); IList LoadRegisteredAdminPages(); + AdminPage HasPageFor(Type type); } \ No newline at end of file diff --git a/src/HopFrame.Web.Admin/Providers/Implementation/AdminPagesProvider.cs b/src/HopFrame.Web.Admin/Providers/Implementation/AdminPagesProvider.cs index d68a5ae..b2e38b4 100644 --- a/src/HopFrame.Web.Admin/Providers/Implementation/AdminPagesProvider.cs +++ b/src/HopFrame.Web.Admin/Providers/Implementation/AdminPagesProvider.cs @@ -16,4 +16,11 @@ public class AdminPagesProvider : IAdminPagesProvider { public IList LoadRegisteredAdminPages() { return _pages.Values.ToList(); } + + public AdminPage HasPageFor(Type type) { + return _pages + .Where(p => p.Value.ModelType == type) + .Select(p => p.Value) + .SingleOrDefault(); + } } \ No newline at end of file diff --git a/src/HopFrame.Web/Components/Administration/AdminPageModal.razor b/src/HopFrame.Web/Components/Administration/AdminPageModal.razor index 04241f2..5fa3ad8 100644 --- a/src/HopFrame.Web/Components/Administration/AdminPageModal.razor +++ b/src/HopFrame.Web/Components/Administration/AdminPageModal.razor @@ -1,11 +1,15 @@ @rendermode InteractiveServer +@using System.Collections +@using System.ComponentModel.DataAnnotations @using BlazorStrap @using BlazorStrap.Shared.Components.Modal @using static Microsoft.AspNetCore.Components.Web.RenderMode @using BlazorStrap.V5 +@using HopFrame.Database.Attributes @using HopFrame.Web.Admin @using HopFrame.Web.Admin.Models +@using HopFrame.Web.Admin.Providers @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Web @@ -23,9 +27,54 @@ @if (!_isEdit && !prop.Editable) continue;
- @prop.DisplayName - - + @if (IsListType(prop)) { + @prop.DisplayName + + + + @foreach (var element in GetListPropertyValues(prop).Select((e, i) => new {e, i})) { + + + + + + @element.e + + } + + + +
+ @if (prop.SelectorType is null) { + + Add + } + else { + @* TODO: implement selector + + + @foreach (var group in _allGroups) { + @if (_group.Permissions.All(g => g.PermissionName != group.Name) && group.Name != _group.Name) { + + } + } + + Add*@ + } +
+
+
+ } + else if (IsSwitch(prop)) { +
+ @prop.DisplayName + +
+ } + else { + @prop.DisplayName + + }
} @@ -38,6 +87,7 @@ @inject IServiceProvider Provider +@inject IAdminPagesProvider PageProvider @code { [Parameter] @@ -51,9 +101,11 @@ private AdminPage _currentPage; private object _entry; private bool _isEdit; + private IDictionary _inputValues; public async Task Show(AdminPage page, object entryToEdit = null) { _entry = null; + _inputValues = new Dictionary(); _currentPage = page; _entry = entryToEdit; @@ -81,11 +133,67 @@ private bool IsDisabled(AdminPageProperty prop) => !prop.Editable; private bool IsRequired(AdminPageProperty prop) => !_isEdit ? prop.Required : prop.Required && prop.EditDisplayValue; + private bool IsSwitch(AdminPageProperty prop) => prop.Type == typeof(bool); + + private bool IsListType(AdminPageProperty prop) { + if (!prop.Type.IsGenericType) return false; + var generic = prop.Type.GenericTypeArguments[0]; + var listType = typeof(IList<>).MakeGenericType(generic); + return prop.Type.IsAssignableFrom(listType); + } + + private IList GetListPropertyValues(AdminPageProperty prop) { + if (!IsListType(prop)) return new List(); + var list = new List(); + + var values = prop.GetValue(_entry); + + if (values is null) { + prop.SetValue(_entry, Activator.CreateInstance(prop.Type)); + return list; + } + + foreach (var value in (IEnumerable)values) { + list.Add(MapPropertyValue(value, prop)); + } + + return list; + } private string GetPropertyValue(AdminPageProperty property) { if (!_isEdit) return ""; if (!property.EditDisplayValue) return ""; - return property.GetValue(_entry)?.ToString(); + return MapPropertyValue(property.GetValue(_entry), property); + } + + public string MapPropertyValue(object value, AdminPageProperty property) { + if (value is null) return string.Empty; + var type = value.GetType(); + + var page = PageProvider.HasPageFor(type); + if (page is not null && !string.IsNullOrWhiteSpace(page.ListingProperty)) { + var prop = page.Properties + .SingleOrDefault(p => p.Name == page.ListingProperty); + + if (prop is not null) { + return MapPropertyValue(prop.GetValue(value), prop); + } + } + + if (type.GetProperties().Any(p => p.GetCustomAttributes(false).Any(a => a is ListingPropertyAttribute))) { + var prop = type.GetProperties() + .SingleOrDefault(p => p.GetCustomAttributes(false).Any(a => a is ListingPropertyAttribute)); + + return MapPropertyValue(prop?.GetValue(value), property); + } + + var stringValue = value.ToString(); + + if (!string.IsNullOrWhiteSpace(property.Prefix)) { + return stringValue?.Replace(property.Prefix, ""); + } + + return stringValue; } private string GetInputType(AdminPageProperty property) { @@ -97,11 +205,18 @@ private void Validate(object sender, ValidationRequestedEventArgs e) { foreach (var value in _values) { + if (value.Key.Validator is null) continue; if (value.Key.Validator?.Invoke(value.Value) == true) continue; Console.WriteLine("INVALID"); } } + private void DeleteListItem(AdminPageProperty prop, int index) { + Console.WriteLine(index); + var list = prop.GetValue(_entry); + list.RemoveAt(index); + } + private async void Save() { foreach (var value in _values) { value.Key.SetValue(_entry, value.Value); diff --git a/src/HopFrame.Web/HopAdminContext.cs b/src/HopFrame.Web/HopAdminContext.cs index e4914c9..54be7d4 100644 --- a/src/HopFrame.Web/HopAdminContext.cs +++ b/src/HopFrame.Web/HopAdminContext.cs @@ -29,7 +29,8 @@ public class HopAdminContext : AdminPagesContext { .Editable(false); generator.Page().Property(u => u.Permissions) - .DisplayInListing(false); + .DisplayInListing(false) + .IsSelector(); generator.Page().Property(u => u.Tokens) .Ignore(); diff --git a/src/HopFrame.Web/Pages/Administration/AdminPageList.razor b/src/HopFrame.Web/Pages/Administration/AdminPageList.razor index a04a374..17c5f64 100644 --- a/src/HopFrame.Web/Pages/Administration/AdminPageList.razor +++ b/src/HopFrame.Web/Pages/Administration/AdminPageList.razor @@ -214,12 +214,8 @@ StateHasChanged(); } - private static string GetPrintableValue(object element, AdminPageProperty prop) { - var entry = prop.GetValue(element); - - //TODO: convert relational types - - return entry?.ToString(); + private string GetPrintableValue(object element, AdminPageProperty prop) { + return _modal?.MapPropertyValue(prop.GetValue(element), prop); } private async void Create() {