Create game from form (#15)
Co-authored-by: Maxime Adler <madler@sqli.com> Reviewed-on: #15
This commit was merged in pull request #15.
This commit is contained in:
@@ -1,31 +1,36 @@
|
||||
@using GameIdeas.BlazorApp.Shared.Components.SelectSearch
|
||||
@using Blazored.FluentValidation
|
||||
@using GameIdeas.BlazorApp.Shared.Components.CircleLoader
|
||||
@using GameIdeas.BlazorApp.Shared.Components.SelectSearch
|
||||
@using GameIdeas.BlazorApp.Shared.Components.Slider
|
||||
@using GameIdeas.Shared.Dto
|
||||
|
||||
<EditForm EditContext="EditContext" OnSubmit="HandleOnSubmit">
|
||||
<FluentValidationValidator/>
|
||||
<div class="game-form">
|
||||
<div class="container">
|
||||
<div class="input-game">
|
||||
<div id="first-label" class="label">@ResourcesKey.Title :</div>
|
||||
<input type="text" class="title" @bind=GameDto.Title>
|
||||
<InputText class="title" @bind-Value=GameDto.Title/>
|
||||
</div>
|
||||
<div class="input-game">
|
||||
<div class="label">@ResourcesKey.ReleaseDate :</div>
|
||||
<input type="date" class="date" @bind=GameDto.ReleaseDate>
|
||||
<InputDate TValue="DateTime?" class="date" @bind-Value=GameDto.ReleaseDate />
|
||||
</div>
|
||||
<div class="input-game">
|
||||
<div class="label">@ResourcesKey.StorageSizeMo :</div>
|
||||
<input type="number" class="storage" @bind=GameDto.StorageSpace>
|
||||
<InputNumber TValue="double?" class="storage" @bind-Value=GameDto.StorageSpace />
|
||||
</div>
|
||||
<div class="input-game">
|
||||
<div class="label">@ResourcesKey.Developers :</div>
|
||||
<SelectSearch TItem="DeveloperDto" Theme="Theme" GetLabel="@(i => i.Name)"
|
||||
Items="Categories?.Developers" @bind-Values=GameDto.Developers />
|
||||
<SelectSearch TItem="DeveloperDto" Theme="Theme" GetLabel="@(i => i.Name)" QuickAdd=true
|
||||
Items="Categories?.Developers" @bind-Values=GameDto.Developers
|
||||
AddItem="@(str => new DeveloperDto() { Name = str })" />
|
||||
</div>
|
||||
<div class="input-game">
|
||||
<div class="label">@ResourcesKey.Publishers :</div>
|
||||
<SelectSearch TItem="PublisherDto" Theme="Theme" GetLabel="@(i => i.Name)"
|
||||
Items="Categories?.Publishers" @bind-Values=GameDto.Publishers />
|
||||
<SelectSearch TItem="PublisherDto" Theme="Theme" GetLabel="@(i => i.Name)" QuickAdd=true
|
||||
Items="Categories?.Publishers" @bind-Values=GameDto.Publishers
|
||||
AddItem="@(str => new PublisherDto() { Name = str })" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
@@ -37,32 +42,52 @@
|
||||
</div>
|
||||
<div class="input-game">
|
||||
<div class="label">@ResourcesKey.Properties :</div>
|
||||
<SelectSearch TItem="PropertyDto" Theme="Theme" GetLabel="@(i => i.Label)"
|
||||
Items="Categories?.Properties" @bind-Values=GameDto.Properties />
|
||||
<SelectSearch TItem="PropertyDto" Theme="Theme" GetLabel="@(i => i.Label)" QuickAdd=true
|
||||
Items="Categories?.Properties" @bind-Values=GameDto.Properties
|
||||
AddItem="@(str => new PropertyDto() { Label = str })" />
|
||||
</div>
|
||||
<div class="input-game">
|
||||
<div class="label">@ResourcesKey.Tags :</div>
|
||||
<SelectSearch TItem="TagDto" Theme="Theme" GetLabel="@(i => i.Label)"
|
||||
Items="Categories?.Tags" @bind-Values=GameDto.Tags />
|
||||
<SelectSearch TItem="TagDto" Theme="Theme" GetLabel="@(i => i.Label)" QuickAdd=true
|
||||
Items="Categories?.Tags" @bind-Values=GameDto.Tags
|
||||
AddItem="@(str => new TagDto() { Label = str })" />
|
||||
|
||||
</div>
|
||||
<div class="input-game">
|
||||
<div class="label">@ResourcesKey.Platforms :</div>
|
||||
<SelectSearch TItem="PlatformDto" Theme="Theme" GetLabel="@(i => i.Label)"
|
||||
Items="Categories?.Platforms" @bind-Values=GameDto.Platforms />
|
||||
<SelectSearch TItem="PlatformDto" Theme="Theme" GetLabel="@(i => i.Label)" QuickAdd=true
|
||||
Items="Categories?.Platforms" @bind-Values=GameDto.Platforms
|
||||
AddItem="@(str => new PlatformDto() { Label = str })" />
|
||||
</div>
|
||||
|
||||
@foreach (var platform in GameDto.Platforms ?? [])
|
||||
{
|
||||
<div class="input-game">
|
||||
<div class="label">@platform.Label :</div>
|
||||
<InputText class="url" @bind-Value=platform.Url />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="description-container">
|
||||
<div id="label-description">@ResourcesKey.Description :</div>
|
||||
<input type="text" class="description" @bind-value=GameDto.Description>
|
||||
<InputTextArea class="description" @bind-Value=GameDto.Description />
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="reset" class="cancel" @onclick=HandleOnCancel>
|
||||
@ResourcesKey.Reset
|
||||
</button>
|
||||
<button type="submit" class="submit">
|
||||
@ResourcesKey.Save
|
||||
</button>
|
||||
<div class="bottom-container">
|
||||
<ValidationSummary class="invalid-content" />
|
||||
|
||||
<div class="buttons">
|
||||
<button type="reset" class="cancel" @onclick=HandleOnCancel disabled="@IsLoading">
|
||||
@ResourcesKey.Reset
|
||||
</button>
|
||||
<button type="submit" class="submit" disabled="@IsLoading">
|
||||
@ResourcesKey.Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
</EditForm>
|
||||
|
||||
@if (IsLoading)
|
||||
{
|
||||
<CircleLoader />
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using GameIdeas.BlazorApp.Helpers;
|
||||
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;
|
||||
@@ -11,29 +13,27 @@ 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; }
|
||||
[Parameter] public CategoriesDto? Categories { get; set; }
|
||||
[Parameter] public EventCallback OnSubmit { get; set; }
|
||||
|
||||
private GameDto 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()
|
||||
{
|
||||
EditContext = new(GameDto);
|
||||
|
||||
if (Popup != null)
|
||||
{
|
||||
Popup.StateChanged += async (_, isOpen) => await HandlePopupStateChanged();
|
||||
}
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
private async Task HandlePopupStateChanged()
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await Js.InvokeVoidAsync("resizeGameForm");
|
||||
|
||||
}
|
||||
|
||||
private void HandleOnCancel()
|
||||
@@ -48,6 +48,26 @@ public partial class GameCreationForm
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
|
||||
GameHelper.WriteTrackingDto(GameDto);
|
||||
var gameId = await GameGateway.CreateGame(GameDto);
|
||||
|
||||
if (gameId != 0)
|
||||
{
|
||||
Popup?.Close();
|
||||
await OnSubmit.InvokeAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
.game-form {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
@@ -24,7 +25,7 @@
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
input {
|
||||
::deep input, ::deep textarea {
|
||||
width: 100%;
|
||||
background: var(--input-secondary);
|
||||
border: solid 1px var(--input-selected);
|
||||
@@ -35,10 +36,19 @@ input {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
input[type="date"]::-webkit-calendar-picker-indicator {
|
||||
filter: invert(1);
|
||||
cursor: pointer;
|
||||
}
|
||||
::deep input[type="date"]::-webkit-calendar-picker-indicator {
|
||||
filter: invert(1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::deep input[type="number"]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::deep textarea {
|
||||
resize: vertical;
|
||||
min-height: 140px;
|
||||
}
|
||||
|
||||
.description-container {
|
||||
margin-top: 8px;
|
||||
@@ -63,6 +73,48 @@ input {
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
input[type="number"]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
.bottom-container {
|
||||
margin-top: 8px;
|
||||
height: 28px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
::deep .invalid-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-shrink: 1;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
height: auto;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
::deep .invalid-content li {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-left: auto;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.buttons button {
|
||||
border: none;
|
||||
outline: none;
|
||||
background: var(--violet);
|
||||
border-radius: var(--small-radius);
|
||||
color: var(--white);
|
||||
font-weight: bold;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.buttons button:hover {
|
||||
background: var(--violet-selected);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using FluentValidation;
|
||||
using GameIdeas.Resources;
|
||||
using GameIdeas.Shared.Dto;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Pages.Games.Components;
|
||||
|
||||
public class GameValidation : AbstractValidator<GameDto>
|
||||
{
|
||||
public GameValidation()
|
||||
{
|
||||
RuleFor(g => g.Title)
|
||||
.NotEmpty().WithMessage(ResourcesKey.InvalidTitle);
|
||||
|
||||
RuleFor(g => g.Interest)
|
||||
.GreaterThanOrEqualTo(1).WithMessage(ResourcesKey.InvalidInterest)
|
||||
.LessThanOrEqualTo(5).WithMessage(ResourcesKey.InvalidInterest);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user