From 99ac4581ae20144ff4cec17e794a67db59770359 Mon Sep 17 00:00:00 2001 From: Kira Jiroux Date: Sat, 5 Apr 2025 00:28:36 -0400 Subject: [PATCH] Individual pokemon views are being served and can move to the next pokemon. Edit form can be accessed from either table or individual view and goes back to where it was. --- .../PokemonService/IPokemonService.cs | 5 + .../Services/PokemonService/PokemonService.cs | 26 +++ .../Features/Pokemon/IPokemonRepository.cs | 5 + .../Repositories/PokemonRepository.cs | 68 +++++- .../Components/Component/Loading.razor | 9 + .../Components/Component/Loading.razor.css | 195 ++++++++++++++++++ .../Components/Component/PokemonCard.razor | 2 +- .../Component/PokemonEditButton.razor | 6 + .../Component/PokemonEditButton.razor.cs | 15 ++ .../Components/Component/PokemonTable.razor | 99 +++++---- .../Components/Pages/PokemonCreate.razor | 82 +++++--- .../Components/Pages/PokemonEdit.razor | 42 +++- .../Components/Pages/PokemonEdit.razor.cs | 16 +- .../Components/Pages/PokemonSleep.razor | 2 +- .../Components/Pages/PokemonView.razor | 78 ++++++- .../Components/Pages/PokemonView.razor.cs | 52 +++++ .../Components/Pages/PokemonView.razor.css | 2 + 17 files changed, 608 insertions(+), 96 deletions(-) create mode 100644 Portfolio.WebUI.Server/Components/Component/Loading.razor create mode 100644 Portfolio.WebUI.Server/Components/Component/Loading.razor.css create mode 100644 Portfolio.WebUI.Server/Components/Component/PokemonEditButton.razor create mode 100644 Portfolio.WebUI.Server/Components/Component/PokemonEditButton.razor.cs create mode 100644 Portfolio.WebUI.Server/Components/Pages/PokemonView.razor.cs create mode 100644 Portfolio.WebUI.Server/Components/Pages/PokemonView.razor.css diff --git a/Portfolio.Application/Services/PokemonService/IPokemonService.cs b/Portfolio.Application/Services/PokemonService/IPokemonService.cs index 4be39b9..fc41f5a 100644 --- a/Portfolio.Application/Services/PokemonService/IPokemonService.cs +++ b/Portfolio.Application/Services/PokemonService/IPokemonService.cs @@ -10,7 +10,12 @@ namespace Portfolio.Application.Services.PokemonService public interface IPokemonService { Task> GetAllPokemonAsync(); + Task GetPokemonByPokemonIdAsync(int id); Task GetPokemonByIdAsync(int id); + Task> GetAllPokemonIdsAsync(); + Task GetPreviousPokemonIdAsync(int id); + Task GetNextPokemonIdAsync(int id); + Task GetVariationPokemonIdAsync(int id); Task AddPokemonAsync(Pokemon pokemon); Task DeletePokemonAsync(int pokemonId); Task UpdatePokemonAsync(Pokemon pokemon); diff --git a/Portfolio.Application/Services/PokemonService/PokemonService.cs b/Portfolio.Application/Services/PokemonService/PokemonService.cs index 72f34ff..024230f 100644 --- a/Portfolio.Application/Services/PokemonService/PokemonService.cs +++ b/Portfolio.Application/Services/PokemonService/PokemonService.cs @@ -33,14 +33,40 @@ namespace Portfolio.Application.Services.PokemonService } + public async Task> GetAllPokemonIdsAsync() + { + return await _pokemonRepository.GetAllPokemonIdsAsync(); + } + + public async Task GetNextPokemonIdAsync(int id) + { + return await _pokemonRepository.GetNextPokemonIdAsync(id); + } + + public async Task GetPokemonByPokemonIdAsync(int id) + { + return await _pokemonRepository.GetPokemonByPokemonIdAsync(id); + } + public async Task GetPokemonByIdAsync(int id) { return await _pokemonRepository.GetPokemonByIdAsync(id); } + public async Task GetPreviousPokemonIdAsync(int id) + { + return await _pokemonRepository.GetPreviousPokemonIdAsync(id); + + } + public async Task UpdatePokemonAsync(Pokemon pokemon) { await _pokemonRepository.UpdatePokemonAsync(pokemon); } + + public async Task GetVariationPokemonIdAsync(int id) + { + return await _pokemonRepository.GetVariationPokemonIdAsync(id); + } } } diff --git a/Portfolio.Domain/Features/Pokemon/IPokemonRepository.cs b/Portfolio.Domain/Features/Pokemon/IPokemonRepository.cs index 3fb4fb6..277f39f 100644 --- a/Portfolio.Domain/Features/Pokemon/IPokemonRepository.cs +++ b/Portfolio.Domain/Features/Pokemon/IPokemonRepository.cs @@ -10,6 +10,11 @@ namespace Portfolio.Domain.Features.Pokemon { Task> GetAllPokemonsAsync(); Task GetPokemonByIdAsync(int id); + Task GetPokemonByPokemonIdAsync(int id); + Task> GetAllPokemonIdsAsync(); + Task GetPreviousPokemonIdAsync(int currentPokemonId); + Task GetNextPokemonIdAsync(int currentPokemonId); + Task GetVariationPokemonIdAsync(int pokemonId); Task AddPokemonAsync(Pokemon pokemon); Task DeletePokemonAsync(int pokemonId); Task UpdatePokemonAsync(Pokemon pokemon); diff --git a/Portfolio.Infrastructure/Repositories/PokemonRepository.cs b/Portfolio.Infrastructure/Repositories/PokemonRepository.cs index bb3649d..22ed845 100644 --- a/Portfolio.Infrastructure/Repositories/PokemonRepository.cs +++ b/Portfolio.Infrastructure/Repositories/PokemonRepository.cs @@ -21,11 +21,14 @@ namespace Portfolio.Infrastructure.Repositories { return await _context.Pokemons.ToListAsync(); } + public async Task GetPokemonByPokemonIdAsync(int id) + { + return await _context.Pokemons.FirstOrDefaultAsync(p => p.PokemonId == id); + } public async Task GetPokemonByIdAsync(int id) { return await _context.Pokemons.FirstOrDefaultAsync(p => p.Id == id); } - public async Task AddPokemonAsync(Pokemon pokemon) { _context.Pokemons.Add(pokemon); @@ -46,5 +49,68 @@ namespace Portfolio.Infrastructure.Repositories _context.Pokemons.Update(pokemon); await _context.SaveChangesAsync(); } + + public async Task GetPreviousPokemonIdAsync(int currentPokemonId) + { + // Get the previous Pokémon's PokemonId + var prevPokemonId = await _context.Pokemons + .Where(p => p.PokemonId < currentPokemonId) + .OrderByDescending(p => p.PokemonId) + .Select(p => (int?)p.PokemonId) + .FirstOrDefaultAsync(); + + // If no previous PokemonId is found, wrap around to the last one + if (prevPokemonId == null) + { + prevPokemonId = await _context.Pokemons + .OrderByDescending(p => p.PokemonId) // Get the last PokemonId + .Select(p => (int?)p.PokemonId) + .FirstOrDefaultAsync(); + } + + return prevPokemonId; + } + + public async Task GetNextPokemonIdAsync(int currentPokemonId) + { + // Get the next Pokémon's PokemonId + var nextPokemonId = await _context.Pokemons + .Where(p => p.PokemonId > currentPokemonId) + .OrderBy(p => p.PokemonId) + .Select(p => (int?)p.PokemonId) + .FirstOrDefaultAsync(); + + // If no next PokemonId is found, wrap around to the first one + if (nextPokemonId == null) + { + nextPokemonId = await _context.Pokemons + .OrderBy(p => p.PokemonId) // Get the first PokemonId + .Select(p => (int?)p.PokemonId) + .FirstOrDefaultAsync(); + } + + return nextPokemonId; + } + + public async Task> GetAllPokemonIdsAsync() + { + return await _context.Pokemons + .OrderBy(p => p.PokemonId) // Ensure it's ordered by PokemonId + .Select(p => p.PokemonId) + .ToListAsync(); + } + + public async Task GetVariationPokemonIdAsync(int pokemonId) + { + // Find a variation for the given PokemonId (where IsVariation is true) + var variation = await _context.Pokemons + .Where(p => p.PokemonId == pokemonId && p.IsVariation) + .FirstOrDefaultAsync(); + + // Return the Id of the variation, or null if no variation is found + return variation?.Id; + } + + } } diff --git a/Portfolio.WebUI.Server/Components/Component/Loading.razor b/Portfolio.WebUI.Server/Components/Component/Loading.razor new file mode 100644 index 0000000..7e8f1c3 --- /dev/null +++ b/Portfolio.WebUI.Server/Components/Component/Loading.razor @@ -0,0 +1,9 @@ +
+
G
+
N
+
I
+
D
+
A
+
O
+
L
+
\ No newline at end of file diff --git a/Portfolio.WebUI.Server/Components/Component/Loading.razor.css b/Portfolio.WebUI.Server/Components/Component/Loading.razor.css new file mode 100644 index 0000000..f64eb57 --- /dev/null +++ b/Portfolio.WebUI.Server/Components/Component/Loading.razor.css @@ -0,0 +1,195 @@ +body { + background: #000; +} + +#load { + position: absolute; + width: 600px; + height: 36px; + left: 50%; + top: 40%; + margin-left: -300px; + overflow: visible; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: default; +} + + #load div { + position: absolute; + width: 20px; + height: 36px; + opacity: 0; + font-family: Helvetica, Arial, sans-serif; + animation: move 3s linear infinite; + -o-animation: move 3s linear infinite; + -moz-animation: move 3s linear infinite; + -webkit-animation: move 3s linear infinite; + transform: rotate(180deg); + -o-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -webkit-transform: rotate(180deg); + color: #35C4F0; + } + + #load div:nth-child(2) { + animation-delay: 0.2s; + -o-animation-delay: 0.2s; + -moz-animation-delay: 0.2s; + -webkit-animation-delay: 0.2s; + } + + #load div:nth-child(3) { + animation-delay: 0.4s; + -o-animation-delay: 0.4s; + -webkit-animation-delay: 0.4s; + -webkit-animation-delay: 0.4s; + } + + #load div:nth-child(4) { + animation-delay: 0.6s; + -o-animation-delay: 0.6s; + -moz-animation-delay: 0.6s; + -webkit-animation-delay: 0.6s; + } + + #load div:nth-child(5) { + animation-delay: 0.8s; + -o-animation-delay: 0.8s; + -moz-animation-delay: 0.8s; + -webkit-animation-delay: 0.8s; + } + + #load div:nth-child(6) { + animation-delay: 1s; + -o-animation-delay: 1s; + -moz-animation-delay: 1s; + -webkit-animation-delay: 1s; + } + + #load div:nth-child(7) { + animation-delay: 1.2s; + -o-animation-delay: 1.2s; + -moz-animation-delay: 1.2s; + -webkit-animation-delay: 1.2s; + } + +@keyframes move { + 0% { + left: 0; + opacity: 0; + } + + 35% { + left: 41%; + -moz-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + -o-transform: rotate(0deg); + transform: rotate(0deg); + opacity: 1; + } + + 65% { + left: 59%; + -moz-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + -o-transform: rotate(0deg); + transform: rotate(0deg); + opacity: 1; + } + + 100% { + left: 100%; + -moz-transform: rotate(-180deg); + -webkit-transform: rotate(-180deg); + -o-transform: rotate(-180deg); + transform: rotate(-180deg); + opacity: 0; + } +} + +@-moz-keyframes move { + 0% { + left: 0; + opacity: 0; + } + + 35% { + left: 41%; + -moz-transform: rotate(0deg); + transform: rotate(0deg); + opacity: 1; + } + + 65% { + left: 59%; + -moz-transform: rotate(0deg); + transform: rotate(0deg); + opacity: 1; + } + + 100% { + left: 100%; + -moz-transform: rotate(-180deg); + transform: rotate(-180deg); + opacity: 0; + } +} + +@-webkit-keyframes move { + 0% { + left: 0; + opacity: 0; + } + + 35% { + left: 41%; + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + opacity: 1; + } + + 65% { + left: 59%; + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + opacity: 1; + } + + 100% { + left: 100%; + -webkit-transform: rotate(-180deg); + transform: rotate(-180deg); + opacity: 0; + } +} + +@-o-keyframes move { + 0% { + left: 0; + opacity: 0; + } + + 35% { + left: 41%; + -o-transform: rotate(0deg); + transform: rotate(0deg); + opacity: 1; + } + + 65% { + left: 59%; + -o-transform: rotate(0deg); + transform: rotate(0deg); + opacity: 1; + } + + 100% { + left: 100%; + -o-transform: rotate(-180deg); + transform: rotate(-180deg); + opacity: 0; + } +} diff --git a/Portfolio.WebUI.Server/Components/Component/PokemonCard.razor b/Portfolio.WebUI.Server/Components/Component/PokemonCard.razor index 17bc90f..46a1c84 100644 --- a/Portfolio.WebUI.Server/Components/Component/PokemonCard.razor +++ b/Portfolio.WebUI.Server/Components/Component/PokemonCard.razor @@ -28,7 +28,7 @@ -
+
@if (string.IsNullOrEmpty(_pokemon.FlavorText)) {

[ Pokemon Flavor Text Placeholder ]

diff --git a/Portfolio.WebUI.Server/Components/Component/PokemonEditButton.razor b/Portfolio.WebUI.Server/Components/Component/PokemonEditButton.razor new file mode 100644 index 0000000..f02aa0f --- /dev/null +++ b/Portfolio.WebUI.Server/Components/Component/PokemonEditButton.razor @@ -0,0 +1,6 @@ +@inject NavigationManager Navigation + \ No newline at end of file diff --git a/Portfolio.WebUI.Server/Components/Component/PokemonEditButton.razor.cs b/Portfolio.WebUI.Server/Components/Component/PokemonEditButton.razor.cs new file mode 100644 index 0000000..4f8b34b --- /dev/null +++ b/Portfolio.WebUI.Server/Components/Component/PokemonEditButton.razor.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.EntityFrameworkCore.Metadata.Internal; + +namespace Portfolio.WebUI.Server.Components.Component +{ + public partial class PokemonEditButton + { + [Parameter] public int PokemonId { get; set; } + + private void EditPokemon(int PokemonId) + { + Navigation.NavigateTo($"/pokemonsleep/edit/{PokemonId}"); + } + } +} diff --git a/Portfolio.WebUI.Server/Components/Component/PokemonTable.razor b/Portfolio.WebUI.Server/Components/Component/PokemonTable.razor index d6b3b13..e6098fa 100644 --- a/Portfolio.WebUI.Server/Components/Component/PokemonTable.razor +++ b/Portfolio.WebUI.Server/Components/Component/PokemonTable.razor @@ -6,62 +6,63 @@ @rendermode InteractiveServer - -@if (pokemons == null) -{ -

Loading...

-} -else -{ - -
+ +
- -
-
-
-

Available Pokémon

-

@(pokemons.Count()) Pokemon

-
+ +
+
+
+

Available Pokémon

+

@(pokemons.Count()) Pokemon

+
- -
- - - - - - - - - - - - - - + +
+
#PokemonTypeSleep TypeSpeciality
+ + + + + + + + + + + + + @if(pokemons == null) + { + + + + } + else + { @foreach (var pokemon in pokemons) { - + @{ string baseUrl = pokemon.PokemonImageUrl; string shinyUrl = pokemon.PokemonShinyImageUrl; } @@ -83,22 +84,22 @@ else { @if (pokemon.VariationName == "Alolan") { - + } @if (pokemon.VariationName == "Paldean") { - + } } else // Otherwise, Base Case { - + } - @@ -115,14 +116,10 @@ else } + } + -
#PokemonTypeSleep TypeSpeciality
- @if(shinyUrl == null){ + @if (shinyUrl == null) + {
- + } else { @@ -71,7 +72,7 @@ else - + }
Alolan @pokemon.PokemonName Alolan @pokemon.PokemonName Paldean @pokemon.PokemonName Paldean @pokemon.PokemonName @pokemon.PokemonName @pokemon.PokemonName -
+
+
- +
-
+
+
+ -} diff --git a/Portfolio.WebUI.Server/Components/Pages/PokemonCreate.razor b/Portfolio.WebUI.Server/Components/Pages/PokemonCreate.razor index 0d6ea81..c1af132 100644 --- a/Portfolio.WebUI.Server/Components/Pages/PokemonCreate.razor +++ b/Portfolio.WebUI.Server/Components/Pages/PokemonCreate.razor @@ -33,7 +33,7 @@ else -
+
# @@ -43,6 +43,53 @@ else
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+
+
@@ -59,31 +106,6 @@ else
- - - -
- - - - - - - -
- - -
- - - - - - - - -
-
@@ -93,8 +115,12 @@ else
- - + + +
+ + +
diff --git a/Portfolio.WebUI.Server/Components/Pages/PokemonEdit.razor b/Portfolio.WebUI.Server/Components/Pages/PokemonEdit.razor index ee8123c..7c91440 100644 --- a/Portfolio.WebUI.Server/Components/Pages/PokemonEdit.razor +++ b/Portfolio.WebUI.Server/Components/Pages/PokemonEdit.razor @@ -1,6 +1,7 @@ @page "/pokemonsleep/edit/{id:int}" @inject IPokemonService PokemonService @inject NavigationManager Navigation +@inject IJSRuntime JSRuntime @attribute [StreamRendering] @rendermode InteractiveServer @@ -31,7 +32,8 @@ else -
+ +
# @@ -39,7 +41,9 @@ else
+
+
@@ -64,7 +68,7 @@ else
- +
@@ -73,7 +77,7 @@ else
- +
@@ -83,30 +87,46 @@ else
+ +
+
+ +
+ + Variation? +
+ + +
+ +
+
+
- +
+
- +
- - + +
+ + +
} - -@code { - -} \ No newline at end of file diff --git a/Portfolio.WebUI.Server/Components/Pages/PokemonEdit.razor.cs b/Portfolio.WebUI.Server/Components/Pages/PokemonEdit.razor.cs index e2c71aa..f45d4e0 100644 --- a/Portfolio.WebUI.Server/Components/Pages/PokemonEdit.razor.cs +++ b/Portfolio.WebUI.Server/Components/Pages/PokemonEdit.razor.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; using Portfolio.Domain.Features.Pokemon; namespace Portfolio.WebUI.Server.Components.Pages @@ -16,12 +17,21 @@ namespace Portfolio.WebUI.Server.Components.Pages private async Task HandleSubmit() { await PokemonService.UpdatePokemonAsync(pokemon); - Navigation.NavigateTo("/pokemonsleep"); + //Navigation.NavigateTo("/pokemonsleep"); + await JSRuntime.InvokeVoidAsync("history.back"); } - private void Cancel() + private async void Cancel() { - Navigation.NavigateTo("/pokemonsleep"); + await JSRuntime.InvokeVoidAsync("history.back"); + } + + private bool HideLabel { get; set; } = true; + private void Toggle() + { + HideLabel = !HideLabel; + } + } } diff --git a/Portfolio.WebUI.Server/Components/Pages/PokemonSleep.razor b/Portfolio.WebUI.Server/Components/Pages/PokemonSleep.razor index 33f537c..5ffde90 100644 --- a/Portfolio.WebUI.Server/Components/Pages/PokemonSleep.razor +++ b/Portfolio.WebUI.Server/Components/Pages/PokemonSleep.razor @@ -9,7 +9,7 @@ Pokémon Sleep
- + diff --git a/Portfolio.WebUI.Server/Components/Pages/PokemonView.razor b/Portfolio.WebUI.Server/Components/Pages/PokemonView.razor index bc52d30..09e24ad 100644 --- a/Portfolio.WebUI.Server/Components/Pages/PokemonView.razor +++ b/Portfolio.WebUI.Server/Components/Pages/PokemonView.razor @@ -1,5 +1,81 @@ @page "/pokemonsleep/pokemon/{id:int}" +@inject IPokemonService PokemonService +@inject NavigationManager Navigation -@code { +@attribute [StreamRendering] +@rendermode InteractiveServer + + + +@if (_pokemon == null) +{ +

Loading...

+} +else +{ + @_pokemon.PokemonName + + +
+
+ +
+ @if (_variationPokemonId != null) + { + @if (_variationPokemonId != null && _pokemonVariant == null){ +

Loading...

+ } + else + { + @if(_pokemon.Id != _pokemonVariant.Id) + { +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + } + else + { +
+ +
+ +
+
+ + } + } + } + else{ +
+ +
+ +
+
+ } +
+ +
+
} diff --git a/Portfolio.WebUI.Server/Components/Pages/PokemonView.razor.cs b/Portfolio.WebUI.Server/Components/Pages/PokemonView.razor.cs new file mode 100644 index 0000000..10341e0 --- /dev/null +++ b/Portfolio.WebUI.Server/Components/Pages/PokemonView.razor.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Components; +using Portfolio.Domain.Features.Pokemon; + +namespace Portfolio.WebUI.Server.Components.Pages +{ + public partial class PokemonView + { + [Parameter] public int Id { get; set; } + private Pokemon? _pokemon; + private Pokemon? _pokemonVariant; + private List _pokemonIds; + private int? _nextPokemonId; + private int? _previousPokemonId; + private int? _variationPokemonId; + private int _currentIndex; + + + protected override async Task OnParametersSetAsync() + { + _pokemon = await PokemonService.GetPokemonByPokemonIdAsync(Id); + + // These can be smart queries if your data is sorted by ID or by another property + _pokemonIds = await PokemonService.GetAllPokemonIdsAsync(); + _currentIndex = _pokemonIds.IndexOf(_pokemon.PokemonId); + //Console.WriteLine(_currentIndex); + + _nextPokemonId = await PokemonService.GetNextPokemonIdAsync(Id); + _previousPokemonId = await PokemonService.GetPreviousPokemonIdAsync(Id); + + _variationPokemonId = await PokemonService.GetVariationPokemonIdAsync(Id); + if (_variationPokemonId != null) + { + Console.WriteLine(_variationPokemonId); + _pokemonVariant = await PokemonService.GetPokemonByIdAsync((int)_variationPokemonId); + Console.WriteLine(_pokemonVariant.VariationName); + } + } + + private void NavigateToNext() + { + if (_nextPokemonId.HasValue) + Navigation.NavigateTo($"/pokemonsleep/pokemon/{_nextPokemonId.Value}"); + } + + private void NavigateToPrevious() + { + if (_previousPokemonId.HasValue) + Navigation.NavigateTo($"/pokemonsleep/pokemon/{_previousPokemonId.Value}"); + } + + } +} diff --git a/Portfolio.WebUI.Server/Components/Pages/PokemonView.razor.css b/Portfolio.WebUI.Server/Components/Pages/PokemonView.razor.css new file mode 100644 index 0000000..46800d1 --- /dev/null +++ b/Portfolio.WebUI.Server/Components/Pages/PokemonView.razor.css @@ -0,0 +1,2 @@ +body { +}