Working on Admin page modal
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
namespace HopFrame.Database.Attributes;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class ListingPropertyAttribute : Attribute;
|
||||
@@ -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]
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -23,5 +23,6 @@ public interface IAdminPageGenerator<TModel> {
|
||||
IAdminPageGenerator<TModel> ConfigureRepository<TRepository>() where TRepository : ModelRepository<TModel>;
|
||||
|
||||
IAdminPropertyGenerator Property<TProperty>(Expression<Func<TModel, TProperty>> propertyExpression);
|
||||
IAdminPageGenerator<TModel> ListingProperty<TProperty>(Expression<Func<TModel, TProperty>> propertyExpression);
|
||||
|
||||
}
|
||||
|
||||
@@ -14,5 +14,6 @@ public interface IAdminPropertyGenerator {
|
||||
IAdminPropertyGenerator Description(string description);
|
||||
IAdminPropertyGenerator Prefix(string prefix);
|
||||
IAdminPropertyGenerator Validator(Func<object, bool> validator);
|
||||
IAdminPropertyGenerator IsSelector<TSelector>();
|
||||
|
||||
}
|
||||
@@ -113,6 +113,12 @@ internal sealed class AdminPageGenerator<TModel> : IAdminPageGenerator<TModel>,
|
||||
return generator;
|
||||
}
|
||||
|
||||
public IAdminPageGenerator<TModel> ListingProperty<TProperty>(Expression<Func<TModel, TProperty>> propertyExpression) {
|
||||
var property = GetPropertyInfo(propertyExpression);
|
||||
Page.ListingProperty = property.Name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminPage<TModel> Compile() {
|
||||
var properties = new List<AdminPageProperty>();
|
||||
|
||||
|
||||
@@ -70,6 +70,11 @@ internal sealed class AdminPropertyGenerator(string name, Type type) : IAdminPro
|
||||
return this;
|
||||
}
|
||||
|
||||
public IAdminPropertyGenerator IsSelector<TSelector>() {
|
||||
_property.SelectorType = typeof(TSelector);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminPageProperty Compile() {
|
||||
_property.DisplayName ??= _property.Name;
|
||||
return _property;
|
||||
|
||||
@@ -11,6 +11,7 @@ public class AdminPage {
|
||||
public string Url { get; set; }
|
||||
public AdminPagePermissions Permissions { get; set; }
|
||||
public IList<AdminPageProperty> Properties { get; set; }
|
||||
public string ListingProperty { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Type RepositoryProvider { get; set; }
|
||||
|
||||
@@ -19,6 +19,8 @@ public sealed class AdminPageProperty {
|
||||
[JsonIgnore]
|
||||
public Type Type { get; set; }
|
||||
|
||||
public Type SelectorType { get; set; }
|
||||
|
||||
public Func<object, bool> Validator { get; set; }
|
||||
|
||||
public object GetValue(object entry) {
|
||||
|
||||
@@ -7,5 +7,6 @@ public interface IAdminPagesProvider {
|
||||
internal void RegisterAdminPage(string url, AdminPage page);
|
||||
AdminPage LoadAdminPage(string url);
|
||||
IList<AdminPage> LoadRegisteredAdminPages();
|
||||
AdminPage HasPageFor(Type type);
|
||||
|
||||
}
|
||||
@@ -16,4 +16,11 @@ public class AdminPagesProvider : IAdminPagesProvider {
|
||||
public IList<AdminPage> LoadRegisteredAdminPages() {
|
||||
return _pages.Values.ToList();
|
||||
}
|
||||
|
||||
public AdminPage HasPageFor(Type type) {
|
||||
return _pages
|
||||
.Where(p => p.Value.ModelType == type)
|
||||
.Select(p => p.Value)
|
||||
.SingleOrDefault();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
<div class="mb-3">
|
||||
@if (IsListType(prop)) {
|
||||
<BSLabel>@prop.DisplayName</BSLabel>
|
||||
<BSListGroup>
|
||||
<BSListGroupItem>
|
||||
<BSListGroup IsFlush="true">
|
||||
@foreach (var element in GetListPropertyValues(prop).Select((e, i) => new {e, i})) {
|
||||
<BSListGroupItem>
|
||||
<BSButton Color="BSColor.Danger" Size="Size.ExtraSmall" MarginEnd="Margins.Small" OnClick="() => DeleteListItem(prop, element.i)">
|
||||
<HopIconDisplay Type="HopIconDisplay.HopIcon.Cross"/>
|
||||
</BSButton>
|
||||
|
||||
<span>@element.e</span>
|
||||
</BSListGroupItem>
|
||||
}
|
||||
</BSListGroup>
|
||||
</BSListGroupItem>
|
||||
<BSListGroupItem>
|
||||
<div style="display: flex; gap: 20px">
|
||||
@if (prop.SelectorType is null) {
|
||||
<input type="text" class="form-control" @onchange="v => _inputValues[prop] = (string)v.Value"/>
|
||||
<BSButton Color="BSColor.Secondary">Add</BSButton>
|
||||
}
|
||||
else {
|
||||
@*<BSInput InputType="InputType.Select"> TODO: implement selector
|
||||
<option selected>Select group</option>
|
||||
|
||||
@foreach (var group in _allGroups) {
|
||||
@if (_group.Permissions.All(g => g.PermissionName != group.Name) && group.Name != _group.Name) {
|
||||
<option value="@group.Name">@group.Name.Replace("group.", "")</option>
|
||||
}
|
||||
}
|
||||
</BSInput>
|
||||
<BSButton Color="BSColor.Secondary">Add</BSButton>*@
|
||||
}
|
||||
</div>
|
||||
</BSListGroupItem>
|
||||
</BSListGroup>
|
||||
}
|
||||
else if (IsSwitch(prop)) {
|
||||
<div class="form-check form-switch">
|
||||
<BSLabel>@prop.DisplayName</BSLabel>
|
||||
<input class="form-check-input" type="checkbox" checked="@_values[prop]" @onchange="e => _values[prop] = Convert.ToBoolean(e.Value)">
|
||||
</div>
|
||||
}
|
||||
else {
|
||||
<BSLabel>@prop.DisplayName</BSLabel>
|
||||
<input type="@GetInputType(prop)" class="form-control" disabled="@IsDisabled(prop)" required="@IsRequired(prop)" value="@GetPropertyValue(prop)" @onchange="e => _values[prop] = e.Value"/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</BSModalContent>
|
||||
@@ -38,6 +87,7 @@
|
||||
</BSModal>
|
||||
|
||||
@inject IServiceProvider Provider
|
||||
@inject IAdminPagesProvider PageProvider
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
@@ -51,9 +101,11 @@
|
||||
private AdminPage _currentPage;
|
||||
private object _entry;
|
||||
private bool _isEdit;
|
||||
private IDictionary<AdminPageProperty, string> _inputValues;
|
||||
|
||||
public async Task Show(AdminPage page, object entryToEdit = null) {
|
||||
_entry = null;
|
||||
_inputValues = new Dictionary<AdminPageProperty, string>();
|
||||
|
||||
_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<string> GetListPropertyValues(AdminPageProperty prop) {
|
||||
if (!IsListType(prop)) return new List<string>();
|
||||
var list = new List<string>();
|
||||
|
||||
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<IList>(_entry);
|
||||
list.RemoveAt(index);
|
||||
}
|
||||
|
||||
private async void Save() {
|
||||
foreach (var value in _values) {
|
||||
value.Key.SetValue(_entry, value.Value);
|
||||
|
||||
@@ -29,7 +29,8 @@ public class HopAdminContext : AdminPagesContext {
|
||||
.Editable(false);
|
||||
|
||||
generator.Page<User>().Property(u => u.Permissions)
|
||||
.DisplayInListing(false);
|
||||
.DisplayInListing(false)
|
||||
.IsSelector<PermissionGroup>();
|
||||
|
||||
generator.Page<User>().Property(u => u.Tokens)
|
||||
.Ignore();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user