mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-27 06:01:06 -04:00
Nest records gain a materialGrade field (server schema v8, blank for existing rows) that is searched, sorted and versioned like the other metadata. The Nest Info dialog gets a grade picker listing the machine configs' grades for the chosen material; density survives only an unchanged name and grade. A blank recorded grade keeps the archive's grade while the name matches, so records saved before v8 do not lose it on open. Saved Nests shows a Grade column. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
51 lines
2.1 KiB
C#
51 lines
2.1 KiB
C#
namespace OpenNest.Data;
|
|
|
|
/// <summary>
|
|
/// One bounded page request for browsing saved nests (<c>GET /api/nests/query</c>).
|
|
/// The server filters and pages in SQL; the client and server share these bounds.
|
|
/// </summary>
|
|
public sealed class NestQuery
|
|
{
|
|
public const int DefaultLimit = 100;
|
|
public const int MaxLimit = 500;
|
|
public const int MaxSearchLength = 200;
|
|
|
|
/// <summary>
|
|
/// Case-insensitive substring matched against name, customer, material, grade, made by,
|
|
/// comments and status (stored and display names). Blank means no filter.
|
|
/// </summary>
|
|
public string Search { get; init; } = "";
|
|
|
|
/// <summary>Column to order by; ties are broken by id in the same direction.</summary>
|
|
public NestSortField Sort { get; init; } = NestSortField.SavedAt;
|
|
|
|
/// <summary>Largest/newest/Z first when true (the default, newest saved first).</summary>
|
|
public bool Descending { get; init; } = true;
|
|
|
|
/// <summary>Number of matching records to skip, zero or greater.</summary>
|
|
public int Offset { get; init; }
|
|
|
|
/// <summary>Maximum records returned, 1 to <see cref="MaxLimit"/>.</summary>
|
|
public int Limit { get; init; } = DefaultLimit;
|
|
|
|
/// <summary>The search text as matched: trimmed, never null.</summary>
|
|
public string NormalizedSearch => (Search ?? "").Trim();
|
|
|
|
/// <summary>The first violated bound, or null when the query may be sent.</summary>
|
|
public string? GetValidationError()
|
|
{
|
|
if (!Enum.IsDefined(Sort))
|
|
return "sort must be a supported column.";
|
|
if (Offset < 0)
|
|
return "offset must be zero or greater.";
|
|
if (Limit < 1 || Limit > MaxLimit)
|
|
return $"limit must be between 1 and {MaxLimit}.";
|
|
if (NormalizedSearch.Length > MaxSearchLength)
|
|
return $"search must be at most {MaxSearchLength} characters.";
|
|
// SQLite LIKE stops at an embedded NUL, which would turn the rest of the text into a wildcard.
|
|
if (NormalizedSearch.Contains('\0'))
|
|
return "search must not contain NUL characters.";
|
|
return null;
|
|
}
|
|
}
|