From 14dc1928bcb9d8a412416e87675a61f40aa0f70c Mon Sep 17 00:00:00 2001 From: Maxime Adler Date: Mon, 12 May 2025 13:08:23 +0200 Subject: [PATCH] Delete game --- .../Pages/Detail/GameDetail.razor.cs | 4 +- .../Pages/Games/Components/GameBase.cs | 2 +- .../Pages/Games/Games.razor | 7 +- .../Pages/Games/Games.razor.cs | 45 ++++++ .../Pages/Games/Gateways/GameGateway.cs | 12 ++ .../Pages/Games/Gateways/IGameGateway.cs | 1 + .../Shared/Constants/Endpoints.cs | 1 + .../Exceptions/GameDeletionException.cs | 3 + .../GameIdeas.BlazorApp/wwwroot/css/app.css | 3 +- .../CreateStaticResourceKey.cs | 2 + .../GameIdeas.WebAPI/Files/GameIdeas.fr.json | 137 +++++++++--------- 11 files changed, 144 insertions(+), 73 deletions(-) create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/GameDeletionException.cs 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 6fc36a7..f5e8bfa 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor.cs @@ -1,10 +1,10 @@ -using GameIdeas.BlazorApp.Pages.Games.Gateways; +using GameIdeas.BlazorApp.Shared.Components; using GameIdeas.Shared.Dto; using Microsoft.AspNetCore.Components; namespace GameIdeas.BlazorApp.Pages.Detail; -public partial class GameDetail +public partial class GameDetail : GameBaseComponent { [Inject] private NavigationManager NavigationManager { get; set; } = default!; [Parameter] public int GameId { get; set; } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameBase.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameBase.cs index a04dd88..b5c9615 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameBase.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameBase.cs @@ -29,7 +29,7 @@ public class GameBase : ComponentBase switch (option) { case DetailOptions.Detail: - NavigationManager.NavigateTo($"/Games/Detail/{GameDto.Id}"); + NavigationManager.NavigateTo($"/Detail/{GameDto.Id}"); break; case DetailOptions.Edit: await OnEdit.InvokeAsync(GameDto); diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor index f64a2ae..4e85548 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor @@ -5,6 +5,7 @@ @using GameIdeas.BlazorApp.Shared.Components.ButtonAdd @using GameIdeas.BlazorApp.Shared.Components.Header @using GameIdeas.BlazorApp.Shared.Components.Popup +@using GameIdeas.BlazorApp.Shared.Components.Popup.Components @using GameIdeas.Resources @inherits GameBaseComponent @layout MainLayout @@ -24,7 +25,7 @@ { @foreach (var game in GamesDto) { - + } } else @@ -43,3 +44,7 @@ + + + + \ No newline at end of file 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 73e72b5..2020cdf 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor.cs @@ -1,5 +1,6 @@ using GameIdeas.BlazorApp.Pages.Games.Filter; using GameIdeas.BlazorApp.Shared.Components; +using GameIdeas.BlazorApp.Shared.Components.Popup; using GameIdeas.BlazorApp.Shared.Models; using GameIdeas.Shared.Dto; using GameIdeas.Shared.Enum; @@ -12,6 +13,8 @@ public partial class Games : GameBaseComponent private GameFilterParams GameFilter = new(); private IEnumerable GamesDto = []; private int CurrentPage; + private Popup? DeletePopup; + private GameDto? GameToDelete; protected override async Task OnInitializedAsync() { @@ -48,4 +51,46 @@ public partial class Games : GameBaseComponent GameFilter = args; await HandleFetchDatas(false); } + + private void HandleDeleteGame(GameDto args) + { + DeletePopup?.Open(); + GameToDelete = args; + } + + private void HandleCancelPopupClicked() + { + DeletePopup?.Close(); + GameToDelete = null; + } + + private Task HandleEditGame(GameDto args) + { + throw new NotImplementedException(); + } + + private async Task HandleRemoveGame() + { + DeletePopup?.Close(); + + if (GameToDelete?.Id == null) + { + return; + } + + try + { + IsLoading = true; + + await GameGateway.DeleteGame(GameToDelete?.Id ?? 0); + await HandleFetchDatas(false); + } + catch (Exception) + { + + throw; + } + + GameToDelete = null; + } } \ 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 index 12f57d2..79f4cef 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs @@ -76,4 +76,16 @@ public class GameGateway(IHttpClientService httpClientService) : IGameGateway throw new CategoryNotFoundException(ResourcesKey.ErrorFetchGames); } } + + public async Task DeleteGame(int gameIdToDelete) + { + try + { + return await httpClientService.DeleteAsync(Endpoints.Game.Delete(gameIdToDelete)); + } + catch (Exception) + { + throw new GameDeletionException(ResourcesKey.ErrorDeleteGame); + } + } } 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 9bc4fc4..193852c 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs @@ -9,4 +9,5 @@ public interface IGameGateway Task CreateGame(GameDetailDto game); Task> FetchGames(GameFilterParams filter, int currentPage); Task GetGameById(int gameId); + Task DeleteGame(int gameIdToDelete); } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs index c16d19b..cbb8905 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs @@ -10,6 +10,7 @@ public static class Endpoints public const string Create = "api/Game/Create"; 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 static class Category diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/GameDeletionException.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/GameDeletionException.cs new file mode 100644 index 0000000..50f5175 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/GameDeletionException.cs @@ -0,0 +1,3 @@ +namespace GameIdeas.BlazorApp.Shared.Exceptions; + +public class GameDeletionException(string message) : Exception(message); diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/wwwroot/css/app.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/wwwroot/css/app.css index c443f39..e3bf462 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/wwwroot/css/app.css +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/wwwroot/css/app.css @@ -147,9 +147,10 @@ code { .body-lg { font-weight: 400; font-size: 14px; + display: block; } -.header-1, .header-2, span, a { +.header-1, .header-2 { display: block; color: var(--white); margin: 0; diff --git a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs index c8f4b8e..6a39b08 100644 --- a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs +++ b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs @@ -39,6 +39,7 @@ public class Translations (TranslationService translationService) public string ErrorFetchCategories => translationService.Translate(nameof(ErrorFetchCategories)); public string PlaceholderAdd => translationService.Translate(nameof(PlaceholderAdd)); public string ErrorCreateGame => translationService.Translate(nameof(ErrorCreateGame)); + public string ErrorDeleteGame => translationService.Translate(nameof(ErrorDeleteGame)); public string InvalidTitle => translationService.Translate(nameof(InvalidTitle)); public string InvalidInterest => translationService.Translate(nameof(InvalidInterest)); public string Unknown => translationService.Translate(nameof(Unknown)); @@ -118,6 +119,7 @@ public static class ResourcesKey public static string ErrorFetchCategories => _instance?.ErrorFetchCategories ?? throw new InvalidOperationException("ResourcesKey.ErrorFetchCategories is not initialized."); 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 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."); diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json index 2c846b8..7c2b521 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json @@ -1,70 +1,71 @@ { - "GamesIdeas": "Game Ideas", - "ManualAdd": "Manuel", - "AutoAdd": "Automatique", - "Login": "Se connecter", - "Logout": "Se déconnecter", - "EnterUsername": "Nom d'utilisateur", - "EnterPassword": "Mot de passe", - "UserManager": "Gestion des utilisateurs", - "CategoriesManager": "Gestion des catégories", - "Filters": "Les filtres", - "LastAdd": "Les ajouts récents", - "Research": "Rechercher", - "Platforms": "Plateformes", - "Tags": "Genres", - "Publisher": "Editeur", - "Developer": "Développeur", - "StorageSize": "Taille d'espace", - "StorageSizeMo": "Taille d'espace en Mo", - "LastModification": "Dernière modifications", - "ReleaseDate": "Date de parution", - "CreateDate": "Date de création", - "UpdateDate": "Date de modification", - "Title": "Titre", - "Interest": "Intérêt", - "Properties": "Propriétés", - "Description": "Description", - "Save": "Enregister", - "Reset": "Annuler", - "ErrorWhenPostingData": "Erreur lors de la requête POST", - "ErrorWhenPutingData": "Erreur lors de la requête PUT", - "ErrorWhenDeletingData": "Erreur lors de la requête DELETE", - "ErrorWhenFetchingData": "Erreur lors de la requête GET", - "RequestFailedStatusFormat": "Erreur lors de la réponse, code {0}", - "ErrorFetchCategories": "Erreur lors de la récupération des catégories", - "PlaceholderAdd": "Ajouter un nouveau", - "ErrorCreateGame": "Erreur lors de la création 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", - "Ascending": "Ascendant", - "Descending": "Descendant", - "ErrorStorageSpaceLabel": "Erreur lors de la génération des label de l'espace de stockage", - "MinStorageSpaceFormat": "Jusqu'à {0}", - "MaxStorageSpaceFormat": "Plus de {0}", - "MinMaxStorageSpaceFormat": "{0} à {1}", - "UserArgumentsNull": "Nom d'utilisateur ou mot de passe invalide", - "InvalidToken": "Le token JWT est invalide", - "UserUnauthorized": "Utilisateur non authorisé", - "UserLoginFailed": "Authentification de l'utilisateur échoué", - "UserLogoutFailed": "Déconnection de l'utilisateur échoué", - "Roles": "Rôles", - "ErrorFetchUsers": "Erreur lors de la récupération des utilisateurs", - "ErrorFetchRoles": "Erreur lors de la récupération des rôles", - "MissingField": "Un champs est manquant", - "ErrorCreateUser": "Erreur lors de la création d'un utilisateur", - "ErrorUpdateUser": "Erreur lors de la mise à jour d'un utilisateur", - "ErrorDeleteUser": "Erreur lors de la suppression d'un utilisateur", - "Cancel": "Annuler", - "Confirm": "Confirmer", - "ConfirmDeleteDescription": "Êtes-vous sur de vouloir supprimer cet élément ?", - "Informations": "Informations", - "About": "À propos", - "ReadMore": "Afficher", - "ReadLess": "Réduire", - "Detail": "Détail", - "Edit": "Modifier", - "Delete": "Supprimer" + "GamesIdeas": "Game Ideas", + "ManualAdd": "Manuel", + "AutoAdd": "Automatique", + "Login": "Se connecter", + "Logout": "Se déconnecter", + "EnterUsername": "Nom d'utilisateur", + "EnterPassword": "Mot de passe", + "UserManager": "Gestion des utilisateurs", + "CategoriesManager": "Gestion des catégories", + "Filters": "Les filtres", + "LastAdd": "Les ajouts récents", + "Research": "Rechercher", + "Platforms": "Plateformes", + "Tags": "Genres", + "Publisher": "Editeur", + "Developer": "Développeur", + "StorageSize": "Taille d'espace", + "StorageSizeMo": "Taille d'espace en Mo", + "LastModification": "Dernière modifications", + "ReleaseDate": "Date de parution", + "CreateDate": "Date de création", + "UpdateDate": "Date de modification", + "Title": "Titre", + "Interest": "Intérêt", + "Properties": "Propriétés", + "Description": "Description", + "Save": "Enregister", + "Reset": "Annuler", + "ErrorWhenPostingData": "Erreur lors de la requête POST", + "ErrorWhenPutingData": "Erreur lors de la requête PUT", + "ErrorWhenDeletingData": "Erreur lors de la requête DELETE", + "ErrorWhenFetchingData": "Erreur lors de la requête GET", + "RequestFailedStatusFormat": "Erreur lors de la réponse, code {0}", + "ErrorFetchCategories": "Erreur lors de la récupération des catégories", + "PlaceholderAdd": "Ajouter un nouveau", + "ErrorCreateGame": "Erreur lors de la création d'un jeu", + "ErrorDeleteGame": "Erreur lors de la suppression 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", + "Ascending": "Ascendant", + "Descending": "Descendant", + "ErrorStorageSpaceLabel": "Erreur lors de la génération des label de l'espace de stockage", + "MinStorageSpaceFormat": "Jusqu'à {0}", + "MaxStorageSpaceFormat": "Plus de {0}", + "MinMaxStorageSpaceFormat": "{0} à {1}", + "UserArgumentsNull": "Nom d'utilisateur ou mot de passe invalide", + "InvalidToken": "Le token JWT est invalide", + "UserUnauthorized": "Utilisateur non authorisé", + "UserLoginFailed": "Authentification de l'utilisateur échoué", + "UserLogoutFailed": "Déconnection de l'utilisateur échoué", + "Roles": "Rôles", + "ErrorFetchUsers": "Erreur lors de la récupération des utilisateurs", + "ErrorFetchRoles": "Erreur lors de la récupération des rôles", + "MissingField": "Un champs est manquant", + "ErrorCreateUser": "Erreur lors de la création d'un utilisateur", + "ErrorUpdateUser": "Erreur lors de la mise à jour d'un utilisateur", + "ErrorDeleteUser": "Erreur lors de la suppression d'un utilisateur", + "Cancel": "Annuler", + "Confirm": "Confirmer", + "ConfirmDeleteDescription": "Êtes-vous sur de vouloir supprimer cet élément ?", + "Informations": "Informations", + "About": "À propos", + "ReadMore": "Afficher", + "ReadLess": "Réduire", + "Detail": "Détail", + "Edit": "Modifier", + "Delete": "Supprimer" } \ No newline at end of file