Rated people panel added

This commit is contained in:
2024-11-01 20:44:01 +01:00
Unverified
parent 226b2b619c
commit 1154cc547b
28 changed files with 479 additions and 59 deletions

View File

@@ -1,5 +1,6 @@
using WatchIt.Common.Model.Accounts;
using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Persons;
using WatchIt.Common.Model.Series;
using WatchIt.Common.Services.HttpClient;
using WatchIt.Website.Services.Configuration;
@@ -135,6 +136,20 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig
.ExecuteAction();
}
public async Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query, Action<IEnumerable<PersonRatedResponse>>? successAction = null, Action? notFoundAction = null)
{
string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountRatedPersons, id);
HttpRequest request = new HttpRequest(HttpMethodType.Get, url)
{
Query = query
};
HttpResponse response = await httpClientService.SendRequestAsync(request);
response.RegisterActionFor2XXSuccess(successAction)
.RegisterActionFor404NotFound(notFoundAction)
.ExecuteAction();
}
#endregion

View File

@@ -1,5 +1,6 @@
using WatchIt.Common.Model.Accounts;
using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Persons;
using WatchIt.Common.Model.Series;
namespace WatchIt.Website.Services.Client.Accounts;
@@ -15,4 +16,5 @@ public interface IAccountsClientService
Task PutAccountInfo(AccountRequest data, Action<AccountResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null);
Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action<IEnumerable<MovieRatedResponse>>? successAction = null, Action? notFoundAction = null);
Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query, Action<IEnumerable<SeriesRatedResponse>>? successAction = null, Action? notFoundAction = null);
Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query, Action<IEnumerable<PersonRatedResponse>>? successAction = null, Action? notFoundAction = null);
}

View File

@@ -12,4 +12,5 @@ public class Accounts
public string PutAccountInfo { get; set; }
public string GetAccountRatedMovies { get; set; }
public string GetAccountRatedSeries { get; set; }
public string GetAccountRatedPersons { get; set; }
}

View File

@@ -60,7 +60,8 @@
PosterPlaceholder="@(PosterPlaceholder)"
PosterDownloadingTask="@(action => PictureDownloadingTask(id, action))"
GlobalRating="@(RatingSource(item))"
SecondaryRating="@(SecondaryRatingSource?.Invoke(item))"
SecondaryRatingSingle="@(SecondaryRatingSingleSource?.Invoke(item))"
SecondaryRatingMultiple="@(SecondaryRatingMultipleSource?.Invoke(item))"
SecondaryRatingTitle="@(SecondaryRatingTitle)"
GetGlobalRatingMethod="@(action => GetGlobalRatingMethod(id, action))"
GetUserRatingMethod="@(GetUserRatingMethod is not null ? (user, actionSuccess, actionNotFound) => GetUserRatingMethod(id, user, actionSuccess, actionNotFound) : null)"

View File

@@ -23,7 +23,8 @@ public partial class ListComponent<TItem, TQuery> : ComponentBase where TItem :
[Parameter] public required Func<TItem, string> NameSource { get; set; }
[Parameter] public Func<TItem, string?> AdditionalNameInfoSource { get; set; } = _ => null;
[Parameter] public required Func<TItem, RatingResponse> RatingSource { get; set; }
[Parameter] public Func<TItem, short?>? SecondaryRatingSource { get; set; }
[Parameter] public Func<TItem, short?>? SecondaryRatingSingleSource { get; set; }
[Parameter] public Func<TItem, RatingResponse?>? SecondaryRatingMultipleSource { get; set; }
[Parameter] public string? SecondaryRatingTitle { get; set; }
[Parameter] public required string UrlIdTemplate { get; set; }
[Parameter] public required Func<long, Action<Picture>, Task> PictureDownloadingTask { get; set; }

View File

@@ -18,7 +18,7 @@
<th scope="col">
<span class="rating-name-text">Global rating:</span>
</th>
@if (SecondaryRating is not null)
@if (SecondaryRatingSingle is not null || SecondaryRatingMultiple is not null)
{
<th scope="col">
<span class="rating-name-text">@(SecondaryRatingTitle):</span>
@@ -39,10 +39,11 @@
EmptyMode="DisplayRatingComponent.DisplayRatingComponentEmptyMode.DoubleDash"
Scale="0.85"/>
</td>
@if (SecondaryRating is not null)
@if (SecondaryRatingSingle is not null || SecondaryRatingMultiple is not null)
{
<td>
<DisplayRatingComponent SingleRating="@(SecondaryRating)"
<DisplayRatingComponent SingleRating="@(SecondaryRatingSingle)"
Rating="@(SecondaryRatingMultiple)"
EmptyMode="DisplayRatingComponent.DisplayRatingComponentEmptyMode.DoubleDash"
Scale="0.85"/>
</td>
@@ -53,18 +54,18 @@
<div class="d-inline-flex align-items-center h-100">
@if (_user is null)
{
<span id="ratingLoginInfoText">You must be logged in to rate</span>
<span id="ratingLoginInfoText">You must be logged in to rate</span>
}
else if (!_userRatingLoaded)
{
<div class="d-flex align-items-center gap-2">
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
<span>Loading...</span>
</div>
<div class="d-flex align-items-center gap-2">
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
<span>Loading...</span>
</div>
}
else
{
<Rating Color="Color.Light" MaxValue="10" @bind-SelectedValue="@(_userRating)" @onclick="@(RatingChanged)"/>
<Rating Color="Color.Light" MaxValue="10" @bind-SelectedValue="@(_userRating)" @onclick="@(RatingChanged)"/>
}
</div>
</td>

View File

@@ -27,7 +27,8 @@ public partial class ListItemComponent : ComponentBase
[Parameter] public required Func<Action<Picture>, Task> PosterDownloadingTask { get; set; }
[Parameter] public RatingResponse? GlobalRating { get; set; }
[Parameter] public short? SecondaryRating { get; set; }
[Parameter] public short? SecondaryRatingSingle { get; set; }
[Parameter] public RatingResponse? SecondaryRatingMultiple { get; set; }
[Parameter] public string? SecondaryRatingTitle { get; set; }
[Parameter] public required Func<Action<RatingResponse>, Task> GetGlobalRatingMethod { get; set; }
[Parameter] public Func<long, Action<short>, Action, Task>? GetUserRatingMethod { get; set; }

View File

@@ -0,0 +1,88 @@
@using WatchIt.Common.Model.Genders
@inherits WatchIt.Website.Components.Common.ListComponent.FilterFormComponent<WatchIt.Common.Model.Persons.PersonRatedResponse, WatchIt.Common.Model.Persons.PersonRatedQueryParameters>
<EditForm Model="@(Query)">
<div class="container-grid">
<div class="row mb-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Name</span>
<InputText class="col form-control" placeholder="Search with regex" @bind-Value="@(Query.Name)"></InputText>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Full name</span>
<InputText class="col form-control" placeholder="Search with regex" @bind-Value="@(Query.FullName)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Description</span>
<InputText class="col form-control" placeholder="Search with regex" @bind-Value="@(Query.Description)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Birth date</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.BirthDateFrom)"/>
<span class="col-auto input-group-text">-</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.BirthDateTo)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Death date</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.DeathDateFrom)"/>
<span class="col-auto input-group-text">-</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.DeathDateTo)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Gender</span>
<InputSelect TValue="short?" class="col form-control" @bind-Value="@(Query.GenderId)">
<option @onclick="() => Query.GenderId = null">No choice</option>
@foreach (GenderResponse gender in _genders)
{
<option value="@(gender.Id)">@(gender.Name)</option>
}
</InputSelect>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Rating (count)</span>
<NumericEdit TValue="long?" Class="col form-control" Min="0" @bind-Value="@(Query.RatingCountFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="long?" Class="col form-control" Min="0" @bind-Value="@(Query.RatingCountTo)"/>
</div>
</div>
<div class="row mt-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Rating (average)</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" Max="10" Step="@(0.01M)" @bind-Value="@(Query.RatingAverageFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" Max="10" Step="@(0.01M)" @bind-Value="@(Query.RatingAverageTo)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">User rating (count)</span>
<NumericEdit TValue="long?" Class="col form-control" Min="0" @bind-Value="@(Query.UserRatingCountFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="long?" Class="col form-control" Min="0" @bind-Value="@(Query.UserRatingCountTo)"/>
</div>
</div>
<div class="row mt-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">User rating (average)</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" Max="10" Step="@(0.01M)" @bind-Value="@(Query.UserRatingAverageFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" Max="10" Step="@(0.01M)" @bind-Value="@(Query.UserRatingAverageTo)"/>
</div>
</div>
</div>
</EditForm>

View File

@@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Components;
using WatchIt.Common.Model.Genders;
using WatchIt.Common.Model.Persons;
using WatchIt.Website.Components.Common.ListComponent;
using WatchIt.Website.Services.Client.Genders;
namespace WatchIt.Website.Components.Pages.UserPage.Subcomponents;
public partial class PersonsRatedFilterFormComponent : FilterFormComponent<PersonRatedResponse, PersonRatedQueryParameters>
{
#region SERVICES
[Inject] private IGendersClientService GendersClientService { get; set; } = default!;
#endregion
#region FIELDS
private IEnumerable<GenderResponse> _genders = [];
#endregion
#region PRIVATE METHODS
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
List<Task> endTasks = new List<Task>();
// STEP 0
endTasks.AddRange(
[
GendersClientService.GetAllGenders(successAction: data => _genders = data)
]);
// END
await Task.WhenAll(endTasks);
StateHasChanged();
}
}
#endregion
}

View File

@@ -1,9 +1,11 @@
@using System.Text
@using WatchIt.Common.Model.Movies
@using WatchIt.Common.Model.Persons
@using WatchIt.Common.Model.Series
@using WatchIt.Website.Components.Pages.UserPage.Panels
@using WatchIt.Website.Components.Common.ListComponent
@using WatchIt.Website.Components.Pages.UserPage.Subcomponents
@using WatchIt.Website.Services.Client.Persons
@page "/user/{id:long?}"
@@ -60,7 +62,6 @@
<Tab Name="movies">Movies</Tab>
<Tab Name="series">TV Series</Tab>
<Tab Name="people">People</Tab>
<Tab Name="roles">Roles</Tab>
</Items>
<Content>
<TabPanel Name="summary">
@@ -75,7 +76,7 @@
NameSource="@(item => item.Title)"
AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)"
RatingSource="@(item => item.Rating)"
SecondaryRatingSource="@(item => _owner ? null : item.UserRating)"
SecondaryRatingSingleSource="@(item => _owner ? null : item.UserRating)"
SecondaryRatingTitle="User rating"
UrlIdTemplate="/media/{0}"
PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))"
@@ -99,7 +100,7 @@
NameSource="@(item => item.Title)"
AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)"
RatingSource="@(item => item.Rating)"
SecondaryRatingSource="@(item => _owner ? null : item.UserRating)"
SecondaryRatingSingleSource="@(item => _owner ? null : item.UserRating)"
SecondaryRatingTitle="User rating"
UrlIdTemplate="/media/{0}"
PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))"
@@ -115,10 +116,33 @@
</div>
</TabPanel>
<TabPanel Name="people">
</TabPanel>
<TabPanel Name="roles">
<div class="mt-default">
<ListComponent TItem="PersonRatedResponse"
TQuery="PersonRatedQueryParameters"
Title="Rated people"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Name)"
RatingSource="@(item => item.Rating)"
SecondaryRatingMultipleSource="@(item => item.UserRating)"
SecondaryRatingTitle="User rating"
UrlIdTemplate="/media/{0}"
PictureDownloadingTask="@((id, action) => PersonsClientService.GetPersonPhoto(id, action))"
ItemDownloadingTask="@((query, action) => AccountsClientService.GetAccountRatedPersons(Id!.Value, query, action))"
SortingOptions="@(new Dictionary<string, string>
{
{ "user_rating.average", "Average user rating" },
{ "user_rating.count", "Number of user ratings" },
{ "rating.average", "Average rating" },
{ "rating.count", "Number of ratings" },
{ "name", "Name" },
{ "birth_date", "Birth date" },
{ "death_date", "Death date" },
})"
PosterPlaceholder="/assets/person_poster.png"
GetGlobalRatingMethod="@((id, action) => PersonsClientService.GetPersonGlobalRating(id, action))">
<PersonsRatedFilterFormComponent/>
</ListComponent>
</div>
</TabPanel>
</Content>
</Tabs>

View File

@@ -4,6 +4,7 @@ using WatchIt.Website.Layout;
using WatchIt.Website.Services.Authentication;
using WatchIt.Website.Services.Client.Accounts;
using WatchIt.Website.Services.Client.Media;
using WatchIt.Website.Services.Client.Persons;
namespace WatchIt.Website.Pages;
@@ -15,6 +16,7 @@ public partial class UserPage : ComponentBase
[Inject] private IAuthenticationService AuthenticationService { get; set; } = default!;
[Inject] private IAccountsClientService AccountsClientService { get; set; } = default!;
[Inject] private IMediaClientService MediaClientService { get; set; } = default!;
[Inject] private IPersonsClientService PersonsClientService { get; set; } = default!;
#endregion

View File

@@ -25,7 +25,8 @@
"GetAccountInfo": "/{0}/info",
"PutAccountInfo": "/info",
"GetAccountRatedMovies": "/{0}/movies",
"GetAccountRatedSeries": "/{0}/series"
"GetAccountRatedSeries": "/{0}/series",
"GetAccountRatedPersons": "/{0}/persons"
},
"Genders": {
"Base": "/genders",