diff --git a/WatchIt.Common/WatchIt.Common.Model/Rating/RatingResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Rating/RatingResponse.cs index 77fed60..9037814 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Rating/RatingResponse.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Rating/RatingResponse.cs @@ -45,6 +45,11 @@ public class RatingResponse { IEnumerable ratingsActorRoles = personActorRoles.SelectMany(x => x.RatingPersonActorRole); IEnumerable ratingsCreatorRoles = personCreatorRoles.SelectMany(x => x.RatingPersonCreatorRole); + return Create(ratingsActorRoles, ratingsCreatorRoles); + } + + public static RatingResponse Create(IEnumerable ratingsActorRoles, IEnumerable ratingsCreatorRoles) + { long ratingSum = ratingsActorRoles.Sum(x => x.Rating) + ratingsCreatorRoles.Sum(x => x.Rating); long ratingCount = ratingsActorRoles.Count() + ratingsCreatorRoles.Count(); return new RatingResponse(ratingSum, ratingCount); diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/PersonsController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/PersonsController.cs index a36a45e..ed213b0 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/PersonsController.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/PersonsController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using WatchIt.Common.Model.Persons; +using WatchIt.Common.Model.Rating; using WatchIt.Common.Model.Roles; using WatchIt.WebAPI.Services.Controllers.Persons; @@ -145,6 +146,22 @@ public class PersonsController : ControllerBase [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task PostPersonCreatorRole([FromRoute]long id, [FromBody]CreatorRolePersonRequest body) => await _personsControllerService.PostPersonCreatorRole(id, body); + #endregion + + #region Rating + + [HttpGet("{id}/rating")] + [AllowAnonymous] + [ProducesResponseType(typeof(RatingResponse), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetPersonGlobalRating([FromRoute]long id) => await _personsControllerService.GetPersonGlobalRating(id); + + [HttpGet("{id}/rating/{user_id}")] + [AllowAnonymous] + [ProducesResponseType(typeof(RatingResponse), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetPersonUserRating([FromRoute]long id, [FromRoute(Name = "user_id")]long userId) => await _personsControllerService.GetPersonUserRating(id, userId); + #endregion #endregion diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Persons/IPersonsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Persons/IPersonsControllerService.cs index b5802ce..76599a3 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Persons/IPersonsControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Persons/IPersonsControllerService.cs @@ -23,4 +23,7 @@ public interface IPersonsControllerService Task PostPersonActorRole(long personId, ActorRolePersonRequest data); Task GetPersonAllCreatorRoles(long personId, CreatorRolePersonQueryParameters queryParameters); Task PostPersonCreatorRole(long personId, CreatorRolePersonRequest data); + + Task GetPersonGlobalRating(long id); + Task GetPersonUserRating(long id, long userId); } \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Persons/PersonsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Persons/PersonsControllerService.cs index daafedf..3a80110 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Persons/PersonsControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Persons/PersonsControllerService.cs @@ -1,8 +1,10 @@ using Microsoft.EntityFrameworkCore; using WatchIt.Common.Model.Persons; +using WatchIt.Common.Model.Rating; using WatchIt.Common.Model.Roles; using WatchIt.Database; using WatchIt.Database.Model.Person; +using WatchIt.Database.Model.Rating; using WatchIt.Database.Model.ViewCount; using WatchIt.WebAPI.Services.Controllers.Common; using WatchIt.WebAPI.Services.Utility.User; @@ -321,6 +323,38 @@ public class PersonsControllerService : IPersonsControllerService return RequestResult.Created($"roles/creator/{item.Id}", new CreatorRoleResponse(item)); } + #endregion + + #region Rating + + public async Task GetPersonGlobalRating(long id) + { + Person? item = await _database.Persons.FirstOrDefaultAsync(x => x.Id == id); + if (item is null) + { + return RequestResult.NotFound(); + } + + RatingResponse ratingResponse = RatingResponse.Create(item.PersonActorRoles, item.PersonCreatorRoles); + + return RequestResult.Ok(ratingResponse); + } + + public async Task GetPersonUserRating(long id, long userId) + { + Person? item = await _database.Persons.FirstOrDefaultAsync(x => x.Id == id); + if (item is null) + { + return RequestResult.NotFound(); + } + + IEnumerable actorRoleRatings = item.PersonActorRoles.SelectMany(x => x.RatingPersonActorRole).Where(x => x.AccountId == userId); + IEnumerable creatorRoleRatings = item.PersonCreatorRoles.SelectMany(x => x.RatingPersonCreatorRole).Where(x => x.AccountId == userId); + RatingResponse ratingResponse = RatingResponse.Create(actorRoleRatings, creatorRoleRatings); + + return RequestResult.Ok(ratingResponse); + } + #endregion #endregion diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Persons.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Persons.cs index 4f372c7..0bbc44e 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Persons.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Persons.cs @@ -17,4 +17,6 @@ public class Persons public string PostPersonActorRole { get; set; } public string GetPersonAllCreatorRoles { get; set; } public string PostPersonCreatorRole { get; set; } + public string GetPersonGlobalRating { get; set; } + public string GetPersonUserRating { get; set; } } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Persons/IPersonsWebAPIService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Persons/IPersonsWebAPIService.cs index c1f7cd9..071ed25 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Persons/IPersonsWebAPIService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Persons/IPersonsWebAPIService.cs @@ -1,4 +1,5 @@ using WatchIt.Common.Model.Persons; +using WatchIt.Common.Model.Rating; using WatchIt.Common.Model.Roles; namespace WatchIt.Website.Services.WebAPI.Persons; @@ -12,6 +13,7 @@ public interface IPersonsWebAPIService Task DeletePerson(long id, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null); Task GetPersonsViewRank(int? first = null, int? days = null, Action>? successAction = null, Action>? badRequestAction = null); + Task PostPersonView(long personId, Action? successAction = null, Action? notFoundAction = null); Task GetPersonPhoto(long id, Action? successAction = null, Action>? badRequestAction = null, Action? notFoundAction = null); Task PutPersonPhoto(long id, PersonPhotoRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null); @@ -21,4 +23,7 @@ public interface IPersonsWebAPIService Task PostPersonActorRole(long id, ActorRolePersonRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null); Task GetPersonAllCreatorRoles(long id, CreatorRolePersonQueryParameters? query = null, Action>? successAction = null); Task PostPersonCreatorRole(long id, CreatorRolePersonRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null); + + Task GetPersonGlobalRating(long id, Action? successAction = null, Action? notFoundAction = null); + Task GetPersonUserRating(long id, long userId, Action? successAction = null, Action? notFoundAction = null); } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Persons/PersonsWebAPIService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Persons/PersonsWebAPIService.cs index cd1fd70..a38a6f0 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Persons/PersonsWebAPIService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Persons/PersonsWebAPIService.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Primitives; using WatchIt.Common.Model.Persons; +using WatchIt.Common.Model.Rating; using WatchIt.Common.Model.Roles; using WatchIt.Common.Services.HttpClient; using WatchIt.Website.Services.Utility.Configuration; @@ -139,7 +140,7 @@ public class PersonsWebAPIService : BaseWebAPIService, IPersonsWebAPIService .ExecuteAction(); } - public async Task PostPersonsView(long personId, Action? successAction = null, Action? notFoundAction = null) + public async Task PostPersonView(long personId, Action? successAction = null, Action? notFoundAction = null) { string url = GetUrl(EndpointsConfiguration.Persons.PostPersonsView, personId); @@ -258,6 +259,34 @@ public class PersonsWebAPIService : BaseWebAPIService, IPersonsWebAPIService #endregion + #region Rating + + public async Task GetPersonGlobalRating(long id, Action? successAction = null, Action? notFoundAction = null) + { + string url = GetUrl(EndpointsConfiguration.Persons.GetPersonGlobalRating, id); + + HttpRequest request = new HttpRequest(HttpMethodType.Get, url); + + HttpResponse response = await _httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor404NotFound(notFoundAction) + .ExecuteAction(); + } + + public async Task GetPersonUserRating(long id, long userId, Action? successAction = null, Action? notFoundAction = null) + { + string url = GetUrl(EndpointsConfiguration.Persons.GetPersonUserRating, id, userId); + + HttpRequest request = new HttpRequest(HttpMethodType.Get, url); + + HttpResponse response = await _httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor404NotFound(notFoundAction) + .ExecuteAction(); + } + + #endregion + #endregion diff --git a/WatchIt.Website/WatchIt.Website/App.razor b/WatchIt.Website/WatchIt.Website/App.razor index 92e4b6b..e5cba46 100644 --- a/WatchIt.Website/WatchIt.Website/App.razor +++ b/WatchIt.Website/WatchIt.Website/App.razor @@ -9,11 +9,11 @@ - + - + diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/ItemPageHeaderPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/ItemPageHeaderPanelComponent.razor new file mode 100644 index 0000000..689936a --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/ItemPageHeaderPanelComponent.razor @@ -0,0 +1,22 @@ +
+
+
+ +
+
+
+

@(Name)

+
+ @if (!string.IsNullOrWhiteSpace(Subname)) + { + @(Subname) + } + @if (!string.IsNullOrWhiteSpace(Description)) + { + @(Description) + } +
+
+
+
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/ItemPageHeaderPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/ItemPageHeaderPanelComponent.razor.cs new file mode 100644 index 0000000..96e0fdd --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/ItemPageHeaderPanelComponent.razor.cs @@ -0,0 +1,51 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model; + +namespace WatchIt.Website.Components.Common.Panels; + +public partial class ItemPageHeaderPanelComponent : ComponentBase +{ + #region PARAMETERS + + [Parameter] public required string Name { get; set; } + [Parameter] public string? Subname { get; set; } + [Parameter] public string? Description { get; set; } + + [Parameter] public required string PosterPlaceholder { get; set; } + [Parameter] public required Func, Task> GetPosterMethod { get; set; } + + #endregion + + + + #region FIELDS + + private Picture? _poster; + + #endregion + + + + #region PRIVATE METHODS + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + List endTasks = new List(1); + + // STEP 0 + endTasks.AddRange( + [ + GetPosterMethod(data => _poster = data) + ]); + + // END + await Task.WhenAll(endTasks); + + StateHasChanged(); + } + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/ItemPageHeaderPanelComponent.razor.css b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/ItemPageHeaderPanelComponent.razor.css new file mode 100644 index 0000000..8d33a8c --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/ItemPageHeaderPanelComponent.razor.css @@ -0,0 +1,13 @@ +/* CLASSES */ + +.mt-grid { + margin-top: 9rem !important; +} + +.title-shadow { + text-shadow: 2px 2px 2px #000; +} + +.description-shadow { + text-shadow: 1px 1px 1px #000; +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor index f2a1ac3..2264955 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor @@ -2,7 +2,7 @@ @if (_loaded) {
- + @if (_pictureChanged || _pictureSaved is not null) { diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/DisplayRatingComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/DisplayRatingComponent.razor new file mode 100644 index 0000000..da7a6c5 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/DisplayRatingComponent.razor @@ -0,0 +1,51 @@ +@using Blazorise.Extensions +
+ @if (Rating is null || Rating.Count > 0 || EmptyMode == DisplayRatingComponentEmptyMode.DoubleDash) + { + + } +
+ @if (Rating is not null && Rating.Count > 0) + { + @($"{Math.Round(Rating.Average, 2)}/10") + @(Rating.Count) + } + else + { +
+ @if (Rating is null) + { +
/10
+ } + else + { + switch (EmptyMode) + { + case DisplayRatingComponentEmptyMode.NoRatings: @("no ratings"); break; + case DisplayRatingComponentEmptyMode.DoubleDash: @("--/10"); break; + } + } +
+ } +
+
+ + \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/DisplayRatingComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/DisplayRatingComponent.razor.cs new file mode 100644 index 0000000..8f6935e --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/DisplayRatingComponent.razor.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Rating; + +namespace WatchIt.Website.Components.Common.Subcomponents; + +public partial class DisplayRatingComponent : ComponentBase +{ + #region PARAMETERS + + [Parameter] public RatingResponse? Rating { get; set; } + [Parameter] public DisplayRatingComponentEmptyMode EmptyMode { get; set; } = DisplayRatingComponentEmptyMode.NoRatings; + [Parameter] public double Scale { get; set; } = 1; + + #endregion + + + + #region ENUMS + + public enum DisplayRatingComponentEmptyMode + { + NoRatings, + DoubleDash, + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/ListItemComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/ListItemComponent.razor index 7f48f38..42b9837 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/ListItemComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/ListItemComponent.razor @@ -1,24 +1,50 @@
- + + +
- - @(Name)@(string.IsNullOrWhiteSpace(AdditionalNameInfo) ? string.Empty : AdditionalNameInfo) - + + @(Name)@(string.IsNullOrWhiteSpace(AdditionalInfo) ? string.Empty : AdditionalInfo) +
-
- -
- @(Rating.Count > 0 ? Rating.Average : "--")/10 - @if (Rating.Count > 0) - { - @(Rating.Count) - } -
+
+ +
+ Global rating: + +
+
+ @if (GetUserRatingMethod is not null && PutRatingMethod is not null && DeleteRatingMethod is not null) + { +
+
+ Your rating: +
+ @if (_user is null) + { + You must be logged in to rate + } + else if (!_userRatingLoaded) + { +
+ + Loading... +
+ } + else + { + + } +
+
+ }
diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/ListItemComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/ListItemComponent.razor.cs index f96cf4b..818f3ee 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/ListItemComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/ListItemComponent.razor.cs @@ -1,29 +1,52 @@ using Microsoft.AspNetCore.Components; using WatchIt.Common.Model; using WatchIt.Common.Model.Rating; +using WatchIt.Website.Services.Utility.Authentication; namespace WatchIt.Website.Components.Common.Subcomponents; public partial class ListItemComponent : ComponentBase { + #region SERVICES + + [Inject] private IAuthenticationService AuthenticationService { get; set; } = default!; + + #endregion + + + #region PARAMETERS - [Parameter] public required long Id { get; set; } [Parameter] public required string Name { get; set; } - [Parameter] public string? AdditionalNameInfo { get; set; } - [Parameter] public required RatingResponse Rating { get; set; } - [Parameter] public required Func, Task> PictureDownloadingTask { get; set; } - [Parameter] public int PictureHeight { get; set; } = 150; + [Parameter] public string? AdditionalInfo { get; set; } + + [Parameter] public int NameSize { get; set; } = 25; + + [Parameter] public required string PosterPlaceholder { get; set; } + [Parameter] public int PosterHeight { get; set; } = 150; + [Parameter] public required Func, Task> PosterDownloadingTask { get; set; } + + [Parameter] public RatingResponse? GlobalRating { get; set; } + [Parameter] public required Func, Task> GetGlobalRatingMethod { get; set; } + [Parameter] public Func, Action, Task>? GetUserRatingMethod { get; set; } + [Parameter] public Func? PutRatingMethod { get; set; } + [Parameter] public Func? DeleteRatingMethod { get; set; } + [Parameter] public Action? OnRatingChanged { get; set; } + + [Parameter] public required string ItemUrl { get; set; } #endregion #region FIELDS - - private bool _loaded; - private Picture? _picture; + private User? _user; + + private Picture? _poster; + + private int _userRating; + private bool _userRatingLoaded; #endregion @@ -35,20 +58,71 @@ public partial class ListItemComponent : ComponentBase { if (firstRender) { - List endTasks = new List(); + List step1Tasks = new List(1); + List endTasks = new List(3); // STEP 0 + if (GetUserRatingMethod is not null) + { + step1Tasks.AddRange( + [ + Task.Run(async () => _user = await AuthenticationService.GetUserAsync()), + ]); + } endTasks.AddRange( [ - PictureDownloadingTask(Id, picture => _picture = picture), + PosterDownloadingTask(data => _poster = data), ]); + if (GlobalRating is null) + { + endTasks.AddRange( + [ + GetGlobalRatingMethod(data => GlobalRating = data), + ]); + } + + // STEP 1 + await Task.WhenAll(step1Tasks); + StateHasChanged(); + if (GetUserRatingMethod is not null && _user is not null) + { + endTasks.AddRange( + [ + GetUserRatingMethod(_user.Id, + data => + { + _userRating = data; + _userRatingLoaded = true; + }, + () => + { + _userRating = 0; + _userRatingLoaded = true; + } + ) + ]); + } await Task.WhenAll(endTasks); - _loaded = true; StateHasChanged(); } } + + private async Task RatingChanged() + { + if (_userRating == 0) + { + await DeleteRatingMethod!(); + } + else + { + await PutRatingMethod!(new RatingRequest((short)_userRating)); + } + + await GetGlobalRatingMethod(data => GlobalRating = data); + OnRatingChanged?.Invoke(); + } #endregion } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/ListItemComponent.razor.css b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/ListItemComponent.razor.css index 91b920c..7c26de1 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/ListItemComponent.razor.css +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/ListItemComponent.razor.css @@ -1,17 +1,10 @@ /* IDS */ -#nameText { - font-size: 25px; +#ratingNameText { + font-size: 14px; + font-weight: bold; } -#ratingStar { - font-size: 30px; -} - -#ratingValue { - font-size: 20px; -} - -#ratingCount { - font-size: 10px; +#ratingLoginInfoText { + font-size: 13px; } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/PictureComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/PictureComponent.razor index 52a3e03..398c4d6 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/PictureComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/PictureComponent.razor @@ -1,7 +1 @@ -@(AlternativeText) - - \ No newline at end of file +@(AlternativeText) \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/RoleListComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/RoleListComponent.razor new file mode 100644 index 0000000..01b8398 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/RoleListComponent.razor @@ -0,0 +1,58 @@ +@typeparam TRole where TRole : WatchIt.Common.Model.Roles.IRoleResponse, WatchIt.Common.Query.IQueryOrderable +@typeparam TQuery where TQuery : WatchIt.Common.Query.QueryParameters +@typeparam TRoleParent + + + +@if (_loaded) +{ + if (_roles.Count > 0) + { +
+ @for (int i = 0; i < _roles.Count; i++) + { + { + int iCopy = i; + KeyValuePair roleParentPair = _roles.ElementAt(i); + TRole role = roleParentPair.Key; + TRoleParent parent = roleParentPair.Value; + string url = string.Format(ParentUrlTemplate, ParentItemIdSource(role)); + if (i > 0) + { +
+ } + + } + } + @if (!_allItemsLoaded) + { +
+ +
+ } +
+ } + else + { + No roles found + } +} +else +{ + +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleListComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/RoleListComponent.razor.cs similarity index 56% rename from WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleListComponent.razor.cs rename to WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/RoleListComponent.razor.cs index da39771..8e5f918 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleListComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/RoleListComponent.razor.cs @@ -1,33 +1,35 @@ -using System.ComponentModel; using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model; using WatchIt.Common.Model.Rating; -using WatchIt.Common.Model.Roles; -using WatchIt.Common.Query; -namespace WatchIt.Website.Components.Pages.MediaPage.Subcomponents; +namespace WatchIt.Website.Components.Common.Subcomponents; -public partial class RoleListComponent : ComponentBase where TRole : IRoleResponse, IQueryOrderable where TQuery : QueryParameters +public partial class RoleListComponent : ComponentBase where TRole : WatchIt.Common.Model.Roles.IRoleResponse, WatchIt.Common.Query.IQueryOrderable + where TQuery : WatchIt.Common.Query.QueryParameters { - #region SERVICES - - [Inject] private NavigationManager NavigationManager { get; set; } = default!; - - #endregion - - - #region PARAMETERS [Parameter] public required long Id { get; set; } [Parameter] public TQuery Query { get; set; } = Activator.CreateInstance(); - [Parameter] public Func? AdditionalTextSource { get; set; } - [Parameter] public required Func>, Task> GetRolesAction { get; set; } - [Parameter] public required Func, Task> GetGlobalRatingAction { get; set; } - [Parameter] public required Func, Action, Task> GetUserRatingAction { get; set; } - [Parameter] public required Func PutRatingAction { get; set; } - [Parameter] public required Func DeleteRatingAction { get; set; } - + + [Parameter] public required Func NameSource { get; set; } + [Parameter] public Func? AdditionalInfoSource { get; set; } + + [Parameter] public required Func, Task> GetRoleParentMethod { get; set; } + [Parameter] public required Func ParentItemIdSource { get; set; } + [Parameter] public required string ParentUrlTemplate { get; set; } + + [Parameter] public required string PosterPlaceholder { get; set; } + [Parameter] public required Func, Task> PosterDownloadingTask { get; set; } + + [Parameter] public required Func, Task> GetGlobalRatingMethod { get; set; } + [Parameter] public required Func, Action, Task> GetUserRatingMethod { get; set; } + [Parameter] public required Func PutRatingMethod { get; set; } + [Parameter] public required Func DeleteRatingMethod { get; set; } + + [Parameter] public Action? OnRatingChanged { get; set; } + #endregion @@ -39,7 +41,7 @@ public partial class RoleListComponent : ComponentBase where TRol private bool _loaded; private bool _allItemsLoaded; - private List _roles = new List(); + private Dictionary _roles = new Dictionary(); private bool _rolesFetching; #endregion @@ -72,6 +74,7 @@ public partial class RoleListComponent : ComponentBase where TRol // INIT Query.First = _pageSize; + Query.OrderBy = "rating.average"; // STEP 0 endTasks.AddRange( @@ -81,18 +84,18 @@ public partial class RoleListComponent : ComponentBase where TRol // END await Task.WhenAll(endTasks); - - _loaded = true; - StateHasChanged(); } } private async Task GetRoles(bool firstFetch = false) { _rolesFetching = true; - await GetRolesAction(Id, Query, data => + await GetRolesAction(Id, Query, async data => { - _roles.AddRange(data); + foreach (TRole item in data) + { + await GetRoleParentMethod(ParentItemIdSource(item), parent => _roles[item] = parent); + } if (data.Count() < _pageSize) { _allItemsLoaded = true; @@ -106,6 +109,9 @@ public partial class RoleListComponent : ComponentBase where TRol } Query.After += data.Count(); _rolesFetching = false; + + _loaded = true; + StateHasChanged(); }); } diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/TitledDisplayRatingComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/TitledDisplayRatingComponent.razor new file mode 100644 index 0000000..6eb08a6 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/TitledDisplayRatingComponent.razor @@ -0,0 +1,10 @@ +
+
+
+ @(Title) +
+
+ +
+
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/TitledDisplayRatingComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/TitledDisplayRatingComponent.razor.cs new file mode 100644 index 0000000..937e658 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/TitledDisplayRatingComponent.razor.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Rating; + +namespace WatchIt.Website.Components.Common.Subcomponents; + +public partial class TitledDisplayRatingComponent : ComponentBase +{ + #region PARAMETERS + + [Parameter] public required RatingResponse Rating { get; set; } + [Parameter] public required string Title { get; set; } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/TitledDisplayRatingComponent.razor.css b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/TitledDisplayRatingComponent.razor.css new file mode 100644 index 0000000..f0c49bb --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/TitledDisplayRatingComponent.razor.css @@ -0,0 +1,5 @@ +/* IDS */ + +#title { + font-size: 1.5rem; +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/DatabasePage/DatabasePageComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/DatabasePage/DatabasePageComponent.razor index 2df7f44..0a3738a 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/DatabasePage/DatabasePageComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/DatabasePage/DatabasePageComponent.razor @@ -52,12 +52,19 @@ { foreach (TItem item in _items) { -
- + long id = IdSource(item); + string url = string.Format(UrlIdTemplate, id); +
+
} if (!_allItemsLoaded) diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/DatabasePage/DatabasePageComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/DatabasePage/DatabasePageComponent.razor.cs index 2bb0928..6d07ab9 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/DatabasePage/DatabasePageComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/DatabasePage/DatabasePageComponent.razor.cs @@ -28,6 +28,12 @@ public partial class DatabasePageComponent : ComponentBase where [Parameter] public required Func>, Task> ItemDownloadingTask { get; set; } [Parameter] public required Dictionary SortingOptions { get; set; } [Parameter] public required RenderFragment ChildContent { get; set; } + [Parameter] public required Func, Task> GetGlobalRatingMethod { get; set; } + [Parameter] public Func, Action, Task>? GetUserRatingMethod { get; set; } + [Parameter] public Func? PutRatingMethod { get; set; } + [Parameter] public Func? DeleteRatingMethod { get; set; } + [Parameter] public required string PosterPlaceholder { get; set; } + #endregion diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/ActorRolesPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/ActorRolesPanelComponent.razor deleted file mode 100644 index 8b32774..0000000 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/ActorRolesPanelComponent.razor +++ /dev/null @@ -1,18 +0,0 @@ -@using WatchIt.Website.Components.Pages.MediaPage.Subcomponents - - - -
-
- Actors - -
-
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/MediaActorRolesPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/MediaActorRolesPanelComponent.razor new file mode 100644 index 0000000..80804ab --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/MediaActorRolesPanelComponent.razor @@ -0,0 +1,26 @@ +@using WatchIt.Common.Model.Persons +@using WatchIt.Common.Model.Roles + + + +
+
+ Actors + +
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/ActorRolesPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/MediaActorRolesPanelComponent.razor.cs similarity index 76% rename from WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/ActorRolesPanelComponent.razor.cs rename to WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/MediaActorRolesPanelComponent.razor.cs index 5cd65b7..54e7287 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/ActorRolesPanelComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/MediaActorRolesPanelComponent.razor.cs @@ -1,15 +1,17 @@ using Microsoft.AspNetCore.Components; using WatchIt.Website.Services.Utility.Authentication; using WatchIt.Website.Services.WebAPI.Media; +using WatchIt.Website.Services.WebAPI.Persons; using WatchIt.Website.Services.WebAPI.Roles; namespace WatchIt.Website.Components.Pages.MediaPage.Panels; -public partial class ActorRolesPanelComponent : ComponentBase +public partial class MediaActorRolesPanelComponent : ComponentBase { #region SERVICES [Inject] private IMediaWebAPIService MediaWebAPIService { get; set; } = default!; + [Inject] private IPersonsWebAPIService PersonsWebAPIService { get; set; } = default!; [Inject] private IRolesWebAPIService RolesWebAPIService { get; set; } = default!; #endregion diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/CreatorRolesPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/MediaCreatorRolesPanelComponent.razor similarity index 52% rename from WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/CreatorRolesPanelComponent.razor rename to WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/MediaCreatorRolesPanelComponent.razor index 29a7c51..3361227 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/CreatorRolesPanelComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/MediaCreatorRolesPanelComponent.razor @@ -1,5 +1,5 @@ +@using WatchIt.Common.Model.Persons @using WatchIt.Common.Model.Roles -@using WatchIt.Website.Components.Pages.MediaPage.Subcomponents @@ -16,16 +16,23 @@ }
- + GetRolesAction="@(MediaWebAPIService.GetMediaAllCreatorRoles)" + NameSource="@((_, parent) => parent.Name)" + GetRoleParentMethod="@((id, action) => PersonsWebAPIService.GetPerson(id, action))" + ParentItemIdSource="@(item => item.PersonId)" + ParentUrlTemplate="/person/{0}" + PosterPlaceholder="/assets/person_poster.png" + PosterDownloadingTask="@((id, action) => PersonsWebAPIService.GetPersonPhoto(id, action))" + GetGlobalRatingMethod="@((id, action) => RolesWebAPIService.GetCreatorRoleRating(id, action))" + GetUserRatingMethod="@((id, userId, actionSuccess, actionNotFound) => RolesWebAPIService.GetCreatorRoleRatingByUser(id, userId, actionSuccess, actionNotFound))" + PutRatingMethod="@((id, request) => RolesWebAPIService.PutCreatorRoleRating(id, request))" + DeleteRatingMethod="@((id) => RolesWebAPIService.DeleteCreatorRoleRating(id))"/>
} else diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/CreatorRolesPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/MediaCreatorRolesPanelComponent.razor.cs similarity index 80% rename from WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/CreatorRolesPanelComponent.razor.cs rename to WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/MediaCreatorRolesPanelComponent.razor.cs index 181c061..c1bb1b4 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/CreatorRolesPanelComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Panels/MediaCreatorRolesPanelComponent.razor.cs @@ -1,19 +1,21 @@ using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Persons; using WatchIt.Common.Model.Roles; -using WatchIt.Website.Components.Pages.MediaPage.Subcomponents; +using WatchIt.Website.Components.Common.Subcomponents; using WatchIt.Website.Services.Utility.Authentication; using WatchIt.Website.Services.WebAPI.Media; +using WatchIt.Website.Services.WebAPI.Persons; using WatchIt.Website.Services.WebAPI.Roles; namespace WatchIt.Website.Components.Pages.MediaPage.Panels; -public partial class CreatorRolesPanelComponent : ComponentBase +public partial class MediaCreatorRolesPanelComponent : ComponentBase { #region SERVICES - + + [Inject] private IPersonsWebAPIService PersonsWebAPIService { get; set; } = default!; [Inject] private IMediaWebAPIService MediaWebAPIService { get; set; } = default!; [Inject] private IRolesWebAPIService RolesWebAPIService { get; set; } = default!; - [Inject] private IAuthenticationService AuthenticationService { get; set; } = default!; #endregion @@ -29,10 +31,8 @@ public partial class CreatorRolesPanelComponent : ComponentBase #region FIELDS - - private User? _user; - private RoleListComponent _roleListComponent; + private RoleListComponent _roleListComponent; private bool _loaded; @@ -44,11 +44,6 @@ public partial class CreatorRolesPanelComponent : ComponentBase #region PRIVATE METHODS - - protected override async Task OnParametersSetAsync() - { - _user = await AuthenticationService.GetUserAsync(); - } protected override async Task OnAfterRenderAsync(bool firstRender) { diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleComponent.razor deleted file mode 100644 index a05b1fd..0000000 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleComponent.razor +++ /dev/null @@ -1,66 +0,0 @@ -@using WatchIt.Common.Model.Roles -@typeparam TRole where TRole : WatchIt.Common.Model.Roles.IRoleResponse - -
-
-
- - - -
-
-
-
- - @if (_person is null) - { - Loading... - } - else - { - - @(_person.Name)@(Role is ActorRoleResponse actor ? $" as {actor.Name}" : string.Empty) - - } - -
-
-
- Global rating: -
- -
- @if (_globalRating is not null) - { - @(_globalRating.Count > 0 ? _globalRating.Average : "--")/10 - if (_globalRating.Count > 0) - { - @(_globalRating.Count) - } - } - else - { -
- } -
-
-
-
-
- Your rating: -
- @if (_user is not null) - { - - } - else - { - You must be logged in to rate the role - } -
-
-
-
-
-
-
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleComponent.razor.cs deleted file mode 100644 index fe7d00d..0000000 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleComponent.razor.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Microsoft.AspNetCore.Components; -using WatchIt.Common.Model; -using WatchIt.Common.Model.Persons; -using WatchIt.Common.Model.Rating; -using WatchIt.Common.Model.Roles; -using WatchIt.Website.Services.Utility.Authentication; -using WatchIt.Website.Services.WebAPI.Persons; - -namespace WatchIt.Website.Components.Pages.MediaPage.Subcomponents; - -public partial class RoleComponent : ComponentBase where TRole : IRoleResponse -{ - #region SERVICES - - [Inject] private IPersonsWebAPIService PersonsWebAPIService { get; set; } = default!; - [Inject] private IAuthenticationService AuthenticationService { get; set; } = default!; - - #endregion - - - - #region PARAMETERS - - [Parameter] public required TRole Role { get; set; } - [Parameter] public required Func, Task> GetGlobalRatingAction { get; set; } - [Parameter] public required Func, Action, Task> GetUserRatingAction { get; set; } - [Parameter] public required Func PutRatingAction { get; set; } - [Parameter] public required Func DeleteRatingAction { get; set; } - - #endregion - - - - #region FIELDS - - private User? _user; - private PersonResponse? _person; - private Picture? _picture; - private RatingResponse? _globalRating; - - private int _yourRating; - - #endregion - - - - #region PRIVATE METHODS - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - List step1Tasks = new List(); - List endTasks = new List(); - - // STEP 0 - step1Tasks.AddRange( - [ - Task.Run(async () => _user = await AuthenticationService.GetUserAsync()), - ]); - endTasks.AddRange( - [ - PersonsWebAPIService.GetPersonPhoto(Role.PersonId, data => _picture = data), - PersonsWebAPIService.GetPerson(Role.PersonId, data => _person = data), - GetGlobalRatingAction(Role.Id, data => _globalRating = data) - ]); - - // STEP 1 - await Task.WhenAll(step1Tasks); - if (_user is not null) - { - endTasks.AddRange( - [ - GetUserRatingAction(Role.Id, _user.Id, data => _yourRating = data, () => _yourRating = 0) - ]); - } - - // END - await Task.WhenAll(endTasks); - - StateHasChanged(); - } - } - - private async Task RatingChanged() - { - if (_yourRating == 0) - { - await DeleteRatingAction(Role.Id); - } - else - { - await PutRatingAction(Role.Id, new RatingRequest((short)_yourRating)); - } - - await GetGlobalRatingAction(Role.Id, data => _globalRating = data); - } - - #endregion -} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleComponent.razor.css b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleComponent.razor.css deleted file mode 100644 index 9179236..0000000 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleComponent.razor.css +++ /dev/null @@ -1,26 +0,0 @@ -/* IDS */ - -#nameText { - font-size: 20px; -} - -#ratingNameText { - font-size: 14px; - font-weight: bold; -} - -#ratingLoginInfoText { - font-size: 13px; -} - -#ratingStar { - font-size: 30px; -} - -#ratingValue { - font-size: 20px; -} - -#ratingCount { - font-size: 10px; -} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleListComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleListComponent.razor deleted file mode 100644 index f16167c..0000000 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleListComponent.razor +++ /dev/null @@ -1,45 +0,0 @@ -@typeparam TRole where TRole : WatchIt.Common.Model.Roles.IRoleResponse, WatchIt.Common.Query.IQueryOrderable -@typeparam TQuery where TQuery : WatchIt.Common.Query.QueryParameters - -@if (_loaded) -{ - if (_roles.Count > 0) - { -
- @for (int i = 0; i < _roles.Count; i++) - { - { - int iCopy = i; - if (i > 0) - { -
- } - - } - } - @if (!_allItemsLoaded) - { -
- -
- } -
- } - else - { - No roles found - } -} -else -{ - -} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleListComponent.razor.css b/WatchIt.Website/WatchIt.Website/Components/Pages/MediaPage/Subcomponents/RoleListComponent.razor.css deleted file mode 100644 index e69de29..0000000 diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonActorRolesPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonActorRolesPanelComponent.razor new file mode 100644 index 0000000..88a40e8 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonActorRolesPanelComponent.razor @@ -0,0 +1,27 @@ +@using WatchIt.Common.Model.Persons +@using WatchIt.Common.Model.Roles + + + +
+
+ Actor + +
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonActorRolesPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonActorRolesPanelComponent.razor.cs new file mode 100644 index 0000000..4b87639 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonActorRolesPanelComponent.razor.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Website.Services.WebAPI.Media; +using WatchIt.Website.Services.WebAPI.Persons; +using WatchIt.Website.Services.WebAPI.Roles; + +namespace WatchIt.Website.Components.Pages.PersonPage.Panels; + +public partial class PersonActorRolesPanelComponent : ComponentBase +{ + #region SERVICES + + [Inject] private IMediaWebAPIService MediaWebAPIService { get; set; } = default!; + [Inject] private IPersonsWebAPIService PersonsWebAPIService { get; set; } = default!; + [Inject] private IRolesWebAPIService RolesWebAPIService { get; set; } = default!; + + #endregion + + + + #region PARAMETERS + + [Parameter] public string Class { get; set; } = string.Empty; + [Parameter] public required long Id { get; set; } + [Parameter] public Action? OnRatingChanged { get; set; } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonCreatorRolesPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonCreatorRolesPanelComponent.razor new file mode 100644 index 0000000..609b18a --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonCreatorRolesPanelComponent.razor @@ -0,0 +1,43 @@ +@using WatchIt.Common.Model.Persons +@using WatchIt.Common.Model.Roles + + + +
+ @if (_loaded) + { +
+ Creators +
+ + @foreach (RoleTypeResponse roleType in _roleTypes) + { + @roleType.Name + } + +
+ +
+ } + else + { + + } +
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonCreatorRolesPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonCreatorRolesPanelComponent.razor.cs new file mode 100644 index 0000000..ac6f353 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonCreatorRolesPanelComponent.razor.cs @@ -0,0 +1,76 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Media; +using WatchIt.Common.Model.Roles; +using WatchIt.Website.Components.Common.Subcomponents; +using WatchIt.Website.Services.WebAPI.Media; +using WatchIt.Website.Services.WebAPI.Persons; +using WatchIt.Website.Services.WebAPI.Roles; + +namespace WatchIt.Website.Components.Pages.PersonPage.Panels; + +public partial class PersonCreatorRolesPanelComponent : ComponentBase +{ + #region SERVICES + + [Inject] private IPersonsWebAPIService PersonsWebAPIService { get; set; } = default!; + [Inject] private IMediaWebAPIService MediaWebAPIService { get; set; } = default!; + [Inject] private IRolesWebAPIService RolesWebAPIService { get; set; } = default!; + + #endregion + + + + #region PARAMETERS + + [Parameter] public string Class { get; set; } = string.Empty; + [Parameter] public required long Id { get; set; } + [Parameter] public Action? OnRatingChanged { get; set; } + + #endregion + + + + #region FIELDS + + private RoleListComponent _roleListComponent; + + private bool _loaded; + + private IEnumerable _roleTypes; + private CreatorRolePersonQueryParameters _query; + + #endregion + + + + #region PRIVATE METHODS + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + List endTasks = new List(); + + // STEP 0 + endTasks.AddRange( + [ + RolesWebAPIService.GetAllCreatorRoleTypes(successAction: data => _roleTypes = data) + ]); + + // END + await Task.WhenAll(endTasks); + _query = new CreatorRolePersonQueryParameters { TypeId = _roleTypes.First().Id }; + + _loaded = true; + StateHasChanged(); + } + } + + private async Task CheckedTypeChanged(short value) + { + _query.TypeId = value; + await _roleListComponent.Refresh(); + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonMetadataPanel.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonMetadataPanel.razor new file mode 100644 index 0000000..331fec5 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonMetadataPanel.razor @@ -0,0 +1,16 @@ +
+ +
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonMetadataPanel.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonMetadataPanel.razor.cs new file mode 100644 index 0000000..03abb8b --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonMetadataPanel.razor.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Persons; + +namespace WatchIt.Website.Components.Pages.PersonPage.Panels; + +public partial class PersonMetadataPanel : ComponentBase +{ + #region PARAMETERS + + [Parameter] public required PersonResponse Item { get; set; } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonRatingPanel.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonRatingPanel.razor new file mode 100644 index 0000000..2490e0b --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonRatingPanel.razor @@ -0,0 +1,17 @@ +
+
+ +
+ @if (_user is not null) + { + + } + else + { +
+

Your rating:

+ Log in to see your rating +
+ } +
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonRatingPanel.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonRatingPanel.razor.cs new file mode 100644 index 0000000..4e57c54 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/PersonPage/Panels/PersonRatingPanel.razor.cs @@ -0,0 +1,97 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Rating; +using WatchIt.Website.Services.Utility.Authentication; +using WatchIt.Website.Services.WebAPI.Persons; + +namespace WatchIt.Website.Components.Pages.PersonPage.Panels; + +public partial class PersonRatingPanel : ComponentBase +{ + #region SERVICES + + [Inject] private IAuthenticationService AuthenticationService { get; set; } = default!; + [Inject] private IPersonsWebAPIService PersonsWebAPIService { get; set; } = default!; + + #endregion + + + + #region PARAMETERS + + [Parameter] public required long Id { get; set; } + [Parameter] public RatingResponse? Rating { get; set; } + + #endregion + + + + #region FIELDS + + private User? _user; + + private RatingResponse? _userRating; + + #endregion + + + + #region PUBLIC METHODS + + public async Task UpdateRating() + { + await Task.WhenAll(UpdateGlobalRating(), UpdateUserRating()); + StateHasChanged(); + } + + #endregion + + + + #region PRIVATE METHODS + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + List step1Tasks = new List(1); + List endTasks = new List(2); + + // STEP 0 + step1Tasks.AddRange( + [ + Task.Run(async () => _user = await AuthenticationService.GetUserAsync()) + ]); + if (Rating is null) + { + endTasks.AddRange( + [ + UpdateGlobalRating() + ]); + } + + // STEP 1 + await Task.WhenAll(step1Tasks); + endTasks.AddRange( + [ + UpdateUserRating() + ]); + + // END + await Task.WhenAll(endTasks); + + StateHasChanged(); + } + } + + protected async Task UpdateGlobalRating() => await PersonsWebAPIService.GetPersonGlobalRating(Id, data => Rating = data); + + protected async Task UpdateUserRating() + { + if (_user is not null) + { + await PersonsWebAPIService.GetPersonUserRating(Id, _user.Id, data => _userRating = data); + } + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/SearchResultPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/SearchResultPanelComponent.razor index e899426..c0a7d47 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/SearchResultPanelComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/SearchResultPanelComponent.razor @@ -28,13 +28,21 @@ }
- - - + @{ + int iCopy = i; + long id = IdSource(_items[iCopy]); + string url = string.Format(UrlIdTemplate, id); + } +
} diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/SearchResultPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/SearchResultPanelComponent.razor.cs index 428f4fe..e3f2f68 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/SearchResultPanelComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/SearchResultPanelComponent.razor.cs @@ -11,11 +11,18 @@ public partial class SearchResultPanelComponent : ComponentBase w [Parameter] public required string Title { get; set; } [Parameter] public required TQuery Query { get; set; } + [Parameter] public required Func IdSource { get; set; } [Parameter] public required Func NameSource { get; set; } [Parameter] public Func AdditionalNameInfoSource { get; set; } = _ => null; [Parameter] public required Func RatingSource { get; set; } + [Parameter] public required Func, Task> GetGlobalRatingMethod { get; set; } + [Parameter] public Func, Action, Task>? GetUserRatingMethod { get; set; } + [Parameter] public Func? PutRatingMethod { get; set; } + [Parameter] public Func? DeleteRatingMethod { get; set; } [Parameter] public required string UrlIdTemplate { get; set; } + [Parameter] public required string PosterPlaceholder { get; set; } + [Parameter] public required Func>, Task> ItemDownloadingTask { get; set; } [Parameter] public required Func, Task> PictureDownloadingTask { get; set; } diff --git a/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor b/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor index 0d46cb7..5740ae8 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor @@ -46,7 +46,12 @@ { "rating.average", "Average rating" }, { "title", "Title" }, { "release_date", "Release date" }, - })"> + })" + PosterPlaceholder="/assets/media_poster.png" + GetGlobalRatingMethod="@((id, action) => MediaWebAPIService.GetMediaRating(id, action))" + GetUserRatingMethod="@((id, userId, successAction, notfoundAction) => MediaWebAPIService.GetMediaRatingByUser(id, userId, successAction, notfoundAction))" + PutRatingMethod="@((id, request) => MediaWebAPIService.PutMediaRating(id, request))" + DeleteRatingMethod="@(id => MediaWebAPIService.DeleteMediaRating(id))"> break; @@ -67,7 +72,12 @@ { "rating.average", "Average rating" }, { "title", "Title" }, { "release_date", "Release date" }, - })"> + })" + PosterPlaceholder="/assets/media_poster.png" + GetGlobalRatingMethod="@((id, action) => MediaWebAPIService.GetMediaRating(id, action))" + GetUserRatingMethod="@((id, userId, successAction, notfoundAction) => MediaWebAPIService.GetMediaRatingByUser(id, userId, successAction, notfoundAction))" + PutRatingMethod="@((id, request) => MediaWebAPIService.PutMediaRating(id, request))" + DeleteRatingMethod="@(id => MediaWebAPIService.DeleteMediaRating(id))"> break; @@ -88,7 +98,9 @@ { "name", "Name" }, { "birth_date", "Birth date" }, { "death_date", "Death date" }, - })"> + })" + PosterPlaceholder="/assets/person_poster.png" + GetGlobalRatingMethod="@((id, action) => PersonsWebAPIService.GetPersonGlobalRating(id, action))"> break; diff --git a/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor.cs index 3f9f6d9..179454a 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Components; +using WatchIt.Website.Layout; using WatchIt.Website.Services.WebAPI.Media; using WatchIt.Website.Services.WebAPI.Movies; using WatchIt.Website.Services.WebAPI.Persons; @@ -24,6 +25,8 @@ public partial class DatabasePage : ComponentBase [Parameter] public string? Type { get; set; } + [CascadingParameter] public MainLayout Layout { get; set; } + #endregion @@ -46,5 +49,16 @@ public partial class DatabasePage : ComponentBase } } + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) + { + // INIT + Layout.BackgroundPhoto = null; + + StateHasChanged(); + } + } + #endregion } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor b/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor index 47a2c9b..2dde4a8 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor @@ -30,50 +30,25 @@ else { if (string.IsNullOrWhiteSpace(_error)) { -
-
- -
+
-
-
-
-
-

- @_media.Title -

-
-
- @if (!string.IsNullOrWhiteSpace(_media.Description)) - { -
-
-
- @_media.Description -
-
-
- } -
-
+
-
+
-
+
-
+
-

- Global rating: @(_globalRating.Count == 0 ? "no ratings" : $"{Math.Round(_globalRating.Average, 1)}/10") -

+
@@ -204,12 +177,12 @@ else
- +
- +
diff --git a/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor.cs index fb48fd0..e3321c9 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor.cs @@ -90,7 +90,6 @@ public partial class MediaPage : ComponentBase [ MediaWebAPIService.PostMediaView(Id), MediaWebAPIService.GetMediaPhotoRandomBackground(Id, data => Layout.BackgroundPhoto = data), - MediaWebAPIService.GetMediaPoster(Id, data => _poster = data), MediaWebAPIService.GetMediaGenres(Id, data => _genres = data), MediaWebAPIService.GetMediaRating(Id, data => _globalRating = data), _media.Type == MediaType.Movie ? MoviesWebAPIService.GetMovie(Id, data => _movie = data) : SeriesWebAPIService.GetSeries(Id, data => _series = data), diff --git a/WatchIt.Website/WatchIt.Website/Pages/PersonPage.razor b/WatchIt.Website/WatchIt.Website/Pages/PersonPage.razor new file mode 100644 index 0000000..d88eb16 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Pages/PersonPage.razor @@ -0,0 +1,89 @@ +@using System.Text +@using WatchIt.Website.Components.Pages.PersonPage.Panels + +@page "/person/{id:long}" + +@{ + StringBuilder sb = new StringBuilder(" - WatchIt"); + + if (!_loaded) sb.Insert(0, "Loading..."); + else if (_person is null) sb.Insert(0, "Error"); + else sb.Insert(0, _person.Name); + + @(sb.ToString()) +} + + + +
+ @if (_loaded) + { + if (_person is not null) + { +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ + + Actor + Creator + + + +
+ +
+
+ +
+ +
+
+
+
+
+
+ } + else + { +
+
+ +
+
+ } + } + else + { +
+
+
+ +
+
+
+ } +
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/PersonPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/PersonPage.razor.cs new file mode 100644 index 0000000..12120a1 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Pages/PersonPage.razor.cs @@ -0,0 +1,78 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Persons; +using WatchIt.Website.Components.Pages.PersonPage.Panels; +using WatchIt.Website.Layout; +using WatchIt.Website.Services.WebAPI.Persons; + +namespace WatchIt.Website.Pages; + +public partial class PersonPage : ComponentBase +{ + #region SERVICES + + [Inject] private IPersonsWebAPIService PersonsWebAPIService { get; set; } = default!; + + #endregion + + + + #region PARAMETERS + + [Parameter] public long Id { get; set; } + + [CascadingParameter] public MainLayout Layout { get; set; } = default!; + + #endregion + + + + #region FIELDS + + private bool _loaded; + + private PersonRatingPanel _ratingPanel = default!; + + private PersonResponse? _person; + + #endregion + + + + #region PRIVATE METHODS + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + // INIT + Layout.BackgroundPhoto = null; + + List step1Tasks = new List(1); + List endTasks = new List(1); + + // STEP 0 + step1Tasks.AddRange( + [ + PersonsWebAPIService.GetPerson(Id, data => _person = data) + ]); + + // STEP 1 + await Task.WhenAll(step1Tasks); + if (_person is not null) + { + endTasks.AddRange( + [ + PersonsWebAPIService.PostPersonView(Id), + ]); + } + + // END + await Task.WhenAll(endTasks); + + _loaded = true; + StateHasChanged(); + } + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor.css b/WatchIt.Website/WatchIt.Website/Pages/PersonPage.razor.css similarity index 100% rename from WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor.css rename to WatchIt.Website/WatchIt.Website/Pages/PersonPage.razor.css diff --git a/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor b/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor index 1b962e4..a88e28e 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor @@ -29,7 +29,12 @@ RatingSource="@(item => item.Rating)" Query="@(new MovieQueryParameters { Title = DecodedQuery, OrderBy = "rating.count" })" ItemDownloadingTask="@(MoviesWebAPIService.GetAllMovies)" - PictureDownloadingTask="@((id, action) => MediaWebAPIService.GetMediaPoster(id, action))"/> + PictureDownloadingTask="@((id, action) => MediaWebAPIService.GetMediaPoster(id, action))" + PosterPlaceholder="/assets/media_poster.png" + GetGlobalRatingMethod="@((id, action) => MediaWebAPIService.GetMediaRating(id, action))" + GetUserRatingMethod="@((id, userId, successAction, notfoundAction) => MediaWebAPIService.GetMediaRatingByUser(id, userId, successAction, notfoundAction))" + PutRatingMethod="@((id, request) => MediaWebAPIService.PutMediaRating(id, request))" + DeleteRatingMethod="@(id => MediaWebAPIService.DeleteMediaRating(id))"/> + PictureDownloadingTask="@((id, action) => MediaWebAPIService.GetMediaPoster(id, action))" + PosterPlaceholder="/assets/media_poster.png" + GetGlobalRatingMethod="@((id, action) => MediaWebAPIService.GetMediaRating(id, action))" + GetUserRatingMethod="@((id, userId, successAction, notfoundAction) => MediaWebAPIService.GetMediaRatingByUser(id, userId, successAction, notfoundAction))" + PutRatingMethod="@((id, request) => MediaWebAPIService.PutMediaRating(id, request))" + DeleteRatingMethod="@(id => MediaWebAPIService.DeleteMediaRating(id))"/> + PictureDownloadingTask="@((id, action) => PersonsWebAPIService.GetPersonPhoto(id, action))" + PosterPlaceholder="/assets/person_poster.png" + GetGlobalRatingMethod="@((id, action) => PersonsWebAPIService.GetPersonGlobalRating(id, action))"/>
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj b/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj index eb28837..9601cf7 100644 --- a/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj +++ b/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj @@ -51,4 +51,9 @@ + + + + + diff --git a/WatchIt.Website/WatchIt.Website/appsettings.json b/WatchIt.Website/WatchIt.Website/appsettings.json index 2142c32..2e65e00 100644 --- a/WatchIt.Website/WatchIt.Website/appsettings.json +++ b/WatchIt.Website/WatchIt.Website/appsettings.json @@ -101,7 +101,9 @@ "GetPersonAllActorRoles": "/{0}/roles/actor", "PostPersonActorRole": "/{0}/roles/actor", "GetPersonAllCreatorRoles": "/{0}/roles/creator", - "PostPersonCreatorRole": "/{0}/roles/creator" + "PostPersonCreatorRole": "/{0}/roles/creator", + "GetPersonGlobalRating": "/{0}/rating", + "GetPersonUserRating": "/{0}/rating/{1}" }, "Roles": { "Base": "/roles", diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/css/general.css b/WatchIt.Website/WatchIt.Website/wwwroot/css/general.css index 4223bee..319ee59 100644 --- a/WatchIt.Website/WatchIt.Website/wwwroot/css/general.css +++ b/WatchIt.Website/WatchIt.Website/wwwroot/css/general.css @@ -53,6 +53,19 @@ body, html { width: 1%; } +.metadata-pill { + border-color: gray; + border-width: 2px; + border-radius: 30px; + border-style: solid; + + padding: 2px 10px; +} + +.metadata-pill-container { + gap: 1rem; +} +