From 5d30c2353e6d6fd4089c2b53f3c94ad134ab4c33 Mon Sep 17 00:00:00 2001 From: Maxime Adler Date: Mon, 12 May 2025 16:25:28 +0200 Subject: [PATCH] Update game --- .../GameIdeas.BlazorApp/Helpers/GameHelper.cs | 14 +++++ .../Pages/Detail/GameDetail.razor.cs | 23 ++++++- .../Games/Components/GameCreationForm.razor | 14 ++--- .../Components/GameCreationForm.razor.cs | 62 ++++++++++++++++--- .../Pages/Games/Games.razor | 2 +- .../Pages/Games/Games.razor.cs | 14 ++++- .../Pages/Games/Gateways/GameGateway.cs | 12 ++++ .../Pages/Games/Gateways/IGameGateway.cs | 1 + .../Shared/Components/GameBaseComponent.cs | 3 +- .../Shared/Constants/Endpoints.cs | 1 + .../Exceptions/FetchGameDetailException.cs | 3 + .../Shared/Exceptions/GameUpdateException.cs | 3 + .../CreateStaticResourceKey.cs | 4 ++ .../GameIdeas.WebAPI/Files/GameIdeas.fr.json | 2 + 14 files changed, 135 insertions(+), 23 deletions(-) create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/FetchGameDetailException.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/GameUpdateException.cs diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs index 27df03e..9778a14 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs @@ -20,6 +20,20 @@ public static class GameHelper game.CreationDate = DateTime.Now; } + public static void UpdateTrackingDto(GameDetailDto game, AuthenticationState authState) + { + if (authState == null) + { + throw new ArgumentNullException(nameof(authState), "Authentication state missing"); + } + + var userId = authState.User.FindFirstValue(ClaimTypes.Sid) + ?? throw new ArgumentNullException(nameof(authState), "user state missing"); + + game.ModificationUserId = userId; + game.ModificationDate = DateTime.Now; + } + public static string GetInterestColor(int interest, int maxInterest) { int firstTier = (int)Math.Floor(0.33 * maxInterest); diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor.cs index f5e8bfa..9e28609 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor.cs @@ -1,6 +1,9 @@ using GameIdeas.BlazorApp.Shared.Components; +using GameIdeas.BlazorApp.Shared.Exceptions; +using GameIdeas.Resources; using GameIdeas.Shared.Dto; using Microsoft.AspNetCore.Components; +using System.Linq.Expressions; namespace GameIdeas.BlazorApp.Pages.Detail; @@ -13,7 +16,7 @@ public partial class GameDetail : GameBaseComponent protected override async Task OnInitializedAsync() { - Game = await GameGateway.GetGameById(GameId); + await FetchGameDetail(); await base.OnInitializedAsync(); } @@ -21,4 +24,22 @@ public partial class GameDetail : GameBaseComponent { NavigationManager.NavigateTo("/"); } + + private async Task FetchGameDetail() + { + try + { + IsLoading = true; + + Game = await GameGateway.GetGameById(GameId); + } + catch (Exception) + { + throw new FetchGameDetailException(ResourcesKey.ErrorFetchDetail); + } + finally + { + IsLoading = false; + } + } } \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor index c9e1028..95b05d1 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor @@ -11,7 +11,7 @@
@ResourcesKey.Title :
- +
@ResourcesKey.ReleaseDate :
@@ -44,21 +44,21 @@
@ResourcesKey.Properties :
+ Items="Categories?.Properties" @bind-Values=GameDto.Properties + AddItem="@(str => new PropertyDto() { Label = str })" />
@ResourcesKey.Tags :
+ Items="Categories?.Tags" @bind-Values=GameDto.Tags + AddItem="@(str => new TagDto() { Label = str })" />
@ResourcesKey.Platforms :
+ Items="Categories?.Platforms" @bind-Values=GameDto.Platforms + AddItem="@(str => new PlatformDto() { Label = str })" />
@foreach (var platform in GameDto.Platforms ?? []) 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 index e7d107f..9545a4c 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.cs @@ -3,6 +3,8 @@ 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.BlazorApp.Shared.Exceptions; +using GameIdeas.Resources; using GameIdeas.Shared.Dto; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; @@ -19,22 +21,25 @@ public partial class GameCreationForm [CascadingParameter] private Popup? Popup { get; set; } [Parameter] public CategoriesDto? Categories { get; set; } [Parameter] public EventCallback OnSubmit { get; set; } - - private readonly GameDetailDto GameDto = new(); + [Parameter] public int? GameIdToUpdate { get; set; } + + private GameDetailDto GameDto = new(); private EditContext? EditContext; private readonly SelectTheme Theme = SelectTheme.Creation; private readonly SliderParams SliderParams = new() { Gap = 1, Min = 1, Max = 5 }; private bool IsLoading = false; - protected override async Task OnInitializedAsync() + protected override void OnInitialized() { EditContext = new(GameDto); - await base.OnInitializedAsync(); + IsLoading = GameIdToUpdate != null; + base.OnInitialized(); } protected override async Task OnAfterRenderAsync(bool firstRender) { await Js.InvokeVoidAsync("resizeGameForm"); + await SetGameToUpdate(); } private void HandleOnCancel() @@ -53,15 +58,22 @@ public partial class GameCreationForm { IsLoading = true; + int gameId; var authState = await AuthenticationState.GetAuthenticationStateAsync(); GameHelper.WriteTrackingDto(GameDto, authState); - var gameId = await GameGateway.CreateGame(GameDto); - - if (gameId != 0) + if (GameDto.Id != null) { - Popup?.Close(); - await OnSubmit.InvokeAsync(); + gameId = await GameGateway.UpdateGame(GameDto); + } + else + { + gameId = await GameGateway.CreateGame(GameDto); + } + + if (gameId == 0) + { + throw new GameCreationException(ResourcesKey.ErrorCreateGame); } } catch (Exception) @@ -73,13 +85,45 @@ public partial class GameCreationForm IsLoading = false; StateHasChanged(); } + + Popup?.Close(); + + await OnSubmit.InvokeAsync(); } + private void HandlePublisherChanged(List pubs) { GameDto.Publisher = pubs.FirstOrDefault(); } + private void HandleDeveloperChanged(List devs) { GameDto.Developer = devs.FirstOrDefault(); } + + private async Task SetGameToUpdate() + { + if (GameIdToUpdate == null) + { + return; + } + + try + { + IsLoading = true; + + GameDto = await GameGateway.GetGameById(GameIdToUpdate ?? 0); + } + catch (Exception) + { + throw new FetchGameDetailException(ResourcesKey.ErrorFetchDetail); + } + finally + { + IsLoading = false; + StateHasChanged(); + } + + EditContext = new(GameDto); + } } \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor index 4e85548..f94e0b6 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor @@ -42,7 +42,7 @@
- + diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor.cs index 2020cdf..f4f0d66 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor.cs @@ -1,3 +1,4 @@ +using GameIdeas.BlazorApp.Pages.Games.Components; using GameIdeas.BlazorApp.Pages.Games.Filter; using GameIdeas.BlazorApp.Shared.Components; using GameIdeas.BlazorApp.Shared.Components.Popup; @@ -15,6 +16,7 @@ public partial class Games : GameBaseComponent private int CurrentPage; private Popup? DeletePopup; private GameDto? GameToDelete; + private int? GameIdToUpdate; protected override async Task OnInitializedAsync() { @@ -64,9 +66,16 @@ public partial class Games : GameBaseComponent GameToDelete = null; } - private Task HandleEditGame(GameDto args) + private void HandleEditGame(GameDto args) { - throw new NotImplementedException(); + if (args.Id == null) + { + return; + } + + GameIdToUpdate = args.Id; + + ManualAddPopup?.Open(); } private async Task HandleRemoveGame() @@ -87,7 +96,6 @@ public partial class Games : GameBaseComponent } catch (Exception) { - throw; } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs index 79f4cef..0affe3f 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs @@ -88,4 +88,16 @@ public class GameGateway(IHttpClientService httpClientService) : IGameGateway throw new GameDeletionException(ResourcesKey.ErrorDeleteGame); } } + + public async Task UpdateGame(GameDetailDto gameDto) + { + try + { + return await httpClientService.PutAsync(Endpoints.Game.Update, gameDto); + } + catch (Exception) + { + throw new GameUpdateException(ResourcesKey.ErrorUpdateGame); + } + } } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs index 193852c..ac8cb99 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs @@ -10,4 +10,5 @@ public interface IGameGateway Task> FetchGames(GameFilterParams filter, int currentPage); Task GetGameById(int gameId); Task DeleteGame(int gameIdToDelete); + Task UpdateGame(GameDetailDto gameDto); } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/GameBaseComponent.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/GameBaseComponent.cs index c06b794..8839cdd 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/GameBaseComponent.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/GameBaseComponent.cs @@ -1,5 +1,4 @@ -using GameIdeas.BlazorApp.Pages.Games.Filter; -using GameIdeas.BlazorApp.Pages.Games.Gateways; +using GameIdeas.BlazorApp.Pages.Games.Gateways; using GameIdeas.BlazorApp.Shared.Models; using GameIdeas.Shared.Dto; using Microsoft.AspNetCore.Components; diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs index cbb8905..1447fb6 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs @@ -11,6 +11,7 @@ public static class Endpoints public static string Fetch(GameFilterDto filter) => $"api/Game?{UrlHelper.BuildUrlParams(filter)}"; public static string FetchById(int gameId) => $"api/Game/{gameId}"; public static string Delete(int gameId) => $"api/Game/Delete/{gameId}"; + public const string Update = "api/Game/Update"; } public static class Category diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/FetchGameDetailException.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/FetchGameDetailException.cs new file mode 100644 index 0000000..94e49d5 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/FetchGameDetailException.cs @@ -0,0 +1,3 @@ +namespace GameIdeas.BlazorApp.Shared.Exceptions; + +public class FetchGameDetailException(string message) : Exception(message); \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/GameUpdateException.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/GameUpdateException.cs new file mode 100644 index 0000000..628a3bb --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/GameUpdateException.cs @@ -0,0 +1,3 @@ +namespace GameIdeas.BlazorApp.Shared.Exceptions; + +public class GameUpdateException(string message) : Exception(message); diff --git a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs index 6a39b08..59d4532 100644 --- a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs +++ b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs @@ -40,10 +40,12 @@ public class Translations (TranslationService translationService) public string PlaceholderAdd => translationService.Translate(nameof(PlaceholderAdd)); public string ErrorCreateGame => translationService.Translate(nameof(ErrorCreateGame)); public string ErrorDeleteGame => translationService.Translate(nameof(ErrorDeleteGame)); + public string ErrorUpdateGame => translationService.Translate(nameof(ErrorUpdateGame)); public string InvalidTitle => translationService.Translate(nameof(InvalidTitle)); public string InvalidInterest => translationService.Translate(nameof(InvalidInterest)); public string Unknown => translationService.Translate(nameof(Unknown)); public string ErrorFetchGames => translationService.Translate(nameof(ErrorFetchGames)); + public string ErrorFetchDetail => translationService.Translate(nameof(ErrorFetchDetail)); public string Ascending => translationService.Translate(nameof(Ascending)); public string Descending => translationService.Translate(nameof(Descending)); public string ErrorStorageSpaceLabel => translationService.Translate(nameof(ErrorStorageSpaceLabel)); @@ -120,10 +122,12 @@ public static class ResourcesKey public static string PlaceholderAdd => _instance?.PlaceholderAdd ?? throw new InvalidOperationException("ResourcesKey.PlaceholderAdd is not initialized."); public static string ErrorCreateGame => _instance?.ErrorCreateGame ?? throw new InvalidOperationException("ResourcesKey.ErrorCreateGame is not initialized."); public static string ErrorDeleteGame => _instance?.ErrorDeleteGame ?? throw new InvalidOperationException("ResourcesKey.ErrorDeleteGame is not initialized."); + public static string ErrorUpdateGame => _instance?.ErrorUpdateGame ?? throw new InvalidOperationException("ResourcesKey.ErrorUpdateGame is not initialized."); public static string InvalidTitle => _instance?.InvalidTitle ?? throw new InvalidOperationException("ResourcesKey.InvalidTitle is not initialized."); public static string InvalidInterest => _instance?.InvalidInterest ?? throw new InvalidOperationException("ResourcesKey.InvalidInterest is not initialized."); public static string Unknown => _instance?.Unknown ?? throw new InvalidOperationException("ResourcesKey.Unknown is not initialized."); public static string ErrorFetchGames => _instance?.ErrorFetchGames ?? throw new InvalidOperationException("ResourcesKey.ErrorFetchGames is not initialized."); + public static string ErrorFetchDetail => _instance?.ErrorFetchDetail ?? throw new InvalidOperationException("ResourcesKey.ErrorFetchDetail is not initialized."); public static string Ascending => _instance?.Ascending ?? throw new InvalidOperationException("ResourcesKey.Ascending is not initialized."); public static string Descending => _instance?.Descending ?? throw new InvalidOperationException("ResourcesKey.Descending is not initialized."); public static string ErrorStorageSpaceLabel => _instance?.ErrorStorageSpaceLabel ?? throw new InvalidOperationException("ResourcesKey.ErrorStorageSpaceLabel is not initialized."); diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json index 7c2b521..631c233 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json @@ -36,10 +36,12 @@ "PlaceholderAdd": "Ajouter un nouveau", "ErrorCreateGame": "Erreur lors de la création d'un jeu", "ErrorDeleteGame": "Erreur lors de la suppression d'un jeu", + "ErrorUpdateGame": "Erreur lors de la modification d'un jeu", "InvalidTitle": "Le titre est incorrect", "InvalidInterest": "L'interêt est incorrect", "Unknown": "Inconnu", "ErrorFetchGames": "Erreur lors de la récupération des jeux", + "ErrorFetchDetail": "Erreur lors de la récupération des détails d'un jeu", "Ascending": "Ascendant", "Descending": "Descendant", "ErrorStorageSpaceLabel": "Erreur lors de la génération des label de l'espace de stockage",