Started working on listing page

This commit is contained in:
2025-01-14 21:12:09 +01:00
parent 6115dcf8e1
commit c4c0424559
12 changed files with 396 additions and 57 deletions

View File

@@ -0,0 +1,36 @@
using System.Reflection;
namespace HopFrame.Core.Config;
public class PropertyConfig(PropertyInfo info) {
public PropertyInfo Info { get; init; } = info;
public string Name { get; set; } = info.Name;
public bool List { get; set; } = true;
public bool Sortable { get; set; } = true;
public bool Searchable { get; set; } = true;
}
public class PropertyConfig<TProp>(PropertyConfig config) {
public PropertyConfig<TProp> SetDisplayName(string displayName) {
config.Name = displayName;
return this;
}
public PropertyConfig<TProp> List(bool display) {
config.List = display;
config.Searchable = false;
return this;
}
public PropertyConfig<TProp> Sortable(bool sortable) {
config.Sortable = sortable;
return this;
}
public PropertyConfig<TProp> Searchable(bool searchable) {
config.Searchable = searchable;
return this;
}
}

View File

@@ -1,10 +1,25 @@
namespace HopFrame.Core.Config;
using System.Linq.Expressions;
using System.Reflection;
public class TableConfig(DbContextConfig config, Type tableType, string propertyName) {
public Type TableType { get; } = tableType;
public string PropertyName { get; } = propertyName;
public DbContextConfig ContextConfig { get; } = config;
namespace HopFrame.Core.Config;
public class TableConfig {
public Type TableType { get; }
public string PropertyName { get; }
public DbContextConfig ContextConfig { get; }
public bool Ignored { get; set; }
public List<PropertyConfig> Properties { get; } = new();
public TableConfig(DbContextConfig config, Type tableType, string propertyName) {
TableType = tableType;
PropertyName = propertyName;
ContextConfig = config;
foreach (var info in tableType.GetProperties()) {
Properties.Add(new PropertyConfig(info));
}
}
}
public class TableConfig<TModel>(TableConfig innerConfig) {
@@ -13,5 +28,40 @@ public class TableConfig<TModel>(TableConfig innerConfig) {
innerConfig.Ignored = true;
return this;
}
public PropertyConfig<TProp> Property<TProp>(Expression<Func<TModel, TProp>> propertyExpression) {
var info = GetPropertyInfo(propertyExpression);
var prop = innerConfig.Properties
.Single(prop => prop.Info.Name == info.Name);
return new PropertyConfig<TProp>(prop);
}
public TableConfig<TModel> Property<TProp>(Expression<Func<TModel, TProp>> propertyExpression, Action<PropertyConfig<TProp>> configurator) {
var prop = Property(propertyExpression);
configurator.Invoke(prop);
return this;
}
private static PropertyInfo GetPropertyInfo<TSource, TProperty>(Expression<Func<TSource, TProperty>> propertyLambda) {
if (propertyLambda.Body is not MemberExpression member) {
throw new ArgumentException($"Expression '{propertyLambda}' refers to a method, not a property.");
}
if (member.Member is not PropertyInfo propInfo) {
throw new ArgumentException($"Expression '{propertyLambda}' refers to a field, not a property.");
}
Type type = typeof(TSource);
if (propInfo.ReflectedType != null && type != propInfo.ReflectedType &&
!type.IsSubclassOf(propInfo.ReflectedType)) {
throw new ArgumentException($"Expression '{propertyLambda}' refers to a property that is not from type {type}.");
}
if (propInfo.Name is null)
throw new ArgumentException($"Expression '{propertyLambda}' refers a not existing property.");
return propInfo;
}
}

View File

@@ -1,5 +1,8 @@
namespace HopFrame.Core.Services;
public interface ITableManager {
public Task<IEnumerable<object>> LoadPage(int page, int perPage = 25);
public IQueryable<object> LoadPage(int page, int perPage = 20);
public (IEnumerable<object>, int) Search(string searchTerm, int page = 0, int perPage = 20);
public int TotalPages(int perPage = 20);
public Task DeleteItem(object item);
}

View File

@@ -15,7 +15,7 @@ internal sealed class ContextExplorer(HopFrameConfig config, IServiceProvider pr
public TableConfig? GetTable(string tableName) {
foreach (var context in config.Contexts) {
var table = context.Tables.FirstOrDefault(table => table.PropertyName == tableName);
var table = context.Tables.FirstOrDefault(table => table.PropertyName.Equals(tableName, StringComparison.CurrentCultureIgnoreCase));
if (table is not null)
return table;
}
@@ -32,7 +32,7 @@ internal sealed class ContextExplorer(HopFrameConfig config, IServiceProvider pr
if (dbContext is null) return null;
var type = typeof(TableManager<>).MakeGenericType(table.TableType);
return Activator.CreateInstance(type, dbContext) as ITableManager;
return Activator.CreateInstance(type, dbContext, table) as ITableManager;
}
return null;

View File

@@ -1,15 +1,49 @@
using Microsoft.EntityFrameworkCore;
using HopFrame.Core.Config;
using Microsoft.EntityFrameworkCore;
namespace HopFrame.Core.Services.Implementations;
internal sealed class TableManager<TModel>(DbContext context) : ITableManager where TModel : class {
internal sealed class TableManager<TModel>(DbContext context, TableConfig config) : ITableManager where TModel : class {
public async Task<IEnumerable<object>> LoadPage(int page, int perPage = 25) {
public IQueryable<object> LoadPage(int page, int perPage = 20) {
var table = context.Set<TModel>();
return await table
return table
.Skip(page * perPage)
.Take(perPage)
.ToArrayAsync();
.Take(perPage);
}
public (IEnumerable<object>, int) Search(string searchTerm, int page = 0, int perPage = 20) {
var table = context.Set<TModel>();
var all = table
.AsEnumerable()
.Where(item => ItemSearched(item, searchTerm))
.ToList();
return (all.Skip(page * perPage).Take(perPage), (int)Math.Ceiling(all.Count / (double)perPage));
}
public int TotalPages(int perPage = 20) {
var table = context.Set<TModel>();
return (int)Math.Ceiling(table.Count() / (double)perPage);
}
public async Task DeleteItem(object item) {
var table = context.Set<TModel>();
table.Remove((item as TModel)!);
await context.SaveChangesAsync();
}
private bool ItemSearched(TModel item, string searchTerm) {
foreach (var property in config.Properties) {
if (!property.Searchable) continue;
var value = property.Info.GetValue(item);
if (value is null) continue;
var strValue = value.ToString();
if (strValue?.Contains(searchTerm) == true)
return true;
}
return false;
}
}