Update game
All checks were successful
Game Ideas build for PR / build_test (pull_request) Successful in 50s

This commit is contained in:
Maxime Adler
2025-05-12 16:25:28 +02:00
parent 14dc1928bc
commit 5d30c2353e
14 changed files with 135 additions and 23 deletions

View File

@@ -20,6 +20,20 @@ public static class GameHelper
game.CreationDate = DateTime.Now; 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) public static string GetInterestColor(int interest, int maxInterest)
{ {
int firstTier = (int)Math.Floor(0.33 * maxInterest); int firstTier = (int)Math.Floor(0.33 * maxInterest);

View File

@@ -1,6 +1,9 @@
using GameIdeas.BlazorApp.Shared.Components; using GameIdeas.BlazorApp.Shared.Components;
using GameIdeas.BlazorApp.Shared.Exceptions;
using GameIdeas.Resources;
using GameIdeas.Shared.Dto; using GameIdeas.Shared.Dto;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using System.Linq.Expressions;
namespace GameIdeas.BlazorApp.Pages.Detail; namespace GameIdeas.BlazorApp.Pages.Detail;
@@ -13,7 +16,7 @@ public partial class GameDetail : GameBaseComponent
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
Game = await GameGateway.GetGameById(GameId); await FetchGameDetail();
await base.OnInitializedAsync(); await base.OnInitializedAsync();
} }
@@ -21,4 +24,22 @@ public partial class GameDetail : GameBaseComponent
{ {
NavigationManager.NavigateTo("/"); NavigationManager.NavigateTo("/");
} }
private async Task FetchGameDetail()
{
try
{
IsLoading = true;
Game = await GameGateway.GetGameById(GameId);
}
catch (Exception)
{
throw new FetchGameDetailException(ResourcesKey.ErrorFetchDetail);
}
finally
{
IsLoading = false;
}
}
} }

View File

@@ -3,6 +3,8 @@ using GameIdeas.BlazorApp.Pages.Games.Gateways;
using GameIdeas.BlazorApp.Shared.Components.Popup; using GameIdeas.BlazorApp.Shared.Components.Popup;
using GameIdeas.BlazorApp.Shared.Components.Select.Models; using GameIdeas.BlazorApp.Shared.Components.Select.Models;
using GameIdeas.BlazorApp.Shared.Components.Slider; using GameIdeas.BlazorApp.Shared.Components.Slider;
using GameIdeas.BlazorApp.Shared.Exceptions;
using GameIdeas.Resources;
using GameIdeas.Shared.Dto; using GameIdeas.Shared.Dto;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
@@ -19,22 +21,25 @@ public partial class GameCreationForm
[CascadingParameter] private Popup? Popup { get; set; } [CascadingParameter] private Popup? Popup { get; set; }
[Parameter] public CategoriesDto? Categories { get; set; } [Parameter] public CategoriesDto? Categories { get; set; }
[Parameter] public EventCallback OnSubmit { get; set; } [Parameter] public EventCallback OnSubmit { get; set; }
[Parameter] public int? GameIdToUpdate { get; set; }
private readonly GameDetailDto GameDto = new(); private GameDetailDto GameDto = new();
private EditContext? EditContext; private EditContext? EditContext;
private readonly SelectTheme Theme = SelectTheme.Creation; private readonly SelectTheme Theme = SelectTheme.Creation;
private readonly SliderParams SliderParams = new() { Gap = 1, Min = 1, Max = 5 }; private readonly SliderParams SliderParams = new() { Gap = 1, Min = 1, Max = 5 };
private bool IsLoading = false; private bool IsLoading = false;
protected override async Task OnInitializedAsync() protected override void OnInitialized()
{ {
EditContext = new(GameDto); EditContext = new(GameDto);
await base.OnInitializedAsync(); IsLoading = GameIdToUpdate != null;
base.OnInitialized();
} }
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
await Js.InvokeVoidAsync("resizeGameForm"); await Js.InvokeVoidAsync("resizeGameForm");
await SetGameToUpdate();
} }
private void HandleOnCancel() private void HandleOnCancel()
@@ -53,15 +58,22 @@ public partial class GameCreationForm
{ {
IsLoading = true; IsLoading = true;
int gameId;
var authState = await AuthenticationState.GetAuthenticationStateAsync(); var authState = await AuthenticationState.GetAuthenticationStateAsync();
GameHelper.WriteTrackingDto(GameDto, authState); GameHelper.WriteTrackingDto(GameDto, authState);
var gameId = await GameGateway.CreateGame(GameDto); if (GameDto.Id != null)
if (gameId != 0)
{ {
Popup?.Close(); gameId = await GameGateway.UpdateGame(GameDto);
await OnSubmit.InvokeAsync(); }
else
{
gameId = await GameGateway.CreateGame(GameDto);
}
if (gameId == 0)
{
throw new GameCreationException(ResourcesKey.ErrorCreateGame);
} }
} }
catch (Exception) catch (Exception)
@@ -73,13 +85,45 @@ public partial class GameCreationForm
IsLoading = false; IsLoading = false;
StateHasChanged(); StateHasChanged();
} }
Popup?.Close();
await OnSubmit.InvokeAsync();
} }
private void HandlePublisherChanged(List<PublisherDto> pubs) private void HandlePublisherChanged(List<PublisherDto> pubs)
{ {
GameDto.Publisher = pubs.FirstOrDefault(); GameDto.Publisher = pubs.FirstOrDefault();
} }
private void HandleDeveloperChanged(List<DeveloperDto> devs) private void HandleDeveloperChanged(List<DeveloperDto> devs)
{ {
GameDto.Developer = devs.FirstOrDefault(); 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);
}
} }

View File

@@ -42,7 +42,7 @@
</div> </div>
<Popup @ref=ManualAddPopup BackdropFilterClicked="HandleBackdropManualAddClicked" Closable=false> <Popup @ref=ManualAddPopup BackdropFilterClicked="HandleBackdropManualAddClicked" Closable=false>
<GameCreationForm Categories="Categories" OnSubmit="() => HandleFetchDatas()" /> <GameCreationForm GameIdToUpdate="@GameIdToUpdate" Categories="Categories" OnSubmit="() => HandleFetchDatas()" />
</Popup> </Popup>
<Popup @ref=DeletePopup Closable=false> <Popup @ref=DeletePopup Closable=false>

View File

@@ -1,3 +1,4 @@
using GameIdeas.BlazorApp.Pages.Games.Components;
using GameIdeas.BlazorApp.Pages.Games.Filter; using GameIdeas.BlazorApp.Pages.Games.Filter;
using GameIdeas.BlazorApp.Shared.Components; using GameIdeas.BlazorApp.Shared.Components;
using GameIdeas.BlazorApp.Shared.Components.Popup; using GameIdeas.BlazorApp.Shared.Components.Popup;
@@ -15,6 +16,7 @@ public partial class Games : GameBaseComponent
private int CurrentPage; private int CurrentPage;
private Popup? DeletePopup; private Popup? DeletePopup;
private GameDto? GameToDelete; private GameDto? GameToDelete;
private int? GameIdToUpdate;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@@ -64,9 +66,16 @@ public partial class Games : GameBaseComponent
GameToDelete = null; 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() private async Task HandleRemoveGame()
@@ -87,7 +96,6 @@ public partial class Games : GameBaseComponent
} }
catch (Exception) catch (Exception)
{ {
throw; throw;
} }

View File

@@ -88,4 +88,16 @@ public class GameGateway(IHttpClientService httpClientService) : IGameGateway
throw new GameDeletionException(ResourcesKey.ErrorDeleteGame); throw new GameDeletionException(ResourcesKey.ErrorDeleteGame);
} }
} }
public async Task<int> UpdateGame(GameDetailDto gameDto)
{
try
{
return await httpClientService.PutAsync<int>(Endpoints.Game.Update, gameDto);
}
catch (Exception)
{
throw new GameUpdateException(ResourcesKey.ErrorUpdateGame);
}
}
} }

View File

@@ -10,4 +10,5 @@ public interface IGameGateway
Task<IEnumerable<GameDto>> FetchGames(GameFilterParams filter, int currentPage); Task<IEnumerable<GameDto>> FetchGames(GameFilterParams filter, int currentPage);
Task<GameDetailDto> GetGameById(int gameId); Task<GameDetailDto> GetGameById(int gameId);
Task<bool> DeleteGame(int gameIdToDelete); Task<bool> DeleteGame(int gameIdToDelete);
Task<int> UpdateGame(GameDetailDto gameDto);
} }

View File

@@ -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.BlazorApp.Shared.Models;
using GameIdeas.Shared.Dto; using GameIdeas.Shared.Dto;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;

View File

@@ -11,6 +11,7 @@ public static class Endpoints
public static string Fetch(GameFilterDto filter) => $"api/Game?{UrlHelper.BuildUrlParams(filter)}"; public static string Fetch(GameFilterDto filter) => $"api/Game?{UrlHelper.BuildUrlParams(filter)}";
public static string FetchById(int gameId) => $"api/Game/{gameId}"; public static string FetchById(int gameId) => $"api/Game/{gameId}";
public static string Delete(int gameId) => $"api/Game/Delete/{gameId}"; public static string Delete(int gameId) => $"api/Game/Delete/{gameId}";
public const string Update = "api/Game/Update";
} }
public static class Category public static class Category

View File

@@ -0,0 +1,3 @@
namespace GameIdeas.BlazorApp.Shared.Exceptions;
public class FetchGameDetailException(string message) : Exception(message);

View File

@@ -0,0 +1,3 @@
namespace GameIdeas.BlazorApp.Shared.Exceptions;
public class GameUpdateException(string message) : Exception(message);

View File

@@ -40,10 +40,12 @@ public class Translations (TranslationService translationService)
public string PlaceholderAdd => translationService.Translate(nameof(PlaceholderAdd)); public string PlaceholderAdd => translationService.Translate(nameof(PlaceholderAdd));
public string ErrorCreateGame => translationService.Translate(nameof(ErrorCreateGame)); public string ErrorCreateGame => translationService.Translate(nameof(ErrorCreateGame));
public string ErrorDeleteGame => translationService.Translate(nameof(ErrorDeleteGame)); public string ErrorDeleteGame => translationService.Translate(nameof(ErrorDeleteGame));
public string ErrorUpdateGame => translationService.Translate(nameof(ErrorUpdateGame));
public string InvalidTitle => translationService.Translate(nameof(InvalidTitle)); public string InvalidTitle => translationService.Translate(nameof(InvalidTitle));
public string InvalidInterest => translationService.Translate(nameof(InvalidInterest)); public string InvalidInterest => translationService.Translate(nameof(InvalidInterest));
public string Unknown => translationService.Translate(nameof(Unknown)); public string Unknown => translationService.Translate(nameof(Unknown));
public string ErrorFetchGames => translationService.Translate(nameof(ErrorFetchGames)); public string ErrorFetchGames => translationService.Translate(nameof(ErrorFetchGames));
public string ErrorFetchDetail => translationService.Translate(nameof(ErrorFetchDetail));
public string Ascending => translationService.Translate(nameof(Ascending)); public string Ascending => translationService.Translate(nameof(Ascending));
public string Descending => translationService.Translate(nameof(Descending)); public string Descending => translationService.Translate(nameof(Descending));
public string ErrorStorageSpaceLabel => translationService.Translate(nameof(ErrorStorageSpaceLabel)); 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 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 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 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 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 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 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 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 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 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."); public static string ErrorStorageSpaceLabel => _instance?.ErrorStorageSpaceLabel ?? throw new InvalidOperationException("ResourcesKey.ErrorStorageSpaceLabel is not initialized.");

View File

@@ -36,10 +36,12 @@
"PlaceholderAdd": "Ajouter un nouveau", "PlaceholderAdd": "Ajouter un nouveau",
"ErrorCreateGame": "Erreur lors de la création d'un jeu", "ErrorCreateGame": "Erreur lors de la création d'un jeu",
"ErrorDeleteGame": "Erreur lors de la suppression 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", "InvalidTitle": "Le titre est incorrect",
"InvalidInterest": "L'interêt est incorrect", "InvalidInterest": "L'interêt est incorrect",
"Unknown": "Inconnu", "Unknown": "Inconnu",
"ErrorFetchGames": "Erreur lors de la récupération des jeux", "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", "Ascending": "Ascendant",
"Descending": "Descendant", "Descending": "Descendant",
"ErrorStorageSpaceLabel": "Erreur lors de la génération des label de l'espace de stockage", "ErrorStorageSpaceLabel": "Erreur lors de la génération des label de l'espace de stockage",