Add detail game page (#41)
All checks were successful
Game Ideas deploy / build-test-deploy (push) Successful in 1m24s

Reviewed-on: #41
This commit was merged in pull request #41.
This commit is contained in:
2025-05-04 15:27:06 +02:00
parent f3c9e1d9da
commit d9d036896d
37 changed files with 1174 additions and 224 deletions

View File

@@ -0,0 +1,98 @@
@page "/Detail/{GameId:int}"
@using GameIdeas.BlazorApp.Helpers
@using GameIdeas.BlazorApp.Shared.Components.Header
@using GameIdeas.BlazorApp.Shared.Components.Interest
@using GameIdeas.BlazorApp.Shared.Constants
@layout MainLayout
<HeaderGameIdeas>
</HeaderGameIdeas>
<div class="detail-container">
<div class="section flex">
<a href="" class="square-button">@Icons.Back</a>
<h1 class="header-1">@Game.Title</h1>
<Interest Value="Game.Interest" />
</div>
<div class="section col-2">
<span class="description">@Game.Description</span>
<div class="medias"></div>
</div>
<div class="section dark col-2">
<div class="properties">
<h2 class="header-2 grd-col-1">@ResourcesKey.Properties</h2>
<div class="pills">
@foreach (var property in Game.Properties ?? [])
{
<div class="pill body-lg">@property.Label</div>
}
</div>
</div>
<div class="tags">
<h2 class="header-2">@ResourcesKey.Tags</h2>
<div class="pills">
@foreach (var property in Game.Tags ?? [])
{
<div class="pill body-lg">@property.Label</div>
}
</div>
</div>
</div>
<div class="section col-2 first-larger">
<div class="additional-informations">
<h2 class="header-2">@ResourcesKey.About</h2>
<div class="informations">
@if (Game.ReleaseDate != null)
{
<div class="information">
<span class="body-sm">@ResourcesKey.ReleaseDate</span>
<span class="body-lg">@Game.ReleaseDate?.ToShortDateString()</span>
</div>
}
@if (Game.StorageSpace != null)
{
<div class="information">
<span class="body-sm">@ResourcesKey.StorageSize</span>
<span class="body-lg">@GameHelper.GetFormatedStorageSpace(Game.StorageSpace)</span>
</div>
}
@if (Game.Developer != null)
{
<div class="information">
<span class="body-sm">@ResourcesKey.Developer</span>
<span class="body-lg">@Game.Developer?.Name</span>
</div>
}
@if (Game.Publisher != null)
{
<div class="information">
<span class="body-sm">@ResourcesKey.Publisher</span>
<span class="body-lg">@Game.Publisher?.Name</span>
</div>
}
</div>
</div>
<div class="platforms">
<h2 class="header-2">@ResourcesKey.Platforms</h2>
<div class="pills">
@foreach (var platform in Game.Platforms ?? [])
{
<a class="body-lg pill platform-pill" href="@platform.Url">@platform.Label</a>
}
</div>
</div>
</div>
<div class="section">
</div>
</div>

View File

@@ -0,0 +1,19 @@
using GameIdeas.BlazorApp.Pages.Games.Gateways;
using GameIdeas.Shared.Dto;
using Microsoft.AspNetCore.Components;
namespace GameIdeas.BlazorApp.Pages.Detail;
public partial class GameDetail
{
[Inject] private IGameGateway GameGateway { get; set; } = default!;
[Parameter] public int GameId { get; set; }
private GameDetailDto Game = new();
protected override async Task OnInitializedAsync()
{
Game = await GameGateway.GetGameById(GameId);
await base.OnInitializedAsync();
}
}

View File

@@ -0,0 +1,103 @@
.detail-container, .properties-tags {
display: grid;
grid-gap: 20px;
}
.flex {
display: flex;
gap: 8px;
align-items: center;
}
.section {
padding: 20px 100px;
}
.col-2 {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 40px;
}
.first-larger {
grid-template-columns: 3fr 2fr;
}
.dark {
background: rgb(0, 0, 0, 0.4)
}
.header-1, .header-2 {
width: 100%;
}
.header-2 {
margin-bottom: 20px;
}
.pills, .informations {
display: flex;
flex-wrap: wrap;
gap: 34px;
}
.additional-informations, .platforms {
padding: 20px;
background: var(--input-secondary);
box-shadow: var(--drop-shadow);
border-radius: var(--big-radius);
}
.pill {
width: fit-content;
height: 24px;
padding: 0 6px;
background: rgb(255, 255, 255, 0.2);
border-radius: var(--small-radius);
align-content: center;
}
.platform-pill {
color: var(--violet);
cursor: pointer;
text-decoration: none;
}
.platform-pill:hover {
text-decoration: underline;
}
.square-button {
height: 28px;
min-height: 28px;
width: 28px;
min-width: 28px;
border-radius: var(--small-radius);
background: var(--input-primary);
overflow: hidden;
cursor: pointer;
}
.square-button ::deep svg {
fill: var(--white);
}
.square-button:hover ::deep svg {
background: var(--input-selected);
}
@media screen and (max-width: 1000px) {
.section {
padding: 20px;
}
}
@media screen and (max-width: 700px) {
.col-2 {
grid-template-columns: 1fr;
}
.platforms {
grid-row: 1;
}
}

View File

@@ -1,5 +1,6 @@
@using Blazored.FluentValidation
@using GameIdeas.BlazorApp.Shared.Components.CircleLoader
@using GameIdeas.BlazorApp.Shared.Components.Select.Models
@using GameIdeas.BlazorApp.Shared.Components.SelectSearch
@using GameIdeas.BlazorApp.Shared.Components.Slider
@using GameIdeas.Shared.Dto
@@ -21,16 +22,16 @@
<InputNumber TValue="double?" class="storage" @bind-Value=GameDto.StorageSpace />
</div>
<div class="input-game">
<div class="label">@ResourcesKey.Developers :</div>
<div class="label">@ResourcesKey.Developer :</div>
<SelectSearch TItem="DeveloperDto" Theme="Theme" GetLabel="@(i => i.Name)" QuickAdd=true
Items="Categories?.Developers" @bind-Values=GameDto.Developers
AddItem="@(str => new DeveloperDto() { Name = str })" />
Items="Categories?.Developers" ValuesChanged="HandleDeveloperChanged"
AddItem="@(str => new DeveloperDto() { Name = str })" SelectType="SelectType.Single" />
</div>
<div class="input-game">
<div class="label">@ResourcesKey.Publishers :</div>
<div class="label">@ResourcesKey.Publisher :</div>
<SelectSearch TItem="PublisherDto" Theme="Theme" GetLabel="@(i => i.Name)" QuickAdd=true
Items="Categories?.Publishers" @bind-Values=GameDto.Publishers
AddItem="@(str => new PublisherDto() { Name = str })" />
Items="Categories?.Publishers" ValuesChanged="HandlePublisherChanged"
AddItem="@(str => new PublisherDto() { Name = str })" SelectType="SelectType.Single" />
</div>
</div>
<div class="container">

View File

@@ -75,4 +75,12 @@ public partial class GameCreationForm
StateHasChanged();
}
}
private void HandlePublisherChanged(List<PublisherDto> pubs)
{
GameDto.Publisher = pubs.FirstOrDefault();
}
private void HandleDeveloperChanged(List<DeveloperDto> devs)
{
GameDto.Developer = devs.FirstOrDefault();
}
}

View File

@@ -1,11 +1,12 @@
@using GameIdeas.BlazorApp.Helpers
@using GameIdeas.BlazorApp.Shared.Components.Interest
@using GameIdeas.BlazorApp.Shared.Constants
@inherits GameBase
<div class="row">
<img class="icon" src="~/icon.png" />
<a class="title" href="@($"/Games/Detail/{GameDto.Id}")">@GameDto.Title</a>
<a class="title" href="@($"/Detail/{GameDto.Id}")">@GameDto.Title</a>
<span class="release-date">@(GameDto.ReleaseDate?.ToShortDateString() ?? @ResourcesKey.Unknown)</span>
@@ -30,12 +31,7 @@
<span class="storage">@GameHelper.GetFormatedStorageSpace(GameDto.StorageSpace)</span>
<div class="interest">
<span class="value" style="@($"color: var({GameHelper.GetInterestColor(GameDto.Interest, 5)})")">
@GameDto.Interest
</span>
<span class="max-value">/5</span>
</div>
<Interest Value="GameDto.Interest" />
<button class="detail">@Icons.Triangle</button>
</div>

View File

@@ -11,7 +11,7 @@
overflow: hidden;
}
.row * {
.row > * {
max-height: 64px;
height: fit-content;
padding: 6px 0;
@@ -39,7 +39,7 @@
background: var(--input-selected);
}
.release-date, .storage, .max-value {
.release-date, .storage {
color: rgb(184, 184, 184);
}
@@ -50,7 +50,7 @@
background: rgb(255, 255, 255, 0.2);
border-radius: var(--small-radius);
align-content: center;
}
}
.platforms, .tags {
display: flex;
@@ -80,16 +80,6 @@
fill: var(--white);
}
.value {
font-size: 24px;
font-weight: bold;
}
.max-value {
position: absolute;
transform: translate(2px, 10px);
}
@media screen and (max-width: 1000px) {
.row {
grid-template-columns: 48px 3fr 2fr 3fr 30px 30px;

View File

@@ -16,10 +16,10 @@
<SelectSearch TItem="PropertyDto" Placeholder="@ResourcesKey.Properties" GetLabel="@(p => p.Label)"
@bind-Values=GameFilter.Properties @bind-Values:after=HandleValueChanged Theme="Theme" Items="Categories?.Properties" />
<SelectSearch TItem="DeveloperDto" Placeholder="@ResourcesKey.Developers" GetLabel="@(p => p.Name)"
<SelectSearch TItem="DeveloperDto" Placeholder="@ResourcesKey.Developer" GetLabel="@(p => p.Name)"
@bind-Values=GameFilter.Developers @bind-Values:after=HandleValueChanged Theme="Theme" Items="Categories?.Developers" />
<SelectSearch TItem="PublisherDto" Placeholder="@ResourcesKey.Publishers" GetLabel="@(p => p.Name)"
<SelectSearch TItem="PublisherDto" Placeholder="@ResourcesKey.Publisher" GetLabel="@(p => p.Name)"
@bind-Values=GameFilter.Publishers @bind-Values:after=HandleValueChanged Theme="Theme" Items="Categories?.Publishers" />
<SelectSearch TItem="int" Placeholder="@ResourcesKey.ReleaseDate" GetLabel="@(p => p.ToString())"

View File

@@ -1,5 +1,4 @@
@page "/"
@using GameIdeas.BlazorApp.Layouts
@using GameIdeas.BlazorApp.Pages.Games.Components
@using GameIdeas.BlazorApp.Pages.Games.Filter
@using GameIdeas.BlazorApp.Shared.Components

View File

@@ -62,4 +62,18 @@ public class GameGateway(IHttpClientService httpClientService) : IGameGateway
throw new GameNotFoundException(ResourcesKey.ErrorFetchGames);
}
}
public async Task<GameDetailDto> GetGameById(int gameId)
{
try
{
var result = await httpClientService.FetchDataAsync<GameDetailDto>(Endpoints.Game.FetchById(gameId));
return result ?? throw new InvalidOperationException(ResourcesKey.ErrorFetchGames);
}
catch (Exception)
{
throw new CategoryNotFoundException(ResourcesKey.ErrorFetchGames);
}
}
}

View File

@@ -8,4 +8,5 @@ public interface IGameGateway
Task<CategoriesDto> FetchCategories();
Task<int> CreateGame(GameDetailDto game);
Task<IEnumerable<GameDto>> FetchGames(GameFilterParams filter, int currentPage);
Task<GameDetailDto> GetGameById(int gameId);
}

View File

@@ -1,5 +1,4 @@
@page "/Users"
@using GameIdeas.BlazorApp.Layouts
@using GameIdeas.BlazorApp.Pages.Users.Components
@using GameIdeas.BlazorApp.Shared.Components.Header
@using GameIdeas.BlazorApp.Shared.Components.Popup