- Backend: accept PagedSearchParametersDto on GET /api/person/{id}
with in-memory search (case-insensitive title Contains) and sort
(name/created/updated/assets) before pagination
- Frontend service: add search, sortBy, sortAsc params to GetByIdAsync
- Frontend page: replace inline action buttons with reusable SortFilterBar
component, wire search/sort state into initial load and infinite scroll
112 lines
4.5 KiB
C#
112 lines
4.5 KiB
C#
using Butter.Dtos.Person;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace MilkStream.Client.Services;
|
|
|
|
/// <summary>
|
|
/// Provides access to person data via the API.
|
|
/// </summary>
|
|
public sealed class PersonService(
|
|
IOptions<ServiceOptions> options,
|
|
IHttpClientFactory httpClientFactory,
|
|
LoginService loginService,
|
|
ILogger<PersonService> logger
|
|
) : AuthServiceBase(options, httpClientFactory, loginService, logger) {
|
|
/// <summary>
|
|
/// Gets a paginated list of people with optional search and sort options.
|
|
/// </summary>
|
|
/// <param name="page">The zero-based page number.</param>
|
|
/// <param name="pageSize">The number of items per page.</param>
|
|
/// <param name="search">Optional search term to filter by name.</param>
|
|
/// <param name="sortBy">Optional field to sort by (e.g. "name", "created", "albums").</param>
|
|
/// <param name="sortAsc">Whether to sort ascending. Default is <c>true</c>.</param>
|
|
/// <returns>A list of person previews, or null if the request failed.</returns>
|
|
public async Task<List<PersonPreviewDto>?> GetAllAsync(int page = 0, int pageSize = 30, string? search = null, string? sortBy = null, bool sortAsc = true) {
|
|
var url = $"/api/person?page={page}&pageSize={pageSize}";
|
|
if (!string.IsNullOrEmpty(search))
|
|
url += $"&search={Uri.EscapeDataString(search)}";
|
|
if (!string.IsNullOrEmpty(sortBy))
|
|
url += $"&sortBy={Uri.EscapeDataString(sortBy)}";
|
|
url += $"&sortAsc={sortAsc.ToString().ToLowerInvariant()}";
|
|
|
|
var response = await Client.GetAsync(url);
|
|
|
|
if (response.IsSuccessStatusCode)
|
|
return await response.Content.ReadFromJsonAsync<List<PersonPreviewDto>>();
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a person by ID with albums and stats.
|
|
/// </summary>
|
|
/// <param name="id">The person ID.</param>
|
|
/// <param name="albumPage">Optional zero-based page number for albums.</param>
|
|
/// <param name="albumPageSize">Optional number of albums per page.</param>
|
|
/// <param name="search">Optional search term to filter album titles.</param>
|
|
/// <param name="sortBy">Optional field to sort albums by (e.g. "name", "created", "updated", "assets").</param>
|
|
/// <param name="sortAsc">Whether to sort ascending. Default is <c>true</c>.</param>
|
|
/// <returns>The person details, or null if not found.</returns>
|
|
public async Task<PersonDetailedDto?> GetByIdAsync(Guid id, int? albumPage = null, int? albumPageSize = null, string? search = null, string? sortBy = null, bool sortAsc = true) {
|
|
var url = $"/api/person/{id}";
|
|
var hasParams = false;
|
|
|
|
if (albumPage.HasValue && albumPageSize.HasValue) {
|
|
url += $"?page={albumPage}&pageSize={albumPageSize}";
|
|
hasParams = true;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(search)) {
|
|
url += $"{(hasParams ? "&" : "?")}search={Uri.EscapeDataString(search)}";
|
|
hasParams = true;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(sortBy)) {
|
|
url += $"{(hasParams ? "&" : "?")}sortBy={Uri.EscapeDataString(sortBy)}";
|
|
hasParams = true;
|
|
}
|
|
|
|
url += $"{(hasParams ? "&" : "?")}sortAsc={sortAsc.ToString().ToLowerInvariant()}";
|
|
|
|
var response = await Client.GetAsync(url);
|
|
|
|
if (response.IsSuccessStatusCode)
|
|
return await response.Content.ReadFromJsonAsync<PersonDetailedDto>();
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates a person's name and profile asset.
|
|
/// </summary>
|
|
/// <param name="id">The person ID.</param>
|
|
/// <param name="dto">The updated person data.</param>
|
|
public async Task UpdatePersonAsync(Guid id, PersonUpdateDto dto) {
|
|
var response = await Client.PostAsJsonAsync($"/api/person/{id}", dto);
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new person.
|
|
/// </summary>
|
|
/// <param name="dto">The person creation data.</param>
|
|
/// <returns>The new person's ID, or null if the request failed.</returns>
|
|
public async Task<Guid?> CreatePersonAsync(PersonCreateDto dto) {
|
|
var response = await Client.PutAsJsonAsync("/api/person", dto);
|
|
|
|
if (response.IsSuccessStatusCode)
|
|
return await response.Content.ReadFromJsonAsync<Guid>();
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Soft-deletes a person.
|
|
/// </summary>
|
|
/// <param name="id">The person ID.</param>
|
|
public async Task DeletePersonAsync(Guid id) {
|
|
var response = await Client.DeleteAsync($"/api/person/{id}");
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
}
|