diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountQueryParameters.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountQueryParameters.cs new file mode 100644 index 0000000..88221db --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountQueryParameters.cs @@ -0,0 +1,67 @@ +using Microsoft.AspNetCore.Mvc; +using WatchIt.Common.Query; + +namespace WatchIt.Common.Model.Accounts; + +public class AccountQueryParameters : QueryParameters +{ + #region PROPERTIES + + [FromQuery(Name = "username")] + public string? Username { get; set; } + + [FromQuery(Name = "email")] + public string? Email { get; set; } + + [FromQuery(Name = "description")] + public string? Description { get; set; } + + [FromQuery(Name = "gender_id")] + public short? GenderId { get; set; } + + [FromQuery(Name = "last_active")] + public DateOnly? LastActive { get; set; } + + [FromQuery(Name = "last_active_from")] + public DateOnly? LastActiveFrom { get; set; } + + [FromQuery(Name = "last_active_to")] + public DateOnly? LastActiveTo { get; set; } + + [FromQuery(Name = "creation_date")] + public DateOnly? CreationDate { get; set; } + + [FromQuery(Name = "creation_date_from")] + public DateOnly? CreationDateFrom { get; set; } + + [FromQuery(Name = "creation_date_to")] + public DateOnly? CreationDateTo { get; set; } + + [FromQuery(Name = "is_admin")] + public bool? IsAdmin { get; set; } + + #endregion + + + + #region PRIVATE METHODS + + protected override bool IsMeetingConditions(AccountResponse item) => + ( + TestStringWithRegex(item.Username, Username) + && + TestStringWithRegex(item.Email, Email) + && + TestStringWithRegex(item.Description, Description) + && + Test(item.Gender?.Id, GenderId) + && + TestComparable(item.LastActive, LastActive, LastActiveFrom, LastActiveTo) + && + TestComparable(item.CreationDate, CreationDate, CreationDateFrom, CreationDateTo) + && + Test(item.IsAdmin, IsAdmin) + ); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountResponse.cs index fb3bc77..8ab8756 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountResponse.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountResponse.cs @@ -1,13 +1,28 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; using WatchIt.Common.Model.Genders; +using WatchIt.Common.Query; namespace WatchIt.Common.Model.Accounts; -public class AccountResponse : Account +public class AccountResponse : Account, IQueryOrderable { #region PROPERTIES + [JsonIgnore] + public static IDictionary> OrderableProperties { get; } = new Dictionary> + { + { "id", x => x.Id }, + { "username", x => x.Username }, + { "email", x => x.Email }, + { "description", x => x.Description }, + { "gender", x => x.Gender.Name }, + { "last_active", x => x.LastActive }, + { "creation_date", x => x.CreationDate }, + { "is_admin", x => x.IsAdmin } + }; + + [JsonPropertyName("id")] public required long Id { get; set; } diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs index 56ea67a..a57a424 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs @@ -94,11 +94,16 @@ public class AccountsController(IAccountsControllerService accountsControllerSer #region Info - [HttpGet("{id}/info")] + [HttpGet] + [AllowAnonymous] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetAccounts(AccountQueryParameters query) => await accountsControllerService.GetAccounts(query); + + [HttpGet("{id}")] [AllowAnonymous] [ProducesResponseType(typeof(AccountResponse), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetAccountInfo([FromRoute]long id) => await accountsControllerService.GetAccountInfo(id); + public async Task GetAccount([FromRoute]long id) => await accountsControllerService.GetAccount(id); [HttpPut("profile_info")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs index cb6a052..14fc4ba 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs @@ -238,7 +238,15 @@ public class AccountsControllerService( #region Info - public async Task GetAccountInfo(long id) + public async Task GetAccounts(AccountQueryParameters query) + { + IEnumerable accounts = await database.Accounts.ToListAsync(); + IEnumerable accountsData = accounts.Select(x => new AccountResponse(x)); + accountsData = query.PrepareData(accountsData); + return RequestResult.Ok(accountsData); + } + + public async Task GetAccount(long id) { Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == id); if (account is null) diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs index 0728d3e..bb03dc5 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs @@ -19,7 +19,8 @@ public interface IAccountsControllerService Task GetAccountProfileBackground(long id); Task PutAccountProfileBackground(AccountProfileBackgroundRequest data); Task DeleteAccountProfileBackground(); - Task GetAccountInfo(long id); + Task GetAccounts(AccountQueryParameters query); + Task GetAccount(long id); Task PutAccountProfileInfo(AccountProfileInfoRequest data); Task PatchAccountUsername(AccountUsernameRequest data); Task PatchAccountEmail(AccountEmailRequest data); diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs index 49e0344..df8649f 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs @@ -150,9 +150,22 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig .ExecuteAction(); } - public async Task GetAccountInfo(long id, Action? successAction = null, Action? notFoundAction = null) + public async Task GetAccounts(AccountQueryParameters query, Action>? successAction = null) { - string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountInfo, id); + string url = GetUrl(EndpointsConfiguration.Accounts.GetAccounts); + HttpRequest request = new HttpRequest(HttpMethodType.Get, url) + { + Query = query + }; + + HttpResponse response = await httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .ExecuteAction(); + } + + public async Task GetAccount(long id, Action? successAction = null, Action? notFoundAction = null) + { + string url = GetUrl(EndpointsConfiguration.Accounts.GetAccount, id); HttpRequest request = new HttpRequest(HttpMethodType.Get, url); HttpResponse response = await httpClientService.SendRequestAsync(request); diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs index 84b48f5..09543c6 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs @@ -18,7 +18,8 @@ public interface IAccountsClientService Task GetAccountProfileBackground(long id, Action? successAction = null, Action>? badRequestAction = null, Action? notFoundAction = null); Task PutAccountProfileBackground(AccountProfileBackgroundRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); Task DeleteAccountProfileBackground(Action? successAction = null, Action? unauthorizedAction = null); - Task GetAccountInfo(long id, Action? successAction = null, Action? notFoundAction = null); + Task GetAccounts(AccountQueryParameters query, Action>? successAction = null); + Task GetAccount(long id, Action? successAction = null, Action? notFoundAction = null); Task PutAccountProfileInfo(AccountProfileInfoRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); Task PatchAccountUsername(AccountUsernameRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); Task PatchAccountEmail(AccountEmailRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs index fae7f58..eae345a 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs @@ -13,7 +13,8 @@ public class Accounts public string GetAccountProfileBackground { get; set; } public string PutAccountProfileBackground { get; set; } public string DeleteAccountProfileBackground { get; set; } - public string GetAccountInfo { get; set; } + public string GetAccounts { get; set; } + public string GetAccount { get; set; } public string PutAccountProfileInfo { get; set; } public string PatchAccountUsername { get; set; } public string PatchAccountEmail { get; set; } diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/UsersSearchResultPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/UsersSearchResultPanelComponent.razor new file mode 100644 index 0000000..cb5dd8e --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/UsersSearchResultPanelComponent.razor @@ -0,0 +1,77 @@ +@using Blazorise.Extensions +@using WatchIt.Website.Components.Pages.SearchPage.Subcomponents + + + +
+
+
+
+

Users

+
+
+ @if (_loaded) + { + if (!_items.IsNullOrEmpty()) + { + for (int i = 0; i < _items.Count; i++) + { + if (i > 0) + { +
+
+
+
+
+ } +
+
+ @{ + int iCopy = i; + } + +
+
+ } + if (!_allItemsLoaded) + { +
+
+
+ +
+
+
+ } + } + else + { +
+
+
+ No items found +
+
+
+ } + } + else + { +
+
+ +
+
+ } +
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/UsersSearchResultPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/UsersSearchResultPanelComponent.razor.cs new file mode 100644 index 0000000..42b7197 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Panels/UsersSearchResultPanelComponent.razor.cs @@ -0,0 +1,97 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Accounts; +using WatchIt.Website.Services.Client.Accounts; + +namespace WatchIt.Website.Components.Pages.SearchPage.Panels; + +public partial class UsersSearchResultPanelComponent : ComponentBase +{ + #region SERVICES + + [Inject] private IAccountsClientService AccountsClientService { get; set; } = default!; + + #endregion + + + + #region PARAMETERS + + [Parameter] public required string Query { get; set; } + + #endregion + + + + #region FIELDS + + private bool _loaded; + + private AccountQueryParameters _query = new AccountQueryParameters + { + First = 5 + }; + + private List _items = []; + private bool _allItemsLoaded; + private bool _itemsLoading; + + #endregion + + + + #region PRIVATE METHODS + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + // INIT + _query.Username = Query; + + List endTasks = new List(); + + // STEP 0 + endTasks.AddRange( + [ + AccountsClientService.GetAccounts(_query, data => + { + _items.AddRange(data); + if (data.Count() < 5) + { + _allItemsLoaded = true; + } + else + { + _query.After = 5; + } + }) + ]); + + // END + await Task.WhenAll(endTasks); + + _loaded = true; + StateHasChanged(); + } + } + + private async Task DownloadItems() + { + _itemsLoading = true; + await AccountsClientService.GetAccounts(_query, data => + { + _items.AddRange(data); + if (data.Count() < 5) + { + _allItemsLoaded = true; + } + else + { + _query.After += 5; + } + _itemsLoading = false; + }); + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Subcomponents/UserSearchResultItemComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Subcomponents/UserSearchResultItemComponent.razor new file mode 100644 index 0000000..dc936c9 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Subcomponents/UserSearchResultItemComponent.razor @@ -0,0 +1,7 @@ + +
+ +

@(Item.Username)

+
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Subcomponents/UserSearchResultItemComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Subcomponents/UserSearchResultItemComponent.razor.cs new file mode 100644 index 0000000..b81a075 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/SearchPage/Subcomponents/UserSearchResultItemComponent.razor.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Accounts; + +namespace WatchIt.Website.Components.Pages.SearchPage.Subcomponents; + +public partial class UserSearchResultItemComponent : ComponentBase +{ + #region PROPERTIES + + [Parameter] public required AccountResponse Item { get; set; } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs index 00657c8..d0df615 100644 --- a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs @@ -91,7 +91,7 @@ public partial class MainLayout : LayoutComponentBase if (_user is not null) { - await AccountsClientService.GetAccountInfo(_user.Id, data => _accountData = data); + await AccountsClientService.GetAccount(_user.Id, data => _accountData = data); } _loaded = true; @@ -121,7 +121,7 @@ public partial class MainLayout : LayoutComponentBase if (!string.IsNullOrWhiteSpace(_searchbarText)) { string query = WebUtility.UrlEncode(_searchbarText); - NavigationManager.NavigateTo($"/search/{query}"); + NavigationManager.NavigateTo($"/search/{query}", true); } } diff --git a/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor b/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor index 7e30b8f..afcbd75 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor @@ -1,3 +1,4 @@ +@using System.Net @using WatchIt.Common.Model.Movies @using WatchIt.Common.Model.Persons @using WatchIt.Common.Model.Series @@ -15,7 +16,7 @@

- Search results for phrase: "@(DecodedQuery)" + Search results for phrase: "@(WebUtility.UrlDecode(Query))"

@@ -27,7 +28,7 @@ NameSource="@(item => item.Title)" AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)" RatingSource="@(item => item.Rating)" - Query="@(new MovieQueryParameters { Title = DecodedQuery, OrderBy = "rating.count" })" + Query="@(new MovieQueryParameters { Title = WebUtility.UrlDecode(Query), OrderBy = "rating.count" })" ItemDownloadingTask="@(MoviesClientService.GetAllMovies)" PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))" PosterPlaceholder="/assets/media_poster.png" @@ -43,7 +44,7 @@ NameSource="@(item => item.Title)" AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)" RatingSource="@(item => item.Rating)" - Query="@(new SeriesQueryParameters { Title = DecodedQuery, OrderBy = "rating.count" })" + Query="@(new SeriesQueryParameters { Title = WebUtility.UrlDecode(Query), OrderBy = "rating.count" })" ItemDownloadingTask="@(SeriesClientService.GetAllSeries)" PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))" PosterPlaceholder="/assets/media_poster.png" @@ -58,9 +59,10 @@ IdSource="@(item => item.Id)" NameSource="@(item => item.Name)" RatingSource="@(item => item.Rating)" - Query="@(new PersonQueryParameters { Name = DecodedQuery, OrderBy = "rating.count" })" + Query="@(new PersonQueryParameters { Name = WebUtility.UrlDecode(Query), OrderBy = "rating.count" })" ItemDownloadingTask="@(PersonsClientService.GetAllPersons)" PictureDownloadingTask="@((id, action) => PersonsClientService.GetPersonPhoto(id, action))" PosterPlaceholder="/assets/person_poster.png" GetGlobalRatingMethod="@((id, action) => PersonsClientService.GetPersonGlobalRating(id, action))"/> + \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs index 4b29a60..cb179b4 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs @@ -58,7 +58,7 @@ public partial class UserEditPage : ComponentBase await Task.WhenAll( [ - AccountsClientService.GetAccountInfo(user.Id, data => _accountData = data), + AccountsClientService.GetAccount(user.Id, data => _accountData = data), AccountsClientService.GetAccountProfileBackground(user.Id, data => Layout.BackgroundPhoto = data) ]); StateHasChanged(); diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor.cs index 95087f0..41cec0a 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor.cs @@ -91,7 +91,7 @@ public partial class UserPage : ComponentBase Id = user.Id; } - await AccountsClientService.GetAccountInfo(Id.Value, data => _accountData = data); + await AccountsClientService.GetAccount(Id.Value, data => _accountData = data); _owner = Id.Value == user?.Id; } diff --git a/WatchIt.Website/WatchIt.Website/appsettings.json b/WatchIt.Website/WatchIt.Website/appsettings.json index 943633b..9f2429a 100644 --- a/WatchIt.Website/WatchIt.Website/appsettings.json +++ b/WatchIt.Website/WatchIt.Website/appsettings.json @@ -27,7 +27,8 @@ "GetAccountProfileBackground": "/{0}/profile_background", "PutAccountProfileBackground": "/profile_background", "DeleteAccountProfileBackground": "/profile_background", - "GetAccountInfo": "/{0}/info", + "GetAccounts": "", + "GetAccount": "/{0}", "PutAccountProfileInfo": "/profile_info", "PatchAccountUsername": "/username", "PatchAccountEmail": "/email",