diff --git a/GerbilManagerWebAPI/Common/PagedResult.cs b/GerbilManagerWebAPI/Common/PagedResult.cs
index 3194e7d..5a26a9b 100644
--- a/GerbilManagerWebAPI/Common/PagedResult.cs
+++ b/GerbilManagerWebAPI/Common/PagedResult.cs
@@ -6,22 +6,49 @@ namespace GerbilManagerWebAPI.Common
/// Standard paged list envelope returned by every list endpoint.
public record PagedResult(IReadOnlyList Items, int TotalCount, int Page, int PageSize);
+ ///
+ /// Query-string parameters for list endpoints. All optional (nullable) so the
+ /// frontend can call a list with no params; Gridify's own GridifyQuery has
+ /// non-nullable int Page/PageSize which [AsParameters] would make REQUIRED.
+ /// Bound via [AsParameters]: ?filter=…&orderBy=…&page=1&pageSize=20.
+ ///
+ public class GridifyParams
+ {
+ public string? Filter { get; set; }
+ public string? OrderBy { get; set; }
+ public int? Page { get; set; }
+ public int? PageSize { get; set; }
+
+ public GridifyQuery ToQuery() => new()
+ {
+ Filter = NormalizeFilter(Filter),
+ OrderBy = OrderBy,
+ Page = Page is > 0 ? Page.Value : 1,
+ PageSize = PageSize is > 0 ? PageSize.Value : 20,
+ };
+
+ // The frontend's gridify.ts emits "==" for equals (its documented convention),
+ // but Gridify's equals operator is a single "=". Translate "==" -> "=". This is
+ // safe because the frontend backslash-escapes any "=" inside values, and the
+ // other operators it uses ( != >= <= =* ) contain no literal "==".
+ private static string? NormalizeFilter(string? filter) =>
+ string.IsNullOrEmpty(filter) ? filter : filter.Replace("==", "=");
+ }
+
public static class QueryableExtensions
{
///
/// Apply a Gridify query (filter/order/page) to an EF query and project each
/// row to a DTO, returning the standard paged envelope. Filter/orderBy names
/// are the ENTITY property names (case-insensitive), e.g. "status==Active",
- /// "orderBy=dateOfBirth", "litterId==...".
+ /// "orderBy=dateOfBirth", "litterId==…".
///
public static async Task> ToPagedResultAsync(
this IQueryable source,
- GridifyQuery query,
+ GridifyParams parameters,
Func map)
{
- query.Page = query.Page <= 0 ? 1 : query.Page;
- query.PageSize = query.PageSize <= 0 ? 20 : query.PageSize;
-
+ var query = parameters.ToQuery();
Paging paging = await source.GridifyAsync(query);
var items = paging.Data.Select(map).ToList();
return new PagedResult(items, paging.Count, query.Page, query.PageSize);
diff --git a/GerbilManagerWebAPI/Endpoints/ColorVarietyEndpoints.cs b/GerbilManagerWebAPI/Endpoints/ColorVarietyEndpoints.cs
index 630b345..7f660a8 100644
--- a/GerbilManagerWebAPI/Endpoints/ColorVarietyEndpoints.cs
+++ b/GerbilManagerWebAPI/Endpoints/ColorVarietyEndpoints.cs
@@ -13,7 +13,7 @@ namespace GerbilManagerWebAPI.Endpoints
{
var group = app.MapGroup("/color-varieties").WithTags("ColorVarieties");
- group.MapGet("/", async ([AsParameters] GridifyQuery query, ApplicationContext db) =>
+ group.MapGet("/", async ([AsParameters] GridifyParams query, ApplicationContext db) =>
TypedResults.Ok(await db.ColorVarieties.AsNoTracking().OrderBy(v => v.SortOrder)
.ToPagedResultAsync(query, ToDto)));
diff --git a/GerbilManagerWebAPI/Endpoints/ContactEndpoints.cs b/GerbilManagerWebAPI/Endpoints/ContactEndpoints.cs
index bdef185..6091e45 100644
--- a/GerbilManagerWebAPI/Endpoints/ContactEndpoints.cs
+++ b/GerbilManagerWebAPI/Endpoints/ContactEndpoints.cs
@@ -13,7 +13,7 @@ namespace GerbilManagerWebAPI.Endpoints
{
var group = app.MapGroup("/contacts").WithTags("Contacts");
- group.MapGet("/", async ([AsParameters] GridifyQuery query, ApplicationContext db) =>
+ group.MapGet("/", async ([AsParameters] GridifyParams query, ApplicationContext db) =>
TypedResults.Ok(await db.Contacts.AsNoTracking().ToPagedResultAsync(query, ToDto)));
group.MapGet("/{id:guid}", async Task, NotFound>> (Guid id, ApplicationContext db) =>
diff --git a/GerbilManagerWebAPI/Endpoints/EnclosureEndpoints.cs b/GerbilManagerWebAPI/Endpoints/EnclosureEndpoints.cs
index 9b15b2d..47f79c7 100644
--- a/GerbilManagerWebAPI/Endpoints/EnclosureEndpoints.cs
+++ b/GerbilManagerWebAPI/Endpoints/EnclosureEndpoints.cs
@@ -13,7 +13,7 @@ namespace GerbilManagerWebAPI.Endpoints
{
var group = app.MapGroup("/enclosures").WithTags("Enclosures");
- group.MapGet("/", async ([AsParameters] GridifyQuery query, ApplicationContext db) =>
+ group.MapGet("/", async ([AsParameters] GridifyParams query, ApplicationContext db) =>
TypedResults.Ok(await db.Enclosures.AsNoTracking().ToPagedResultAsync(query, ToDto)));
group.MapGet("/{id:guid}", async Task, NotFound>> (Guid id, ApplicationContext db) =>
diff --git a/GerbilManagerWebAPI/Endpoints/GerbilEndpoints.cs b/GerbilManagerWebAPI/Endpoints/GerbilEndpoints.cs
index f895266..b96e30f 100644
--- a/GerbilManagerWebAPI/Endpoints/GerbilEndpoints.cs
+++ b/GerbilManagerWebAPI/Endpoints/GerbilEndpoints.cs
@@ -15,7 +15,7 @@ namespace GerbilManagerWebAPI.Endpoints
var group = app.MapGroup("/gerbils").WithTags("Gerbils");
// GET /gerbils (Gridify: filter/order/page; e.g. status==Active, litterId==…, orderBy=name)
- group.MapGet("/", async ([AsParameters] GridifyQuery query, ApplicationContext db) =>
+ group.MapGet("/", async ([AsParameters] GridifyParams query, ApplicationContext db) =>
TypedResults.Ok(await db.Gerbils.AsNoTracking()
.ToPagedResultAsync(query, ToDto)));
diff --git a/GerbilManagerWebAPI/Endpoints/LitterEndpoints.cs b/GerbilManagerWebAPI/Endpoints/LitterEndpoints.cs
index a66b1f1..e6350f5 100644
--- a/GerbilManagerWebAPI/Endpoints/LitterEndpoints.cs
+++ b/GerbilManagerWebAPI/Endpoints/LitterEndpoints.cs
@@ -14,7 +14,7 @@ namespace GerbilManagerWebAPI.Endpoints
var group = app.MapGroup("/litters").WithTags("Litters");
// GET /litters (Gridify: date range + orderBy=date supported on the DateOnly column)
- group.MapGet("/", async ([AsParameters] GridifyQuery query, ApplicationContext db) =>
+ group.MapGet("/", async ([AsParameters] GridifyParams query, ApplicationContext db) =>
TypedResults.Ok(await db.Litters.AsNoTracking().ToPagedResultAsync(query, ToDto)));
group.MapGet("/{id:guid}", async Task, NotFound>> (Guid id, ApplicationContext db) =>
diff --git a/GerbilManagerWebAPI/Endpoints/RecordEndpoints.cs b/GerbilManagerWebAPI/Endpoints/RecordEndpoints.cs
index 5515573..1bffd35 100644
--- a/GerbilManagerWebAPI/Endpoints/RecordEndpoints.cs
+++ b/GerbilManagerWebAPI/Endpoints/RecordEndpoints.cs
@@ -14,7 +14,7 @@ namespace GerbilManagerWebAPI.Endpoints
var group = app.MapGroup("/health-records").WithTags("HealthRecords");
// GET /health-records?filter=gerbilId==…
- group.MapGet("/", async ([AsParameters] GridifyQuery query, ApplicationContext db) =>
+ group.MapGet("/", async ([AsParameters] GridifyParams query, ApplicationContext db) =>
TypedResults.Ok(await db.HealthRecords.AsNoTracking().ToPagedResultAsync(query, ToDto)));
group.MapGet("/{id:guid}", async Task, NotFound>> (Guid id, ApplicationContext db) =>
@@ -66,7 +66,7 @@ namespace GerbilManagerWebAPI.Endpoints
{
var group = app.MapGroup("/weight-records").WithTags("WeightRecords");
- group.MapGet("/", async ([AsParameters] GridifyQuery query, ApplicationContext db) =>
+ group.MapGet("/", async ([AsParameters] GridifyParams query, ApplicationContext db) =>
TypedResults.Ok(await db.WeightRecords.AsNoTracking().ToPagedResultAsync(query, ToDto)));
group.MapGet("/{id:guid}", async Task, NotFound>> (Guid id, ApplicationContext db) =>