Files
MilkyShots/MilkStream.Client/Services/PersonService.cs
REDCODE 445b030160 feat(cosplayer-detail): add SortFilterBar for album search and sort
- 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
2026-07-12 20:11:06 +02:00

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();
}
}