Started working on admin page modal

This commit is contained in:
2024-10-08 21:34:00 +02:00
parent bc0651cb75
commit d2729870e3
7 changed files with 180 additions and 29 deletions

View File

@@ -13,5 +13,6 @@ public interface IAdminPropertyGenerator {
IAdminPropertyGenerator DisplayName(string displayName);
IAdminPropertyGenerator Description(string description);
IAdminPropertyGenerator Prefix(string prefix);
IAdminPropertyGenerator Validator(Func<object, bool> validator);
}

View File

@@ -14,7 +14,8 @@ internal sealed class AdminPageGenerator<TModel> : IAdminPageGenerator<TModel>,
public AdminPageGenerator() {
Page = new AdminPage<TModel> {
Permissions = new AdminPagePermissions()
Permissions = new AdminPagePermissions(),
ModelType = typeof(TModel)
};
_propertyGenerators = new Dictionary<string, AdminPropertyGenerator>();

View File

@@ -65,6 +65,11 @@ internal sealed class AdminPropertyGenerator(string name, Type type) : IAdminPro
return this;
}
public IAdminPropertyGenerator Validator(Func<object, bool> validator) {
_property.Validator = validator;
return this;
}
public AdminPageProperty Compile() {
_property.DisplayName ??= _property.Name;
return _property;
@@ -111,6 +116,10 @@ internal sealed class AdminPropertyGenerator(string name, Type type) : IAdminPro
Bold(attribute?.Bold == true);
}
if (attributes.Any(a => a is RequiredAttribute)) {
_property.Required = true;
}
if (attributes.Any(a => a is AdminPrefixAttribute)) {
var attribute = attributes.Single(a => a is AdminPrefixAttribute) as AdminPrefixAttribute;
Prefix(attribute?.Prefix);

View File

@@ -15,6 +15,8 @@ public class AdminPage {
[JsonIgnore]
public Type RepositoryProvider { get; set; }
public Type ModelType { get; set; }
public string DefaultSortPropertyName { get; set; }
public ListSortDirection DefaultSortDirection { get; set; }

View File

@@ -13,8 +13,23 @@ 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 Bold { get; set; }
public bool Required { get; set; }
public bool Ignore { get; set; }
[JsonIgnore]
public Type Type { get; set; }
public Func<object, bool> Validator { get; set; }
public object GetValue(object entry) {
return entry.GetType().GetProperty(Name)?.GetValue(entry);
}
public T GetValue<T>(object entry) {
return (T)entry.GetType().GetProperty(Name)?.GetValue(entry);
}
public void SetValue(object entry, object value) {
entry.GetType().GetProperty(Name)?.SetValue(entry, value);
}
}

View File

@@ -0,0 +1,119 @@
@rendermode InteractiveServer
@using BlazorStrap
@using BlazorStrap.Shared.Components.Modal
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using BlazorStrap.V5
@using HopFrame.Web.Admin
@using HopFrame.Web.Admin.Models
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Web
<BSModal DataId="admin-page-modal" HideOnValidSubmit="true" IsStaticBackdrop="true" @ref="_modal">
<BSForm TValue="object" EditContext="_context" OnValidSubmit="Save">
@if (!_isEdit) {
<BSModalHeader>Create entry</BSModalHeader>
}
else {
<BSModalHeader>Edit entry</BSModalHeader>
}
<BSModalContent>
@foreach (var prop in GetEditableProperties()) {
@if (!_isEdit && !prop.Editable) continue;
<div class="mb-3">
<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>
<BSModalFooter>
<BSButton Target="admin-page-modal">Cancel</BSButton>
<BSButton IsSubmit="true" Color="BSColor.Primary">Save</BSButton>
</BSModalFooter>
</BSForm>
</BSModal>
@inject IServiceProvider Provider
@code {
[Parameter]
public Func<Task> ReloadDelegate { get; set; }
private BSModalBase _modal;
private EditContext _context;
private IDictionary<AdminPageProperty, object> _values;
private IModelRepository _repository;
private AdminPage _currentPage;
private object _entry;
private bool _isEdit;
public async Task Show(AdminPage page, object entryToEdit = null) {
_entry = null;
_currentPage = page;
_entry = entryToEdit;
_isEdit = entryToEdit is not null;
_repository = Provider.GetService(_currentPage.RepositoryProvider) as IModelRepository;
_entry ??= Activator.CreateInstance(_currentPage.ModelType);
_context = new EditContext(_entry);
_context.OnValidationRequested += Validate;
_values = new Dictionary<AdminPageProperty, object>();
foreach (var property in _currentPage.Properties) {
_values.Add(property, property.GetValue(_entry));
}
await _modal.ShowAsync();
}
private IList<AdminPageProperty> GetEditableProperties() {
return _currentPage.Properties
.Where(p => !p.Ignore)
.OrderBy(p => p.Editable)
.ToList();
}
private bool IsDisabled(AdminPageProperty prop) => !prop.Editable;
private bool IsRequired(AdminPageProperty prop) => !_isEdit ? prop.Required : prop.Required && prop.EditDisplayValue;
private string GetPropertyValue(AdminPageProperty property) {
if (!_isEdit) return "";
if (!property.EditDisplayValue) return "";
return property.GetValue(_entry)?.ToString();
}
private string GetInputType(AdminPageProperty property) {
if (!property.EditDisplayValue)
return "password";
return "text";
}
private void Validate(object sender, ValidationRequestedEventArgs e) {
foreach (var value in _values) {
if (value.Key.Validator?.Invoke(value.Value) == true) continue;
Console.WriteLine("INVALID");
}
}
private async void Save() {
foreach (var value in _values) {
value.Key.SetValue(_entry, value.Value);
}
if (!_isEdit) {
await _repository.CreateO(_entry);
}
else {
await _repository.UpdateO(_entry);
}
await ReloadDelegate.Invoke();
}
}

View File

@@ -20,6 +20,8 @@
<PageTitle>@_pageData.Title</PageTitle>
<AuthorizedView Permission="@_pageData.Permissions.View" RedirectIfUnauthorized="administration/login" />
<AdminPageModal ReloadDelegate="Reload" @ref="_modal"/>
<div class="title">
<h3>
@_pageData.Title administration
@@ -63,10 +65,17 @@
@foreach (var entry in _displayedModels) {
<BSTR>
@foreach (var prop in GetListingProperties()) {
<BSTD Class="@GetClass(prop)">
@GetValue(entry, prop)
@if (prop.Bold) {
<BSTD Class="bold">
@GetPrintableValue(entry, prop)
</BSTD>
}
else {
<BSTD>
@GetPrintableValue(entry, prop)
</BSTD>
}
}
@if (_hasEditPermission || _hasDeletePermission) {
<BSTD>
@@ -105,6 +114,7 @@
private AdminPage _pageData;
private IModelRepository _modelRepository;
private IEnumerable<object> _modelBuffer;
private AdminPageModal _modal;
private bool _hasEditPermission;
private bool _hasDeletePermission;
@@ -114,14 +124,12 @@
private DateTime _lastSearch;
private IList<object> _displayedModels;
protected override void OnInitialized() {
protected override async Task OnInitializedAsync() {
_pageData = Pages.LoadAdminPage(Url);
_currentSortProperty = _pageData.DefaultSortPropertyName;
_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!'");
@@ -129,7 +137,7 @@
_hasEditPermission = await Permissions.HasPermission(Auth.User, _pageData.Permissions.Update);
_hasDeletePermission = await Permissions.HasPermission(Auth.User, _pageData.Permissions.Delete);
Reload();
await Reload();
}
private IList<AdminPageProperty> GetListingProperties() {
@@ -139,7 +147,7 @@
.ToList();
}
private async void Reload() {
private async Task Reload() {
_modelBuffer = await _modelRepository.ReadAllO();
_displayedModels = _modelBuffer.ToList();
@@ -157,15 +165,15 @@
var prop = GetListingProperties()
.SingleOrDefault(p => p.Name == property);
var comparer = Comparer<object>.Create((x, y) => {
if (prop.Type == typeof(DateTime)) {
if (prop?.Type == typeof(DateTime)) {
DateTime dateX = (DateTime) x.GetType().GetProperty(prop.Name)?.GetValue(x)!;
DateTime dateY = (DateTime) y.GetType().GetProperty(prop.Name)?.GetValue(y)!;
return DateTime.Compare(dateX, dateY);
}
var propX = GetValue(x, prop);
var propY = GetValue(y, prop);
var propX = GetPrintableValue(x, prop);
var propY = GetPrintableValue(y, prop);
return String.CompareOrdinal(propX, propY);
});
@@ -198,7 +206,7 @@
var props = GetListingProperties();
_displayedModels = _modelBuffer
.Where(model => props.Any(prop => GetValue(model, prop).Contains(search)))
.Where(model => props.Any(prop => GetPrintableValue(model, prop).Contains(search)))
.ToList();
}
@@ -206,29 +214,25 @@
StateHasChanged();
}
private string GetValue(object entry, AdminPageProperty property) {
object propValue = entry.GetType().GetProperty(property.Name)?.GetValue(entry);
private static string GetPrintableValue(object element, AdminPageProperty prop) {
var entry = prop.GetValue(element);
return propValue?.ToString();
}
//TODO: convert relational types
private string GetClass(AdminPageProperty property) {
if (property.Bold)
return "bold";
return "";
return entry?.ToString();
}
private async void Create() {
//TODO: Open create modal
await _modal.Show(_pageData);
}
private async void Edit(object entry) {
//TODO: Open edit modal
await _modal.Show(_pageData, entry);
}
private async void Delete(object entry) {
var result = await Alerts.FireAsync(new SweetAlertOptions {
Title = "Do you really want to delete this entry?",
Title = "Are you sure?",
Text = "You won't be able to revert this!",
Icon = SweetAlertIcon.Warning,
ConfirmButtonText = "Yes",
@@ -238,7 +242,7 @@
if (result.IsConfirmed) {
await _modelRepository.DeleteO(entry);
Reload();
await Reload();
await Alerts.FireAsync(new SweetAlertOptions {
Title = "Deleted!",