From 225e8ba140349d9279e88255d3bf1c1ebfda8fcf Mon Sep 17 00:00:00 2001 From: Egamorf Date: Sun, 13 Apr 2025 17:21:15 +0200 Subject: [PATCH] Add frontend creation game (#13) Reviewed-on: https://gitea.egamorf.com/PRJ-Game-Ideas/game-ideas/pulls/13 --- .../Layouts/MainLayout.razor | 10 +- .../Layouts/MainLayout.razor.css | 5 +- .../Games/Components/GameCreationForm.razor | 69 ++++++++++ .../Components/GameCreationForm.razor.cs | 56 ++++++++ .../Components/GameCreationForm.razor.css | 68 ++++++++++ .../Components/GameCreationForm.razor.js | 5 + .../Games/Filter/AdvancedGameFilter.razor | 15 +-- .../Games/Filter/AdvancedGameFilter.razor.cs | 52 -------- .../Pages/Games/Filter/GameFilter.razor | 20 ++- .../Pages/Games/Filter/GameFilter.razor.cs | 31 +---- .../Pages/Games/Filter/GameFilterParams.cs | 11 +- .../Pages/Games/GameBase.razor | 8 +- .../Pages/Games/GameBase.razor.cs | 19 +++ .../Pages/Games/Gateways/GameGateway.cs | 24 ++++ .../Pages/Games/Gateways/IGameGateway.cs | 8 ++ .../Pages/Games/Header/GameHeader.razor | 8 +- .../Pages/Games/Header/GameHeader.razor.cs | 7 +- .../Client/GameIdeas.BlazorApp/Program.cs | 4 +- .../Services/AuthentificationService.cs | 21 --- .../Services/HttpClientService.cs | 125 ++++++++++++++++++ .../Services/IHttpClientService.cs | 12 ++ .../Components/Account/AccountSettings.razor | 2 +- .../Account/AccountSettings.razor.cs | 16 +-- .../BackdropFilter/BackdropFilter.razor.css | 2 +- .../BackdropFilter/BackdropFilter.razor.js | 2 +- .../Shared/Components/Popup/Popup.razor | 18 +++ .../Shared/Components/Popup/Popup.razor.cs | 52 ++++++++ .../Shared/Components/Popup/Popup.razor.css | 41 ++++++ .../Components/Search/SearchInput.razor | 6 +- .../Components/Search/SearchInput.razor.cs | 6 +- .../Components/Search/SearchInput.razor.css | 1 + .../Components/Select/Models/SelectElement.cs | 6 +- .../Components/Select/Models/SelectTheme.cs | 3 +- .../Select/MultipleSelectList.razor.css | 14 +- .../Shared/Components/Select/SelectList.razor | 9 +- .../Components/Select/SelectList.razor.cs | 17 +-- .../Shared/Components/Slider/Slider.razor | 11 ++ .../Shared/Components/Slider/Slider.razor.cs | 30 +++++ .../Shared/Components/Slider/Slider.razor.css | 89 +++++++++++++ .../Shared/Components/Slider/SliderParams.cs | 8 ++ .../Components/SliderRange/SliderRange.razor | 12 +- .../SliderRange/SliderRange.razor.cs | 18 ++- .../SliderRange/SliderRangeParams.cs | 2 - .../Shared/Constants/Endpoints.cs | 14 ++ .../Shared}/Constants/Icons.cs | 23 ++-- .../Exceptions/CategoryNotFoundException.cs | 3 + .../GameIdeas.BlazorApp/wwwroot/css/app.css | 1 - .../GameIdeas.BlazorApp/wwwroot/index.html | 1 + .../CreateStaticResourceKey.cs | 34 ++++- .../GameIdeas.Shared/Dto/CategoriesDto.cs | 10 ++ src/GameIdeas/GameIdeas.Shared/Dto/GameDto.cs | 4 +- .../Controllers/CategoryController.cs | 26 ++++ .../Controllers/GameController.cs | 4 +- .../GameIdeas.WebAPI/Files/GameIdeas.fr.json | 17 ++- .../Server/GameIdeas.WebAPI/Program.cs | 8 +- .../Services/CategoryService.cs | 28 ++++ .../GameIdeas.WebAPI/Services/GameService.cs | 3 +- .../Services/Interfaces/ICategoryService.cs | 8 ++ .../Services/Interfaces/IGameService.cs | 12 ++ .../GameIdeas.WebAPI/Services/UserService.cs | 5 - 60 files changed, 913 insertions(+), 231 deletions(-) create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.css create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.js create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs delete mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Services/AuthentificationService.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Services/HttpClientService.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Services/IHttpClientService.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Popup/Popup.razor create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Popup/Popup.razor.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Popup/Popup.razor.css create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Slider/Slider.razor create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Slider/Slider.razor.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Slider/Slider.razor.css create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Slider/SliderParams.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs rename src/GameIdeas/{GameIdeas.Shared => Client/GameIdeas.BlazorApp/Shared}/Constants/Icons.cs (62%) create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/CategoryNotFoundException.cs create mode 100644 src/GameIdeas/GameIdeas.Shared/Dto/CategoriesDto.cs create mode 100644 src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/CategoryController.cs create mode 100644 src/GameIdeas/Server/GameIdeas.WebAPI/Services/CategoryService.cs create mode 100644 src/GameIdeas/Server/GameIdeas.WebAPI/Services/Interfaces/ICategoryService.cs create mode 100644 src/GameIdeas/Server/GameIdeas.WebAPI/Services/Interfaces/IGameService.cs delete mode 100644 src/GameIdeas/Server/GameIdeas.WebAPI/Services/UserService.cs diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Layouts/MainLayout.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Layouts/MainLayout.razor index 81e4bfe..8b1aa4e 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Layouts/MainLayout.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Layouts/MainLayout.razor @@ -5,8 +5,8 @@ @Body - - - - -
\ No newline at end of file +
+ + + +
\ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Layouts/MainLayout.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Layouts/MainLayout.razor.css index 407f5bd..60ead01 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Layouts/MainLayout.razor.css +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Layouts/MainLayout.razor.css @@ -37,10 +37,11 @@ } .background { - height: 100%; - width: 100%; + height: 100vh; + width: 100vw; background: var(--background); position: fixed; + overflow: hidden; top: 0; left: 0; z-index: var(--index-background); diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor new file mode 100644 index 0000000..7354efa --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor @@ -0,0 +1,69 @@ +@using GameIdeas.BlazorApp.Shared.Components.Select +@using GameIdeas.BlazorApp.Shared.Components.Select.Models +@using GameIdeas.BlazorApp.Shared.Components.Slider +@using GameIdeas.Shared.Dto + + +
+
+
+
@ResourcesKey.Title :
+ +
+
+
@ResourcesKey.ReleaseDate :
+ +
+
+
@ResourcesKey.StorageSizeMo :
+ +
+
+
@ResourcesKey.Developers :
+ +
+
+
@ResourcesKey.Publishers :
+ +
+
+
+
+
@ResourcesKey.Interest :
+
+ +
+
+
+
@ResourcesKey.Properties :
+ +
+
+
@ResourcesKey.Genres :
+ + +
+
+
@ResourcesKey.Platforms :
+ +
+
+
+
+
@ResourcesKey.Description :
+ +
+
+ + +
+
\ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.cs new file mode 100644 index 0000000..97c0854 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.cs @@ -0,0 +1,56 @@ +using GameIdeas.BlazorApp.Pages.Games.Gateways; +using GameIdeas.BlazorApp.Shared.Components.Popup; +using GameIdeas.BlazorApp.Shared.Components.Select.Models; +using GameIdeas.BlazorApp.Shared.Components.Slider; +using GameIdeas.Shared.Dto; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Forms; +using Microsoft.JSInterop; + +namespace GameIdeas.BlazorApp.Pages.Games.Components; + +public partial class GameCreationForm +{ + [Inject] private IJSRuntime Js { get; set; } = default!; + [Inject] private IGameGateway GameGateway { get; set; } = default!; + [CascadingParameter] private Popup? Popup { get; set; } + + private GameDto GameDto = new(); + private CategoriesDto CategoriesDto = new(); + private EditContext? EditContext; + private readonly SelectListTheme SelectListTheme = SelectListTheme.Creation; + private readonly SliderParams SliderParams = new() { Gap = 1, Min = 1, Max = 5 }; + + protected override async Task OnInitializedAsync() + { + EditContext = new(GameDto); + CategoriesDto = await GameGateway.FetchCategories(); + + if (Popup != null) + { + Popup.StateChanged += async (_, isOpen) => await HandlePopupStateChanged(); + } + + await base.OnInitializedAsync(); + } + + private async Task HandlePopupStateChanged() + { + await Js.InvokeVoidAsync("resizeGameForm"); + } + + private void HandleOnCancel() + { + Popup?.Close(); + } + + private async Task HandleOnSubmit(EditContext args) + { + if (EditContext?.Validate() == false) + { + return; + } + + + } +} \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.css new file mode 100644 index 0000000..b28e0a8 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.css @@ -0,0 +1,68 @@ +.game-form { + display: flex; + flex-direction: row; + gap: 20px; +} + +.container { + display: flex; + flex-direction: column; + gap: 8px; +} + +.input-game { + display: grid; + grid-template-columns: auto 200px; + grid-gap: 8px; + height: 24px; +} + +.label { + text-wrap: nowrap; + align-content: center; + font-weight: bold; + grid-column: 1; +} + +input { + width: 100%; + background: var(--input-secondary); + border: solid 1px var(--input-selected); + outline: none; + border-radius: var(--small-radius); + grid-column: 2; + box-sizing: border-box; + color: var(--white); +} + + input[type="date"]::-webkit-calendar-picker-indicator { + filter: invert(1); + cursor: pointer; + } + +.description-container { + margin-top: 8px; + display: grid; + grid-template-columns: auto 1fr; + grid-gap: 8px; +} + +.description { + min-height: 140px; +} + +#label-description { + text-wrap: nowrap; + align-content: center; + font-weight: bold; + height: 24px; +} + +.slider { + padding: 0 20px; + align-content: center; +} + +input[type="number"]::-webkit-inner-spin-button { + -webkit-appearance: none; +} \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.js b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.js new file mode 100644 index 0000000..ab5b850 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.js @@ -0,0 +1,5 @@ +window.resizeGameForm = () => { + const label = document.getElementById('first-label'); + const targetLabel = document.getElementById('label-description'); + targetLabel.style.width = window.getComputedStyle(label).getPropertyValue("width"); +}; \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor index 5dab57c..8c1dd8f 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor @@ -5,46 +5,39 @@
diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor.cs index dfa5e1b..cb74ed4 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor.cs @@ -7,56 +7,4 @@ public partial class AdvancedGameFilter { [Parameter] public GameFilterParams? GameFilterParams { get; set; } [Parameter] public EventCallback GameFilterParamsChanged { get; set; } - - - private readonly IEnumerable> Plateforms = [ - new() { Item = "Steam", Label = "Steam" }, - new() { Item = "GOG", Label = "GOG" }, - new() { Item = "Epic games", Label = "Epic games" }, - new() { Item = "Ubisoft", Label = "Ubisoft" }, - ]; - - private readonly IEnumerable> Genres = [ - new() { Item = "Rogue Like", Label = "Rogue Like" }, - new() { Item = "Aventure", Label = "Aventure" }, - new() { Item = "RPG", Label = "RPG" }, - new() { Item = "Fast FPS", Label = "Fast FPS" }, - ]; - - private readonly IEnumerable> Publishers = [ - new() { Item = "Electronic Arts", Label = "Electronic Arts" }, - new() { Item = "Ubisoft", Label = "Ubisoft" }, - new() { Item = "Activision Blizzard", Label = "Activision Blizzard" }, - new() { Item = "Bethesda", Label = "Bethesda" } - ]; - - private readonly IEnumerable> Developers = [ - new() { Item = "CD Projekt Red", Label = "CD Projekt Red" }, - new() { Item = "Naughty Dog", Label = "Naughty Dog" }, - new() { Item = "Rockstar Games", Label = "Rockstar Games" }, - new() { Item = "FromSoftware", Label = "FromSoftware" }, - ]; - - private readonly IEnumerable> StorageSizes = [ - new() { Item = "1 Go", Label = "1 Go" }, - new() { Item = "10 Go", Label = "10 Go" }, - new() { Item = "50 Go", Label = "50 Go" }, - new() { Item = "100 Go", Label = "100 Go" }, - ]; - - private readonly IEnumerable> LastModifiedDates = [ - new() { Item = "2023-12-15", Label = "15 Décembre 2023" }, - new() { Item = "2024-01-20", Label = "20 Janvier 2024" }, - new() { Item = "2024-02-05", Label = "5 Février 2024" }, - new() { Item = "2024-03-10", Label = "10 Mars 2024" }, - ]; - - private readonly IEnumerable> ReleaseDates = [ - new() { Item = "2023-11-11", Label = "11 Novembre 2023" }, - new() { Item = "2024-01-25", Label = "25 Janvier 2024" }, - new() { Item = "2024-03-03", Label = "3 Mars 2024" }, - new() { Item = "2024-04-15", Label = "15 Avril 2024" }, - ]; - - } \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilter.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilter.razor index 06a2942..414cda9 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilter.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilter.razor @@ -4,13 +4,13 @@ @using GameIdeas.BlazorApp.Shared.Components.SliderRange @using GameIdeas.BlazorApp.Shared.Models @using GameIdeas.Shared.Dto +@using GameIdeas.Shared.Enum
-
@@ -38,24 +38,22 @@
+ @bind-Max=GameFilterParams.MaxRating + @bind-Min=GameFilterParams.MinRating />
diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilter.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilter.razor.cs index 49d7461..8ffe951 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilter.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilter.razor.cs @@ -15,33 +15,19 @@ public partial class GameFilter [Parameter] public DisplayType DisplayType { get; set; } [Parameter] public EventCallback DisplayTypeChanged { get; set; } - private readonly IEnumerable>> SortTypes = [ - new() { Item = _ => SortType.Ascending, Label = "Ascendant", IsSelected = true }, - new() { Item = _ => SortType.Descending, Label = "Descendant" } + private readonly IEnumerable> SortTypes = [ + new(SortType.Ascending, "Ascendant") { IsSelected = true }, + new(SortType.Descending, "Descendant") ]; private readonly IEnumerable>> GameProperties = [ - new() { Item = game => game?.Title, Label = "Nom", IsSelected = true }, - new() { Item = game => game?.ReleaseDate, Label = "Date de parution" } - ]; - - private readonly IEnumerable> Plateforms = [ - new() { Item = "Steam", Label = "Steam" }, - new() { Item = "GOG", Label = "GOG" }, - new() { Item = "Epic games", Label = "Epic games" }, - new() { Item = "Ubisoft", Label = "Ubisoft" }, - ]; - - private readonly IEnumerable> Genres = [ - new() { Item = "Rogue Like", Label = "Rogue Like" }, - new() { Item = "Aventure", Label = "Aventure" }, - new() { Item = "RPG", Label = "RPG" }, - new() { Item = "Fast FPS", Label = "Fast FPS" }, + new(game => game?.Title, "Nom") { IsSelected = true }, + new(game => game?.ReleaseDate, "Date de parution"), ]; private EditContext? EditContext; private readonly SliderRangeParams SliderRangeParams = - new() { Min = 1, ValueMin = 1, ValueMax = 5, Max = 5 }; + new() { Min = 1, Max = 5 }; protected override void OnInitialized() { @@ -52,11 +38,6 @@ public partial class GameFilter }; } - private void HandleSortTypeChanged(Func getHeader) - { - GameFilterParams.SortType = (SortType?)getHeader(null) ?? SortType.Ascending; - } - private async Task HandleDisplayClicked(DisplayType displayType) { DisplayType = displayType; diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilterParams.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilterParams.cs index 4290ab3..d95d5c4 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilterParams.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilterParams.cs @@ -5,16 +5,17 @@ namespace GameIdeas.BlazorApp.Pages.Games.Filter; public class GameFilterParams { - public SortType? SortType { get; set; } + public SortType SortType { get; set; } = SortType.Ascending; public Func? SortProperty { get; set; } public string? SearchName { get; set; } - public IEnumerable? Plateforms { get; set; } - public IEnumerable? Genres { get; set; } + public IEnumerable? Platforms { get; set; } + public IEnumerable? Properties { get; set; } + public IEnumerable? Tags { get; set; } public IEnumerable? Publishers { get; set; } public IEnumerable? Developers { get; set; } public IEnumerable? StorageSizes { get; set; } public IEnumerable? LastModification { get; set; } public IEnumerable? ReleaseDates { get; set; } - public int MaxRating { get; set; } - public int MinRating { get; set; } + public int MaxRating { get; set; } = 5; + public int MinRating { get; set; } = 1; } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/GameBase.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/GameBase.razor index dd434a7..b72d4b0 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/GameBase.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/GameBase.razor @@ -1,24 +1,28 @@ @page "/Games" @using GameIdeas.BlazorApp.Layouts +@using GameIdeas.BlazorApp.Pages.Games.Components @using GameIdeas.BlazorApp.Pages.Games.Filter @using GameIdeas.BlazorApp.Pages.Games.Header @using GameIdeas.BlazorApp.Shared.Components +@using GameIdeas.BlazorApp.Shared.Components.Popup @using GameIdeas.Resources @layout MainLayout @ResourcesKey.GamesIdeas - +
-
+ + + diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/GameBase.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/GameBase.razor.cs index 60ab45d..92aff27 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/GameBase.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/GameBase.razor.cs @@ -1,4 +1,5 @@ using GameIdeas.BlazorApp.Pages.Games.Filter; +using GameIdeas.BlazorApp.Shared.Components.Popup; using GameIdeas.BlazorApp.Shared.Models; namespace GameIdeas.BlazorApp.Pages.Games; @@ -7,4 +8,22 @@ public partial class GameBase () { private DisplayType DisplayType = DisplayType.List; private GameFilterParams GameFilterParams = new(); + private Popup? ManualAddPopup; + private void HandleAddClicked(AddType addType) + { + switch (addType) + { + case AddType.Manual: + ManualAddPopup?.Open(); + break; + case AddType.Auto: + break; + default: + break; + } + } + private void HandleBackdropManualAddClicked() + { + ManualAddPopup?.Close(); + } } \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs new file mode 100644 index 0000000..92af68b --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs @@ -0,0 +1,24 @@ +using GameIdeas.BlazorApp.Services; +using GameIdeas.BlazorApp.Shared.Constants; +using GameIdeas.BlazorApp.Shared.Exceptions; +using GameIdeas.Resources; +using GameIdeas.Shared.Dto; + +namespace GameIdeas.BlazorApp.Pages.Games.Gateways; + +public class GameGateway(IHttpClientService httpClientService) : IGameGateway +{ + public async Task FetchCategories() + { + try + { + var result = await httpClientService.FetchDataAsync(Endpoints.Category.AllCategories); + + return result ?? throw new InvalidOperationException(ResourcesKey.ErrorFetchCategories); + } + catch (Exception) + { + throw new CategoryNotFoundException(ResourcesKey.ErrorFetchCategories); + } + } +} diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs new file mode 100644 index 0000000..7d71440 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs @@ -0,0 +1,8 @@ +using GameIdeas.Shared.Dto; + +namespace GameIdeas.BlazorApp.Pages.Games.Gateways; + +public interface IGameGateway +{ + Task FetchCategories(); +} diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor index c222be4..515e557 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor @@ -22,11 +22,9 @@
- +
diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs index a854eaf..936b5bc 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs @@ -1,4 +1,5 @@ using GameIdeas.BlazorApp.Shared.Components.Account; +using GameIdeas.BlazorApp.Shared.Components.Select; using GameIdeas.BlazorApp.Shared.Components.Select.Models; using GameIdeas.BlazorApp.Shared.Models; using GameIdeas.Resources; @@ -13,11 +14,12 @@ public partial class GameHeader : ComponentBase private readonly IEnumerable> SelectElements = [ - new SelectElement { Item = AddType.Manual, Label = ResourcesKey.ManualAdd }, - new SelectElement { Item = AddType.Auto, Label = ResourcesKey.AutoAdd } + new SelectElement(AddType.Manual, ResourcesKey.ManualAdd), + new SelectElement (AddType.Auto, ResourcesKey.AutoAdd) ]; private AccountSettings? AccountSettings; + private SelectList? SelectListAdd; private void HandleIconClicked() { @@ -26,6 +28,7 @@ public partial class GameHeader : ComponentBase private async Task HandleAddTypeClickedAsync(AddType value) { + SelectListAdd?.Close(); await AddTypeChanged.InvokeAsync(value); } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs index e35dffa..98b893b 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs @@ -1,5 +1,6 @@ using System.Net.Http.Json; using GameIdeas.BlazorApp; +using GameIdeas.BlazorApp.Pages.Games.Gateways; using GameIdeas.BlazorApp.Services; using GameIdeas.Resources; using Microsoft.AspNetCore.Components.Web; @@ -22,7 +23,8 @@ builder.Services.AddHttpClient( client.Timeout = TimeSpan.FromMinutes(3); }); -builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Services/AuthentificationService.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Services/AuthentificationService.cs deleted file mode 100644 index 380b679..0000000 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Services/AuthentificationService.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace GameIdeas.BlazorApp.Services; - -public class AuthentificationService -{ - private bool isLogin; - - public bool IsLogin - { - get { return isLogin; } - } - - public void Login() - { - isLogin = true; - } - - public void Logout() - { - isLogin = false; - } -} diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Services/HttpClientService.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Services/HttpClientService.cs new file mode 100644 index 0000000..f0c39ac --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Services/HttpClientService.cs @@ -0,0 +1,125 @@ +using GameIdeas.Resources; +using System.Net.Http.Headers; +using System.Text.Json.Serialization; +using System.Text.Json; +using System.Text; + +namespace GameIdeas.BlazorApp.Services; + +public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFactory loggerFactory) : IHttpClientService +{ + private readonly HttpClient httpClient = httpClientFactory.CreateClient("GameIdeas.WebAPI"); + private readonly ILogger logger = loggerFactory.CreateLogger(); + + private readonly JsonSerializerOptions _optionsCamelCase = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault + }; + + private readonly JsonSerializerOptions _optionsCaseInsensitive = new() + { + PropertyNameCaseInsensitive = true, + ReferenceHandler = ReferenceHandler.Preserve + }; + + public async Task PostAsync(string url, object data) + { + var jsonContent = JsonSerializer.Serialize(data, _optionsCamelCase); + var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); + var response = await httpClient.PostAsync(url, content); + + return await GetResultValue(response, ResourcesKey.ErrorWhenPostingData); + } + + public async Task PutAsync(string url, object data) + { + var jsonContent = JsonSerializer.Serialize(data, _optionsCamelCase); + var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); + var response = await httpClient.PutAsync(url, content); + + return await GetResultValue(response, ResourcesKey.ErrorWhenPutingData); + } + + public async Task DeleteAsync(string? url) + { + var response = await httpClient.DeleteAsync(url); + + return await GetResultValue(response, ResourcesKey.ErrorWhenDeletingData); + } + + public async Task FetchDataAsync(string? url) + { + var response = await httpClient.GetAsync(url); + + return await GetResultValue(response, ResourcesKey.ErrorWhenFetchingData); + } + + public async Task FetchBytesAsync(string? url) + { + var response = await httpClient.GetAsync(url); + + if (response.IsSuccessStatusCode) + { + return await response.Content.ReadAsByteArrayAsync(); + } + + throw new HttpRequestException( + $"{ResourcesKey.ErrorWhenFetchingData} + StatusCode: {response.StatusCode} " + + $"+ Reason: {response.ReasonPhrase}"); + } + + public async Task FetchStreamAsync(string? url) + { + var response = await httpClient.GetAsync(url); + + if (response.IsSuccessStatusCode) + { + return await response.Content.ReadAsStreamAsync(); + } + + throw new HttpRequestException($"{ResourcesKey.ErrorWhenFetchingData} + StatusCode: {response.StatusCode} " + + $"+ Reason: {response.ReasonPhrase}"); + } + + public async Task PostFileAsync(string? url, Stream fileStream, string fileName, string contentType) + { + using var content = new MultipartFormDataContent(); + + var streamContent = new StreamContent(fileStream); + streamContent.Headers.ContentType = new MediaTypeHeaderValue(contentType); + content.Add(streamContent, "file", fileName); + + var response = await httpClient.PostAsync(url, content); + + return await GetResultValue(response, ResourcesKey.ErrorWhenPostingData); + } + + private async Task GetResultValue(HttpResponseMessage response, string errorMessage) + { + if (response.IsSuccessStatusCode) + { + try + { + var responseContent = await response.Content.ReadAsStringAsync(); + + if (string.IsNullOrWhiteSpace(responseContent)) + { + return default; + } + + var result = JsonSerializer.Deserialize(responseContent, _optionsCaseInsensitive); + + return result; + } + catch (Exception ex) + { + throw new JsonException(ex.Message); + } + } + + logger.LogError(ResourcesKey.RequestFailedStatusFormat, response.StatusCode); + throw new HttpRequestException( + $"{errorMessage} + StatusCode: {response.StatusCode} + Reason: {response.ReasonPhrase}"); + } +} diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Services/IHttpClientService.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Services/IHttpClientService.cs new file mode 100644 index 0000000..299ab00 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Services/IHttpClientService.cs @@ -0,0 +1,12 @@ +namespace GameIdeas.BlazorApp.Services; + +public interface IHttpClientService +{ + Task PostAsync(string url, object data); + Task PutAsync(string url, object data); + Task DeleteAsync(string? url); + Task FetchDataAsync(string? url); + Task FetchBytesAsync(string? url); + Task FetchStreamAsync(string? url); + Task PostFileAsync(string? url, Stream fileStream, string fileName, string contentType); +} diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Account/AccountSettings.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Account/AccountSettings.razor index 5b111c2..98094ae 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Account/AccountSettings.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Account/AccountSettings.razor @@ -3,7 +3,7 @@