Compare commits

...

4 Commits

29 changed files with 1132 additions and 585 deletions

View File

@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Components;
namespace Portfolio.WebUI.Server.Components.Component
{
public partial class Loading
{
[Parameter]
public string LoadingString { get; set; }
}
}

View File

@ -0,0 +1,19 @@
<div class="add-card card border-2 border-secondary-subtle rounded-4"
role="button"
tabindex="0"
@onclick="HandleClick">
<div class="card-body d-flex align-items-center justify-content-center p-4">
<svg xmlns="http://www.w3.org/2000/svg"
width="@IconSize"
height="@IconSize"
fill="currentColor"
class="bi bi-plus-circle-fill"
viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3z" />
</svg>
</div>
</div>
@code {
}

View File

@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components;
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
public partial class PokemonAddButton
{
[Parameter]
public EventCallback OnAdd { get; set; }
[Parameter]
public int IconSize { get; set; } = 64;
private async Task HandleClick()
{
await OnAdd.InvokeAsync();
}
}
}

View File

@ -0,0 +1,22 @@
.add-card {
min-width: 160px;
min-height: 120px;
max-width: 200px;
cursor: pointer;
background-color: var(--bs-info-subtle);
transition: transform .08s ease, box-shadow .08s ease, background-color .08s ease;
}
.add-card:hover {
background-color: var(--bs-light);
transform: translateY(-1px);
}
.add-card:focus {
outline: none;
box-shadow: 0 0 0 .25rem rgba(13,110,253,.25);
}
.add-card[aria-disabled="true"] {
cursor: not-allowed;
}

View File

@ -5,7 +5,7 @@ using System.Text.Json;
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
partial class PokemonDownload
partial class PokemonDownloadButton
{
[Parameter]
public List<Pokemon> _Pokemon { get; set; }

View File

@ -0,0 +1,308 @@
@inject IPokemonService PokemonService
@if(formUse == "ADD")
{
<div class="pokemon-form-container m-auto bg-info border border-5 border-info-subtle rounded-4 p-3">
<EditForm class="col" Model="NewPokemon">
<DataAnnotationsValidator />
<div class="bg-primary-subtle rounded"><p class="fs-3 fw-light text-center card-title">New Pokemon</p></div>
<!-- Pokemon Number and Name -->
<div class="row mt-1">
<div class="col input-group mb-2">
<span class="input-group-text text-sm-center rounded-start">#</span>
<InputNumber min="1"
placeholder="Pokedex #"
id="PokemonId"
@bind-Value="NewPokemon.PokemonId"
@onchange="@SendPokemon"
class="form-control "
type="number" />
<InputText placeholder="Pokemon Name"
id="PokemonName"
@bind-Value="NewPokemon.PokemonName"
@onchange="@SendPokemon"
class="form-control w-50 rounded-end" />
</div>
</div>
<!-- Variation Check -->
<div class="d-flex flex-row justify-content-start input-group ">
<InputCheckbox id="IsVariation"
@bind-Value="NewPokemon.IsVariation"
@onclick="@Toggle"
@onchange="@SendPokemon"
class="form-check-input p-3 rounded" />
<span class="input-group-text ms-1 @GetRoundingClass()">Variation?</span>
<InputText placeholder="How So?"
id="VariationName"
@bind-Value="NewPokemon.VariationName"
@onchange="@SendPokemon"
class="form-control rounded-end"
hidden="@HideLabel" />
</div>
<!-- <br> -->
<div class="border-bottom border-3 border-info-subtle rounded m-1 my-3"></div>
<!-- Pokemon Type -->
<div class="row mb-2">
<div class="input-group m-auto">
<span for="PokemonType" class="input-group-text rounded-start">Pokemon Type</span>
<InputSelect id="PokemonType" @bind-Value="NewPokemon.PokemonType" @onchange="@SendPokemon" class="form-select rounded-end">
<option disabled value="" selected>Select...</option>
@foreach (var pt in PokemonTypes)
{
<option value="@pt">@pt</option>
}
</InputSelect>
</div>
</div>
<!-- Pokemon Sleep Type, Specialty -->
<div class="row mb-3 mx-0">
<!-- Sleep Type -->
<div class="col ps-0 pe-1">
<div class="row input-group m-auto">
<span for="SleepType" class="input-group-text rounded-top">Sleep Type</span>
</div>
<div class="row input-group m-auto">
<InputSelect id="SleepType" @bind-Value="NewPokemon.SleepType" @onchange="@SendPokemon" class="form-select rounded-bottom">
<option disabled value="" selected>Select...</option>
@foreach (var st in SleepTypes)
{
<option value="@st">@st</option>
}
</InputSelect>
</div>
</div>
<!-- Speciality -->
<div class="col ps-1 pe-0">
<div class="row input-group m-auto">
<span for="Speciality" class="input-group-text rounded-top">Specialty</span>
</div>
<div class="row input-group m-auto">
<InputSelect id="Speciality" @bind-Value="NewPokemon.Speciality" @onchange="@SendPokemon" class="form-select rounded-bottom">
<option disabled value="" selected>Select...</option>
@foreach (var sp in Specialities)
{
<option value="@sp">@sp</option>
}
</InputSelect>
</div>
</div>
</div>
<!-- <br> -->
<div class="border-bottom border-3 border-info-subtle rounded m-1 my-3"></div>
<!-- Images -->
<div class="row mb-2">
<div class="input-group m-auto">
<span for="ImageUrl" class="input-group-text rounded-start">Base Image URL</span>
<InputText id="ImageUrl" @bind-Value="NewPokemon.PokemonImageUrl" @onchange="@SendPokemon" class="form-control rounded-end" />
</div>
</div>
<div class="row mb-2">
<div class="input-group m-auto">
<span for="ShinyImageUrl" class="input-group-text rounded-start">Shiny Image URL</span>
<InputText id="ShinyImageUrl" @bind-Value="NewPokemon.PokemonShinyImageUrl" @onchange="@SendPokemon" class="form-control rounded-end" />
</div>
</div>
<!-- Flavor -->
<div class="row mb-2">
<div class="input-group m-auto">
<span for="FlavorText" class="input-group-text rounded-start">Flavor Text</span>
<InputText id="FlavorText" @bind-Value="NewPokemon.FlavorText" @onchange="@SendPokemon" class="form-control rounded-end" />
</div>
</div>
<!-- <br> -->
<div class="border-bottom border-3 border-info-subtle rounded m-1 my-3"></div>
@if (showErrors && !IsComplete)
{
<div class="alert alert-warning mt-2">
Please complete: @string.Join(", ", MissingFields())
</div>
}
<div class="d-flex mt-3 justify-content-center gap-3">
@if (mostRecentForm)
{
<button type="button"
class="btn btn-danger rounded rounded-5 p-1"
@onclick="@HandleRemove">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-x-circle" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16" />
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708" />
</svg>
</button>
}
else
{
<button type="button"
class="btn btn-danger rounded rounded-5 p-1"
disabled>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-x-circle" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16" />
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708" />
</svg>
</button>
}
</div>
</EditForm>
</div>
}
else if (formUse == "EDIT")
{
<div class="pokemon-form-container m-auto bg-info border border-5 border-info-subtle rounded-4 p-3">
<EditForm class="col" Model="PokemonToEdit">
<DataAnnotationsValidator />
<div class="bg-primary-subtle rounded"><p class="fs-3 fw-light text-center card-title">Edit Pokemon</p></div>
<!-- Pokemon Number and Name -->
<div class="row mt-1">
<div class="col input-group mb-2">
<span class="input-group-text text-sm-center rounded-start">#</span>
<InputNumber min="1"
placeholder="Pokedex #"
id="PokemonId"
@bind-Value="PokemonToEdit.PokemonId"
@onchange="@SendPokemon"
class="form-control "
type="number" />
<InputText placeholder="Pokemon Name"
id="PokemonName"
@bind-Value="PokemonToEdit.PokemonName"
@onchange="@SendPokemon"
class="form-control w-50 rounded-end" />
</div>
</div>
<!-- Variation Check -->
<div class="d-flex flex-row justify-content-start input-group ">
<InputCheckbox id="IsVariation"
@bind-Value="PokemonToEdit.IsVariation"
@onclick="@Toggle"
@onchange="@SendPokemon"
class="form-check-input p-3 rounded" />
<span class="input-group-text ms-1 @GetRoundingClass()">Variation?</span>
<InputText placeholder="How So?"
id="VariationName"
@bind-Value="PokemonToEdit.VariationName"
@onchange="@SendPokemon"
class="form-control rounded-end"
hidden="@HideLabel" />
</div>
<!-- <br> -->
<div class="border-bottom border-3 border-info-subtle rounded m-1 my-3"></div>
<!-- Pokemon Type -->
<div class="row mb-2">
<div class="input-group m-auto">
<span for="PokemonType" class="input-group-text rounded-start">Pokemon Type</span>
<InputSelect id="PokemonType" @bind-Value="PokemonToEdit.PokemonType" class="form-select rounded-end" @onchange="@SendPokemon">
<option disabled value="" selected>Select...</option>
@foreach (var pt in PokemonTypes)
{
<option value="@pt">@pt</option>
}
</InputSelect>
</div>
</div>
<!-- Pokemon Sleep Type, Specialty -->
<div class="row mb-3 mx-0">
<!-- Sleep Type -->
<div class="col ps-0 pe-1">
<div class="row input-group m-auto">
<span for="SleepType" class="input-group-text rounded-top">Sleep Type</span>
</div>
<div class="row input-group m-auto">
<InputSelect id="SleepType" @bind-Value="PokemonToEdit.SleepType" class="form-select rounded-bottom" @onchange="@SendPokemon">
<option disabled value="" selected>Select...</option>
@foreach (var st in SleepTypes)
{
<option value="@st">@st</option>
}
</InputSelect>
</div>
</div>
<!-- Speciality -->
<div class="col ps-1 pe-0">
<div class="row input-group m-auto">
<span for="Speciality" class="input-group-text rounded-top">Specialty</span>
</div>
<div class="row input-group m-auto">
<InputSelect id="Speciality" @bind-Value="PokemonToEdit.Speciality" class="form-select rounded-bottom" @onchange="@SendPokemon">
<option disabled value="" selected>Select...</option>
@foreach (var sp in Specialities)
{
<option value="@sp">@sp</option>
}
</InputSelect>
</div>
</div>
</div>
<!-- <br> -->
<div class="border-bottom border-3 border-info-subtle rounded m-1 my-3"></div>
<!-- Images -->
<div class="row mb-2">
<div class="input-group m-auto">
<span for="ImageUrl" class="input-group-text rounded-start">Base Image URL</span>
<InputText id="ImageUrl" @bind-Value="PokemonToEdit.PokemonImageUrl" class="form-control rounded-end" @onchange="@SendPokemon" />
</div>
</div>
<div class="row mb-2">
<div class="input-group m-auto">
<span for="ShinyImageUrl" class="input-group-text rounded-start">Shiny Image URL</span>
<InputText id="ShinyImageUrl" @bind-Value="PokemonToEdit.PokemonShinyImageUrl" class="form-control rounded-end" @onchange="@SendPokemon" />
</div>
</div>
<!-- Flavor -->
<div class="row mb-2">
<div class="input-group m-auto">
<span for="FlavorText" class="input-group-text rounded-start">Flavor Text</span>
<InputText id="FlavorText" @bind-Value="PokemonToEdit.FlavorText" class="form-control rounded-end" @onchange="@SendPokemon" />
</div>
</div>
<!-- <br> -->
<div class="border-bottom border-3 border-info-subtle rounded m-1 my-3"></div>
@if (showErrors && !IsComplete)
{
<div class="alert alert-warning mt-2">
Please complete: @string.Join(", ", MissingFields())
</div>
}
<div class="d-flex mt-3 justify-content-center gap-3">
@if (mostRecentForm)
{
<button type="button"
class="btn btn-danger rounded rounded-5 p-1"
@onclick="@HandleRemove">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-x-circle" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16" />
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708" />
</svg>
</button>
}
</div>
</EditForm>
</div>
}

View File

@ -0,0 +1,162 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Portfolio.Application.Services.PokemonService;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
public partial class PokemonForm
{
// To Add or Edit Pokemon
[Parameter]
public string formUse { get; set; }
[Parameter]
public EventCallback<Pokemon> OnPokemonReady { get; set; }
[Parameter]
public EventCallback RemoveForm { get; set; }
// When Adding
[Parameter]
public bool mostRecentForm { get; set; }
private Pokemon NewPokemon = new Pokemon
{
PokemonId = 0, // Or any default ID logic
PokemonName = string.Empty, // Required fields cannot be null
SleepType = string.Empty,
Speciality = string.Empty,
IsVariation = false
};
// When Editing
[Parameter]
public Pokemon? PokemonToEdit { get; set; }
private int PokemonToEditId { get; set; }
// General Form
protected static readonly string[] PokemonTypes = new[]
{
"Grass","Fire","Water","Normal","Flying","Bug","Poison","Electric","Ground","Rock","Ice",
"Steel","Fighting","Psychic","Dark","Fairy","Ghost","Dragon"
};
protected static readonly string[] SleepTypes = new[] { "Dozing", "Snoozing", "Slumbering" };
protected static readonly string[] Specialities = new[] { "Berries", "Ingredients", "Skills", "All" };
private bool HideLabel { get; set; }
private bool showErrors { get; set; } = false;
protected override async Task OnInitializedAsync()
{
if (formUse == "EDIT")
{
if (PokemonToEdit.IsVariation == true)
{
HideLabel = false;
}
PokemonToEditId = PokemonToEdit.Id;
}
else
{
HideLabel = true;
}
}
private void Toggle()
{
HideLabel = !HideLabel;
}
// CSS-function to get proper styling for form elements
private string GetRoundingClass()
{
if (!HideLabel)
{
return "rounded-start";
}
return "rounded-start rounded-end";
}
// Minimal "complete" check (no data annotations needed)
private bool IsComplete =>
NewPokemon.PokemonId > 0 &&
!string.IsNullOrWhiteSpace(NewPokemon.PokemonName) &&
!string.IsNullOrWhiteSpace(NewPokemon.PokemonType) &&
!string.IsNullOrWhiteSpace(NewPokemon.SleepType) &&
!string.IsNullOrWhiteSpace(NewPokemon.Speciality) &&
(!NewPokemon.IsVariation || !string.IsNullOrWhiteSpace(NewPokemon.VariationName));
private IEnumerable<string> MissingFields()
{
if (NewPokemon.PokemonId <= 0) yield return "Pokédex #";
if (string.IsNullOrWhiteSpace(NewPokemon.PokemonName)) yield return "Name";
if (string.IsNullOrWhiteSpace(NewPokemon.PokemonType)) yield return "Type";
if (string.IsNullOrWhiteSpace(NewPokemon.SleepType)) yield return "Sleep Type";
if (string.IsNullOrWhiteSpace(NewPokemon.Speciality)) yield return "Specialty";
if (NewPokemon.IsVariation && string.IsNullOrWhiteSpace(NewPokemon.VariationName)) yield return "Variation Name";
}
private async Task HandleRemove()
{
await RemoveForm.InvokeAsync();
}
private async Task SendPokemon()
{
if(formUse == "ADD")
{
if (!IsComplete)
{
showErrors = true;
StateHasChanged();
return;
}
// Optionally send a copy to avoid later mutation by the child
var copy = new Pokemon
{
PokemonId = NewPokemon.PokemonId,
PokemonName = NewPokemon.PokemonName,
PokemonType = NewPokemon.PokemonType,
SleepType = NewPokemon.SleepType,
Speciality = NewPokemon.Speciality,
IsVariation = NewPokemon.IsVariation,
VariationName = NewPokemon.VariationName,
PokemonImageUrl = NewPokemon.PokemonImageUrl,
PokemonShinyImageUrl = NewPokemon.PokemonShinyImageUrl,
FlavorText = NewPokemon.FlavorText
};
await OnPokemonReady.InvokeAsync(copy);
}
else
{
// Optionally send a copy to avoid later mutation by the child
var edit = new Pokemon
{
Id = PokemonToEditId,
PokemonId = PokemonToEdit.PokemonId,
PokemonName = PokemonToEdit.PokemonName,
PokemonType = PokemonToEdit.PokemonType,
SleepType = PokemonToEdit.SleepType,
Speciality = PokemonToEdit.Speciality,
IsVariation = PokemonToEdit.IsVariation,
VariationName = PokemonToEdit.VariationName,
PokemonImageUrl = PokemonToEdit.PokemonImageUrl,
PokemonShinyImageUrl = PokemonToEdit.PokemonShinyImageUrl,
FlavorText = PokemonToEdit.FlavorText
};
await OnPokemonReady.InvokeAsync(edit);
Console.WriteLine(edit);
}
}
}
}

View File

@ -0,0 +1,25 @@

$display-font-sizes: (
1: 5rem,
2: 4.5rem,
3: 4rem,
4: 3.5rem,
5: 3rem,
6: 2.5rem
);
.pokemon-form-container {
position: relative;
aspect-ratio: 3 / 4; /* Maintains card shape dynamically */
background-color: var(--bg-color);
border-width: .5rem;
border-style: solid;
border-radius: 5% / 3.5%;
border-color: var(--border-color);
box-shadow: 0 0 10px var(--border-color);
}
.checkbox-styling {
width: 100px;
height: 100px;
}

View File

@ -1,16 +1,16 @@

<!-- Search Input -->
<div class="pokemon-selector p-3 bg-light">
<input class="form-control mb-3" placeholder="Search Pokémon..." @bind="SearchTerm" @oninput="HandleSearch" />
<input class="form-control mb-3 rounded rounded-5" placeholder="Search Pokémon..." @bind="SearchTerm" @oninput="HandleSearch" />
<!-- Scrollable Pokémon Grid -->
<div class="row pokemon-grid">
<div class="row pokemon-grid pt-1">
@foreach (var pokemon in FilteredPokemon)
{
bool isSelected = SelectedPokemon?.Id == pokemon.Id;
<div class="col-6 col-md-3 mb-3">
<div class="card pokemon-card small-card @(isSelected ? "border-primary border-2 shadow" : "border-2 border-white")"
<div class="card pokemon-card small-card @(isSelected ? "border-primary border-2 shadow" : "border-2 border-white") rounded rounded-3"
@onclick="() => SelectPokemon(pokemon)">
<img src="@pokemon.PokemonImageUrl" class="card-img-top" style="height: 50px; object-fit: contain;" />
<div class="card-body p-2 text-center">
@ -21,7 +21,3 @@
}
</div>
</div>
<style>
</style>

View File

@ -5,11 +5,13 @@
border-radius: 5% / 3.5%;
display: flex;
flex-direction: column;
}
.pokemon-grid {
flex: 1 1 auto;
overflow-y: auto;
align-content: flex-start;
}
@ -19,7 +21,7 @@
}
.pokemon-card:hover {
transform: scale(1.05);
transform: scale(1.13);
}
.small-card {

View File

@ -5,20 +5,17 @@
@attribute [StreamRendering]
@rendermode InteractiveServer
<div class="d-flex justify-content-end">
</div>
<!-- Table A: Desktop View-->
<div class="container d-none d-md-block " style="height: 70vh;">
<div class="container d-none d-md-block" style="height: 70vh;">
<!-- Main UI -->
<div class="border-0 mt-4 mx-auto col-12 col-md-10 col-lg-8 pokemontable ">
<div class="border-0 mt-4 mx-auto col-12 col-md-10 col-lg-8 pokemontable">
<!-- Table Header -->
<div class="row bg-secondary bg-gradient py-3 border-0">
<div class="row bg-secondary bg-gradient py-3 border-0 rounded-top">
<div class="d-flex align-items-center justify-content-between w-100 position-relative px-3">
<!-- Left: Search -->
<input class="form-control w-25 me-3"
<input class="form-control w-25 me-3 rounded rounded-5"
placeholder="Search Pokémon..."
@bind="SearchTerm"
@oninput="HandleSearch" />
@ -33,7 +30,7 @@
<div class="badge bg-info">
<p class="statText mb-0">@(pokemons.Count()) Pokémon</p>
</div>
<PokemonDownload _Pokemon="pokemons" />
<PokemonDownloadButton _Pokemon="pokemons" />
</div>
</div>
@ -43,8 +40,8 @@
<div class="tableFixHead d-flex flex-column justify-content-start bg-secondary table-responsive row ">
<table class="table table-borderless border-0 table-secondary table-striped align-middle">
<!-- Table Head -->
<thead class="">
<tr class="">
<thead>
<tr>
<th class="text-white text-bg-info col-2 d-none d-md-table-cell" scope="col"></th>
<th class="text-white text-bg-info col-1" scope="col">#</th>
<th class="text-white text-bg-info col-2" scope="col">Pokémon</th>
@ -68,7 +65,7 @@
else
{
<!-- Table Body -->
<tbody class="">
<tbody>
<tr></tr>
@if (FilteredPokemon != null && FilteredPokemon.Any())
{
@ -104,7 +101,7 @@
</td>
<!-- Section 2: Pokemon # -->
<th class="" scope="row" style="cursor: default;">@pokemon.PokemonId</th>
<th scope="row" style="cursor: default;">@pokemon.PokemonId</th>
<!-- Section 3: Pokemon Name -->
@ -114,21 +111,21 @@
<!-- Section 4: Pokemon Type -->
<td class="">
<td>
<div class="d-flex justify-content-center">
<img src="@GetTypeImageUrl(pokemon.PokemonType)" style="width:36px; height:36px;" />
</div>
</td>
<!-- Section 5: Sleep Type -->
<td class="" style="">
<td>
<div class="d-flex justify-content-center ">
<PokemonBadge BadgeItem="@pokemon.SleepType" />
</div>
</td>
<!-- Section 6: Speciality -->
<td class="" style="">
<td>
<div class="d-flex justify-content-center">
<PokemonBadge BadgeItem="@pokemon.Speciality" />
@ -137,10 +134,10 @@
@if (adminToggle)
{
<td class="" style="">
<td>
<div class="d-flex justify-content-center">
<PokemonEditButton PokemonId="@pokemon.PokemonId" />
@*<button @onclick="() => ConfirmDelete(pokemon.Id)">Delete</button>*@
</div>
</td>
}
@ -203,10 +200,10 @@
@foreach (var pokemon in pokemons)
{
<tr class="border-0">
<div class="d-flex align-items-center">
<div class="d-flex align-items-center" style="border: 1px dashed hotpink;">
<!-- Pokemon Image -->
<div class="me-3">
<div class="me-3" style="border: 1px dashed hotpink;">
<div class="flip-container-sm" @onclick="() => ToggleImage(pokemon.Id)">
<div class="flipper-sm @(isShiny[pokemon.Id] ? "flipped" : "")">
<img class="front img-fluid" src="@pokemon.PokemonImageUrl" />
@ -218,7 +215,7 @@
</div>
</div>
<div class="">
<div style="border: 1px dashed hotpink;">
<!-- Number and Name -->
<h5>
@pokemon.PokemonId -

View File

@ -3,11 +3,9 @@
<div class="page">
<main class="">
<NavMenu3 />
<article class="container ">
<div class="">
<div class="container">
@Body
</div>
</article>
</main>
@* <MadeWithLoveFooter/> *@
</div>

View File

@ -3,6 +3,8 @@
@inject IPokemonService PokemonService
@inject NavigationManager Navigation
@inject IJSRuntime JS
@attribute [StreamRendering]
@rendermode InteractiveServer
@ -17,119 +19,72 @@
}
else
{
<div class="w-50 mt-5 m-auto create-container bg-info border border-5 border-info-subtle">
<div class="container mx-0 px-0">
<div class="row mt-5">
<div class="d-flex justify-content-evenly h-100 p-0">
<div class="mx-1 align-content-center">
<div class="addcard">
<PokemonForm
formUse="ADD"
OnPokemonReady="ReceivePokemon1"
mostRecentForm=false
/>
</div>
</div>
@if(!pokemon2FormView && !pokemon3FormView)
{
<div class="mx-1 align-content-center">
<PokemonAddButton OnAdd="TogglePokemon2FormView" />
</div>
}
else if (pokemon2FormView && !pokemon3FormView)
{
<div class="mx-1 align-content-center">
<div class="addcard">
<PokemonForm OnPokemonReady="ReceivePokemon2"
formUse="ADD"
RemoveForm="TogglePokemon2FormView"
mostRecentForm="@pokemon2FormView" />
</div>
</div>
<div class="mx-1 align-content-center">
<PokemonAddButton OnAdd="TogglePokemon3FormView" />
</div>
}
else if (!pokemon2FormView && pokemon3FormView)
{
<div class="mx-1 align-content-center">
<div class="addcard">
<PokemonForm OnPokemonReady="ReceivePokemon2"
formUse="ADD"
RemoveForm="TogglePokemon2FormView"
mostRecentForm="@pokemon2FormView" />
</div>
</div>
<div class="mx-1 align-content-center">
<div class="addcard">
<PokemonForm OnPokemonReady="ReceivePokemon3"
formUse="ADD"
RemoveForm="TogglePokemon3FormView"
mostRecentForm="@pokemon3FormView" />
</div>
</div>
}
<div class="card-header rounded-top-3 bg-info py-3">
<div class="row text-center">
<h2 class="text-white text-decoration-underline">Add New Pokémon</h2>
</div>
</div>
<div class="p-3" >
<EditForm class=" col" Model="NewPokemon" OnValidSubmit="HandleSubmit">
<DataAnnotationsValidator />
<!-- Pokemon Number and Name -->
<div class="row mt-3">
<div class="col-sm-3 input-group mb-3">
<!-- Pokemon #-->
<span for="PokemonId" class="input-group-text">#</span>
<InputNumber min="0" placeholder="Pokedex #" id="PokemonId" @bind-Value="NewPokemon.PokemonId" class="form-control " required />
<!-- Pokemon Name-->
<InputText placeholder="Pokemon Name" id="PokemonName" @bind-Value="NewPokemon.PokemonName" class="form-control w-75" required />
</div>
</div>
<!-- Pokemon Type, Sleep Type, and Speciality -->
<div class="row mb-3">
<!-- Pokemon Type -->
<div class="col m-auto">
<label for="PokemonType" class="form-label">Pokemon Type</label>
<InputSelect id="PokemonType" @bind-Value="NewPokemon.PokemonType" class="form-select">
<option dsabled value="">Select Type</option>
<option value="Grass">Grass</option>
<option value="Fire">Fire</option>
<option value="Water">Water</option>
<option value="Normal">Normal</option>
<option value="Flying">Flying</option>
<option value="Bug">Bug</option>
<option value="Poison">Poison</option>
<option value="Electric">Electric</option>
<option value="Ground">Ground</option>
<option value="Rock">Rock</option>
<option value="Ice">Ice</option>
<option value="Steel">Steel</option>
<option value="Fighting">Fighting</option>
<option value="Psychic">Psychic</option>
<option value="Dark">Dark</option>
<option value="Fairy">Fairy</option>
<option value="Ghost">Ghost</option>
<option value="Dragon">Dragon</option>
</InputSelect>
</div>
<!-- Sleep Type -->
<div class="col m-auto">
<label for="SleepType" class="form-label">Sleep Type</label>
<InputSelect id="SleepType" @bind-Value="NewPokemon.SleepType" class="form-select">
<option value="Dozing">Dozing</option>
<option value="Snoozing">Snoozing</option>
<option value="Slumbering">Slumbering</option>
</InputSelect>
</div>
<!-- Speciality-->
<div class="col m-auto">
<label for="Speciality" class="form-label">Specialty</label>
<InputSelect id="Speciality" @bind-Value="NewPokemon.Speciality" class="form-select">
<option value="Berries">Berries</option>
<option value="Ingredients">Ingredients</option>
<option value="Skills">Skills</option>
<option value="All">All</option>
</InputSelect>
</div>
</div>
<!-- If New Pokemon is Variant -->
<div class="row mb-3">
<div class="input-group align-items-center">
<!-- Variation Check -->
<div class="d-flex">
<InputCheckbox id="IsVariation" @bind-Value="NewPokemon.IsVariation" @onclick="@Toggle" class="form-check-input checkbox-styling p-3 rounded" />
<span for="IsVariation" class="input-group-text mx-2">Variation?</span>
</div>
<!-- Variation Region Input -->
<div class="flex-fill">
<InputText placeholder="What Variant? (Alolan, Paldean)" hidden="@HideLabel" id="VariationName" @bind-Value="NewPokemon.VariationName" class="form-control" />
</div>
</div>
</div>
<!-- New Image URL Field -->
<div class="row mb-3 m-auto">
<label for="ImageUrl" class="form-label">Base Image URL</label>
<InputText id="ImageUrl" @bind-Value="NewPokemon.PokemonImageUrl" class="form-control" />
</div>
<!-- Shiny Image URL Input -->
<div class="row mb-3 m-auto">
<label for="ImageUrl" class="form-label">Shiny Image URL</label>
<InputText id="ImageUrl" @bind-Value="NewPokemon.PokemonShinyImageUrl" class="form-control" />
</div>
<!-- Flavor Text Input -->
<div class="row mb-3 m-auto">
<label for="FlavorText" class="form-label">Flavor Text</label>
<InputText id="FlavorText" @bind-Value="NewPokemon.FlavorText" class="form-control" />
</div>
<!-- Form Buttons -->
<div class="d-flex mt-5 justify-content-between">
<button type="button" class="btn btn-danger rounded rouneded-4 mb-3 " @onclick="Cancel">Cancel</button>
<button type="submit" class="btn btn-success rounded rouneded-4 mb-3 ">Add Pokemon</button>
</div>
</EditForm>
</div>
<div class="d-flex justify-content-center pt-3">
<div class="btn-group">
<button @onclick="@HandleAdd" class="btn btn-primary rounded">Add Pokemon</button>
</div>
</div>
}

View File

@ -1,17 +1,32 @@
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{
public partial class PokemonCreate
{
private bool pokemon2FormView { get; set; } = false;
private bool pokemon3FormView { get; set; } = false;
private bool HideLabel { get; set; } = true;
private void Toggle()
{
HideLabel = !HideLabel;
}
private void TogglePokemon2FormView()
{
pokemon2FormView = !pokemon2FormView;
}
private void TogglePokemon3FormView()
{
pokemon3FormView = !pokemon3FormView;
TogglePokemon2FormView();
private Pokemon NewPokemon = new Pokemon
}
private Pokemon pokemon1, pokemon2, pokemon3 = new Pokemon
{
PokemonId = 0, // Or any default ID logic
PokemonName = string.Empty, // Required fields cannot be null
@ -27,12 +42,61 @@ namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{
this.ToggleVariationName = true;
}
private async Task HandleSubmit()
private bool IsComplete(Pokemon NewPokemon) =>
NewPokemon.PokemonId > 0 &&
!string.IsNullOrWhiteSpace(NewPokemon.PokemonName) &&
!string.IsNullOrWhiteSpace(NewPokemon.PokemonType) &&
!string.IsNullOrWhiteSpace(NewPokemon.SleepType) &&
!string.IsNullOrWhiteSpace(NewPokemon.Speciality) &&
(!NewPokemon.IsVariation || !string.IsNullOrWhiteSpace(NewPokemon.VariationName));
private async Task HandleAdd()
{
/*
Okay there are three versions of submits which need to be checked
1. Single submit, only one, if both pokemon#FormView is false
2. 2 Submit, if pokemon2FormView is true
3. 3 Submit, if pokemon3FormView is true
*/
if (!pokemon2FormView && !pokemon3FormView)
{
//if(IsComplete(pokemon1))
//{
await HandleSubmit(pokemon1);
Navigation.NavigateTo("/pokemon");
//}
}
else if (pokemon2FormView)
{
//if (IsComplete(pokemon1) && IsComplete(pokemon2))
//{
await HandleSubmit(pokemon1);
await HandleSubmit(pokemon2);
Navigation.NavigateTo("/pokemon");
// }
}
else if (pokemon3FormView)
{
//if (IsComplete(pokemon1) && IsComplete(pokemon2) && IsComplete(pokemon3))
//{
await HandleSubmit(pokemon1);
await HandleSubmit(pokemon2);
await HandleSubmit(pokemon3);
Navigation.NavigateTo("/pokemon");
//}
}
}
private async Task HandleSubmit(Pokemon NewPokemon)
{
isSubmitting = true;
await PokemonService.AddPokemonAsync(NewPokemon);
isSubmitting = false;
Navigation.NavigateTo("/pokemon");
}
protected void Cancel(MouseEventArgs e)
@ -41,5 +105,41 @@ namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
Navigation.NavigateTo("/pokemon");
}
private async Task ReceivePokemon1(Pokemon p)
{
// Save received pokemon as Pokemon 1
pokemon1 = p;
// Server console (Blazor Server)
Console.WriteLine($"[Pokemon 1] #{pokemon1.PokemonId} {pokemon1.PokemonName} | {pokemon1.PokemonType} | {pokemon1.SleepType} | {pokemon1.Speciality} | Var:{pokemon1.IsVariation} {pokemon1.VariationName}");
// Browser console
await JS.InvokeVoidAsync("console.log", "Pokemon 1:", pokemon1);
}
private async Task ReceivePokemon2(Pokemon p)
{
// Save received pokemon as Pokemon 1
pokemon2 = p;
// Server console (Blazor Server)
Console.WriteLine($"[Pokemon 2] #{pokemon2.PokemonId} {pokemon2.PokemonName} | {pokemon2.PokemonType} | {pokemon2.SleepType} | {pokemon2.Speciality} | Var:{pokemon2.IsVariation} {pokemon2.VariationName}");
// Browser console
await JS.InvokeVoidAsync("console.log", "Pokemon 2:", pokemon2);
}
private async Task ReceivePokemon3(Pokemon p)
{
// Save received pokemon as Pokemon 1
pokemon3 = p;
// Server console (Blazor Server)
Console.WriteLine($"[Pokemon 3] #{pokemon3.PokemonId} {pokemon3.PokemonName} | {pokemon3.PokemonType} | {pokemon3.SleepType} | {pokemon3.Speciality} | Var:{pokemon3.IsVariation} {pokemon3.VariationName}");
// Browser console
await JS.InvokeVoidAsync("console.log", "Pokemon 3:", pokemon3);
}
}
}

View File

@ -1,6 +1,5 @@

.create-container {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(255,255, 255, 0.19);
border-radius: 15px;
.addcard {
width: 100%;
max-width: 25rem;
}

View File

@ -1,7 +1,7 @@
@page "/pokemonsleep/edit/{id:int}"
@inject IPokemonService PokemonService
@inject NavigationManager Navigation
@inject IJSRuntime JSRuntime
@inject IJSRuntime JS
@attribute [StreamRendering]
@rendermode InteractiveServer
@ -12,122 +12,33 @@
@if (pokemon == null)
{
<p><em>Loading...</em></p>
<Loading />
}
else
{
<!-- Total Componenet-->
<div class="w-50 mt-3 m-auto bg-info edit-container">
<!-- Header -->
<div class="card-header bg-secondary ml-0 py-3 w-100 ">
<div class="row">
<div class="col-12 text-center">
<h2 class="text-info">Edit Pokémon</h2>
<div class="container mx-0 px-0">
<div class="row mt-5">
<div class="d-flex justify-content-evenly h-100 p-0">
<div class="mx-1 align-content-center">
<div class="addcard">
<PokemonForm
formUse="EDIT"
OnPokemonReady="ReceivePokemon"
RemoveForm="Cancel"
mostRecentForm=true
PokemonToEdit="pokemon"
/>
</div>
</div>
</div>
</div>
<!-- Body -->
<div class="p-3 w-100 flex-column ">
<EditForm class="col mb-3" Model="pokemon" OnValidSubmit="HandleSubmit">
<DataAnnotationsValidator />
<!-- Dex Number and Name -->
<div class="row ">
<div class="col-sm-3 input-group mb-3">
<span for="PokemonId" class="input-group-text">#</span>
<InputNumber min="0" id="PokemonId" @bind-Value="pokemon.PokemonId" class="form-control" required disabled />
<InputText id="PokemonName" @bind-Value="pokemon.PokemonName" class="form-control w-75" required />
</div>
</div>
<!-- Pokemon Type, Sleep Type, and Speciality -->
<div class="row">
<!-- Pokemon Type -->
<div class="col mb-3 m-auto">
<label for="PokemonType" class="form-label">Pokemon Type</label>
<InputSelect id="PokemonType" @bind-Value="pokemon.PokemonType" class="form-select">
<option dsabled value="">Select Type</option>
<option value="Grass">Grass</option>
<option value="Fire">Fire</option>
<option value="Water">Water</option>
<option value="Normal">Normal</option>
<option value="Flying">Flying</option>
<option value="Bug">Bug</option>
<option value="Poison">Poison</option>
<option value="Electric">Electric</option>
<option value="Ground">Ground</option>
<option value="Rock">Rock</option>
<option value="Ice">Ice</option>
<option value="Steel">Steel</option>
<option value="Fighting">Fighting</option>
<option value="Psychic">Psychic</option>
<option value="Dark">Dark</option>
<option value="Fairy">Fairy</option>
<option value="Ghost">Ghost</option>
<option value="Dragon">Dragon</option>
</InputSelect>
</div>
<!-- Sleep Type -->
<div class="col mb-3 m-auto">
<label for="SleepType" class="form-label">Sleep Type</label>
<InputSelect id="SleepType" @bind-Value="pokemon.SleepType" class="form-select">
<option value="Dozing">Dozing</option>
<option value="Snoozing">Snoozing</option>
<option value="Slumbering">Slumbering</option>
</InputSelect>
</div>
<!-- Speciality-->
<div class="col mb-3 m-auto">
<label for="Speciality" class="form-label">Specialty</label>
<InputSelect id="Speciality" @bind-Value="pokemon.Speciality" class="form-select">
<option value="Berries">Berries</option>
<option value="Ingredients">Ingredients</option>
<option value="Skills">Skills</option>
</InputSelect>
</div>
</div>
<!-- Variation Check -->
<div class="row">
<div class="input-group mb-3">
<!-- Variation Check -->
<div class=" d-inline-flex">
<InputCheckbox id="IsVariation" @bind-Value="pokemon.IsVariation" @onclick="@Toggle" class="form-check-input checkbox-styling p-3 mt-1" />
<span for="IsVariation" class="input-group-text m-1">Variation?</span>
</div>
<!-- Variation Region Input -->
<div class="w-75 mx-2 mt-1">
<InputText placeholder="What Variant? (Alolan, Paldean)" hidden="@HideLabel" id="VariationName" @bind-Value="pokemon.VariationName" class="form-control" />
</div>
</div>
</div>
<!-- Image URL Input -->
<div class="row mb-3 m-auto">
<label for="ImageUrl" class="form-label">Base Image URL</label>
<InputText id="ImageUrl" @bind-Value="pokemon.PokemonImageUrl" class="form-control" />
</div>
<!-- Shiny Image URL Input -->
<div class="row mb-3 m-auto">
<label for="ImageUrl" class="form-label">Shiny Image URL</label>
<InputText id="ImageUrl" @bind-Value="pokemon.PokemonShinyImageUrl" class="form-control" />
</div>
<!-- Flavor Text Input -->
<div class="row mb-3 m-auto">
<label for="FlavorText" class="form-label">Flavor Text</label>
<InputText id="FlavorText" @bind-Value="pokemon.FlavorText" class="form-control" />
</div>
<!-- Form Buttons -->
<div class="d-flex justify-content-between">
<button type="button" class="btn btn-secondary mb-3" @onclick="Cancel">Cancel</button>
<button type="submit" class="btn btn-primary mb-3">Save Changes</button>
</div>
</EditForm>
</div>
<div class="d-flex justify-content-center">
<div class="btn-group">
<button @onclick="@HandleSubmit" class="btn btn-primary rounded">Edit Pokemon</button>
</div>
</div>
}

View File

@ -14,16 +14,28 @@ namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
pokemon = await PokemonService.GetPokemonByIdAsync(Id);
}
private async Task ReceivePokemon(Pokemon p)
{
// Save received pokemon as Pokemon 1
pokemon = p;
// Server console (Blazor Server)
Console.WriteLine($"[Pokemon 1] #{pokemon.PokemonId} {pokemon.PokemonName} | {pokemon.PokemonType} | {pokemon.SleepType} | {pokemon.Speciality} | Var:{pokemon.IsVariation} {pokemon.VariationName}");
// Browser console
await JS.InvokeVoidAsync("console.log", "Pokemon 1:", pokemon);
}
private async Task HandleSubmit()
{
await PokemonService.UpdatePokemonAsync(pokemon);
//Navigation.NavigateTo("/pokemonsleep");
await JSRuntime.InvokeVoidAsync("history.back");
await JS.InvokeVoidAsync("history.back");
}
private async void Cancel()
{
await JSRuntime.InvokeVoidAsync("history.back");
await JS.InvokeVoidAsync("history.back");
}

View File

@ -5,3 +5,13 @@
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(255,255, 255, 0.19);
border-radius: 15px;
}
.addcard {
width: 100%;
max-width: 30rem;
flex: 0 0 auto; /* prevent flex shrink/grow */
display: flex;
justify-content: center;
align-items: center;
padding: 0.5rem;
}

View File

@ -0,0 +1,93 @@
@page "/pokemon/{id:int}"
@inject IPokemonService PokemonService
@inject NavigationManager Navigation
@attribute [StreamRendering]
@rendermode InteractiveServer
<PokemonHeader />
@if (_pokemon == null)
{
<Loading />
}
else
{
<PageTitle>@_pokemon.PokemonName</PageTitle>
<!-- Total Componenet-->
<div class="container">
<div class="w-75 row mt-4 m-auto">
<!-- Previous Pokemon Button -->
<div class="col-auto">
<button class="mt-1 p-2 btn btn-danger rounded-5 align-self-start text-white" disabled="@(!_previousPokemonId.HasValue)" @onclick="NavigateToPrevious">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-arrow-left-circle-fill" viewBox="0 0 16 16">
<path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0m3.5 7.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5z" />
</svg>
</button>
</div>
<!-- Pokemon Presentation -->
<div class="mt-5 mx-5 col justify-content-center">
@if (_variationPokemonId != null)
{
@if (_variationPokemonId != null && _pokemonVariant == null){
<Loading />
}
else
{
<!-- If Variation Pokemon have same PokemonId -->
@if(_pokemon.Id != _pokemonVariant.Id)
{
<div class="d-flex justify-content-center">
<div class="pokemoncard m-1">
<PokemonCard Pokemon="_pokemon" />
</div>
<div class="pokemoncard m-1">
<PokemonCard Pokemon="_pokemonVariant" />
</div>
</div>
}
<!-- If Variation Pokemon has diff PokemonId -->
else
{
<div class="d-flex justify-content-center align-items-center h-100">
<div class="pokemoncard">
<PokemonCard Pokemon="_pokemonVariant" />
</div>
</div>
}
}
}
else{
<!-- Standard Pokemon Display -->
<div class="d-flex justify-content-center align-items-center h-100">
<div class="pokemoncard m-1">
<PokemonCard Pokemon="_pokemon" />
</div>
</div>
}
</div>
<!-- Next Pokemon Button -->
<div class="col-auto">
<button class="mt-1 p-2 btn btn-danger rounded rounded-5 align-self-start text-white" disabled="@(!_nextPokemonId.HasValue)" @onclick="NavigateToNext">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-arrow-right-circle-fill" viewBox="0 0 16 16">
<path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0M4.5 7.5a.5.5 0 0 0 0 1h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5z" />
</svg>
</button>
</div>
</div>
</div>
}

View File

@ -0,0 +1,52 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{
public partial class PokemonIndividualView
{
[Parameter] public int Id { get; set; }
private Pokemon? _pokemon;
private Pokemon? _pokemonVariant;
private List<int> _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($"/pokemon/{_nextPokemonId.Value}");
}
private void NavigateToPrevious()
{
if (_previousPokemonId.HasValue)
Navigation.NavigateTo($"/pokemon/{_previousPokemonId.Value}");
}
}
}

View File

@ -0,0 +1,9 @@
.pokemoncard {
width: 100%;
max-width: 350px;
flex: 0 0 auto; /* prevent flex shrink/grow */
display: flex;
justify-content: center;
align-items: center;
padding: 0.5rem;
}

View File

@ -1,23 +1,67 @@
@page "/pokemon"
@page "/pokemonsleep"
@inject IPokemonService PokemonService
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Pokémon Sleep</PageTitle>
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
<PokemonHeader />
<div class="w-100">
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
<!-- Card -->
<div class="container card shadow-sm border-0 p-3 pb-4 mt-4 m-auto w-75 mb-5 bg-white">
<PokemonHeader />
<!-- Top Row-->
<div class="row d-flex flex-row">
<!-- Image side -->
<div class="col-5 border-0 ">
<img class="shadow-sm w-100 m-auto img-fluid" src="images/Pokemon_Sleep_ID.jpg" />
</div>
<!-- Text side -->
<div class="col w-50 p-3">
<p class="fw-bold fst-italic fs-3">Pokémon Sleep? Really?</p>
<p class="fw-normal fs-6 text-start">
Yes, really! Pokémon Sleep has become a fun addition to my day since it's release.
</p>
<p class="fw-bold fst-italic fs-3">But what do you even do?</p>
<p class="fw-normal fs-6 text-start">
That's harder to explain. See, it isn't as much a game, as it is a gamified sleep tracker. But it's fun to collect the Pokémon and gather their berries and ingredients and create silly little Pokémon-themed foods. Plus, it encourages me to try to get to bed in a timely manner; though, if I'm honest, I definitely put my Pokémon to bed ahead of when I do.
</p>
<PokemonTable AllPokemon="pokemons"/>
</div>
</div>
<hr class="mt-5" />
<!-- Second Row -->
<div class="row mt-3">
<p class="fw-bold fst-italic fs-3">Okay, but why a whole section dedicated to Pokémon Sleep?</p>
<p class="fw-normal fs-6 text-start">
Well, as it is in any Pokémon game, Natures (and in this case Subskills) matter, amongst other things. This was designed as a helpful tool in assessing new Pokémon acquired from Sleep Research.
</p>
</div>
<!-- Button Row-->
<div class="d-flex justify-content-evenly mt-4 ">
<button class="btn btn-info rounded-4 d-flex justify-content-center" style="width:12rem;">
<NavLink class="nav-link d-flex align-items-center" href="/pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-table" viewBox="0 0 16 16">
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 2h-4v3h4zm0 4h-4v3h4zm0 4h-4v3h3a1 1 0 0 0 1-1zm-5 3v-3H6v3zm-5 0v-3H1v2a1 1 0 0 0 1 1zm-4-4h4V8H1zm0-4h4V4H1zm5-3v3h4V4zm4 4H6v3h4z" />
</svg>
<span class="ps-1 text-white">Available Pokémon</span>
</NavLink>
</button>
<button class="btn btn-success rounded-4 d-flex justify-content-center" style="width:12rem;">
<NavLink class="nav-link d-flex align-items-center" href="/rate-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFF" class="bi bi-award-fill" viewBox="0 0 16 16">
<path d="m8 0 1.669.864 1.858.282.842 1.68 1.337 1.32L13.4 6l.306 1.854-1.337 1.32-.842 1.68-1.858.282L8 12l-1.669-.864-1.858-.282-.842-1.68-1.337-1.32L2.6 6l-.306-1.854 1.337-1.32.842-1.68L6.331.864z" />
<path d="M4 11.794V16l4-1 4 1v-4.206l-2.018.306L8 13.126 6.018 12.1z" />
</svg>
<span class="ps-1 text-white">Rate Pokémon</span>
</NavLink>
</button>
</div>
</div>

View File

@ -1,30 +0,0 @@
using Portfolio.Application.Services.PokemonService;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{
public partial class PokemonSleep
{
public List<Pokemon> pokemons = new List<Pokemon>();
public List<string> pokemonImageUrls = new List<string>();
public List<string> pokemonShinyImageUrls = new List<string>();
protected override async Task OnInitializedAsync()
{
var result = await PokemonService.GetAllPokemonAsync();
if (result is not null)
{
pokemons = result;
pokemons.Sort((x, y) => x.PokemonId.CompareTo(y.PokemonId));
foreach (var pokemon in pokemons)
{
pokemonImageUrls.Add(pokemon.PokemonImageUrl);
pokemonShinyImageUrls.Add(pokemon.PokemonShinyImageUrl);
}
}
}
}
}

View File

@ -1,93 +0,0 @@

.tableFixHead {
overflow: auto;
height: 600px;
}
.tableFixHead thead th {
position: sticky;
top: 0;
z-index: 10;
}
.flip-container {
perspective: 1000px;
display: inline-block;
width: 90px;
height: 90px;
cursor: pointer;
}
.flipper {
transition: transform 0.6s;
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: relative;
}
.flipped {
transform: rotateY(180deg);
}
.front, .back {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
backface-visibility: hidden;
}
.back {
transform: rotateY(180deg);
}
.badge {
width: 100px;
height: 30px;
color: white;
padding: 4px 8px;
text-align: center;
vertical-align: middle;
border-radius: 30px;
}
.statText {
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 13px;
}
.dozing {
background-color: #fcdc5e;
}
.snoozing {
background-color: #4ce8ed;
}
.slumbering {
background-color: #4588fb;
}
.berries {
background-color: #24d86b;
}
.ingredients {
background-color: #fdbe4d;
}
.skills {
background-color: #47a0fc;
}
.page-content {
position: relative;
z-index: 1; /* Higher than stickers */
background-color: rgba(255, 255, 255, 0.8); /* Optional translucent bg */
}

View File

@ -1,67 +0,0 @@
@page "/pokemonsleep"
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Pokémon Sleep</PageTitle>
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
<PokemonHeader />
<!-- Card -->
<div class="container card shadow-sm border-0 p-3 pb-4 mt-4 m-auto w-75 mb-5 bg-white">
<!-- Top Row-->
<div class="row d-flex flex-row">
<!-- Image side -->
<div class="col-5 border-0 ">
<img class="shadow-sm w-100 m-auto img-fluid" src="images/Pokemon_Sleep_ID.jpg" />
</div>
<!-- Text side -->
<div class="col w-50 p-3">
<p class="fw-bold fst-italic fs-3">Pokémon Sleep? Really?</p>
<p class="fw-normal fs-6 text-start">
Yes, really! Pokémon Sleep has become a fun addition to my day since it's release.
</p>
<p class="fw-bold fst-italic fs-3">But what do you even do?</p>
<p class="fw-normal fs-6 text-start">
That's harder to explain. See, it isn't as much a game, as it is a gamified sleep tracker. But it's fun to collect the Pokémon and gather their berries and ingredients and create silly little Pokémon-themed foods. Plus, it encourages me to try to get to bed in a timely manner; though, if I'm honest, I definitely put my Pokémon to bed ahead of when I do.
</p>
</div>
</div>
<!-- Second Row -->
<div class="row mt-3">
<p class="fw-bold fst-italic fs-3">Okay, but why a whole section dedicated to Pokémon Sleep?</p>
<p class="fw-normal fs-6 text-start">
Well, as it is in any Pokémon game, Natures (and in this case Subskills) matter, amongst other things. This was designed as a helpful tool in assessing new Pokémon acquired from Sleep Research.
</p>
</div>
<!-- Button Row-->
<div class="d-flex justify-content-evenly mt-4 ">
<button class="btn btn-info rounded-4 d-flex justify-content-center" style="width:12rem;">
<NavLink class="nav-link d-flex align-items-center" href="/pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-table" viewBox="0 0 16 16">
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 2h-4v3h4zm0 4h-4v3h4zm0 4h-4v3h3a1 1 0 0 0 1-1zm-5 3v-3H6v3zm-5 0v-3H1v2a1 1 0 0 0 1 1zm-4-4h4V8H1zm0-4h4V4H1zm5-3v3h4V4zm4 4H6v3h4z" />
</svg>
<span class="ps-1 text-white">Available Pokémon</span>
</NavLink>
</button>
<button class="btn btn-success rounded-4 d-flex justify-content-center" style="width:12rem;">
<NavLink class="nav-link d-flex align-items-center" href="/rate-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFF" class="bi bi-award-fill" viewBox="0 0 16 16">
<path d="m8 0 1.669.864 1.858.282.842 1.68 1.337 1.32L13.4 6l.306 1.854-1.337 1.32-.842 1.68-1.858.282L8 12l-1.669-.864-1.858-.282-.842-1.68-1.337-1.32L2.6 6l-.306-1.854 1.337-1.32.842-1.68L6.331.864z" />
<path d="M4 11.794V16l4-1 4 1v-4.206l-2.018.306L8 13.126 6.018 12.1z" />
</svg>
<span class="ps-1 text-white">Rate Pokémon</span>
</NavLink>
</button>
</div>
</div>

View File

@ -1,93 +1,23 @@
@page "/pokemon/{id:int}"
@page "/pokemon"
@inject IPokemonService PokemonService
@inject NavigationManager Navigation
@attribute [StreamRendering]
@rendermode InteractiveServer
<PokemonHeader />
<PageTitle>Pokémon Sleep</PageTitle>
<div class="w-100">
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
@if (_pokemon == null)
{
<Loading />
}
else
{
<PageTitle>@_pokemon.PokemonName</PageTitle>
<!-- Total Componenet-->
<PokemonHeader />
<div class="container">
<div class="w-75 row mt-4 m-auto">
<PokemonTable AllPokemon="pokemons"/>
<!-- Previous Pokemon Button -->
<div class="col-auto">
<button class="mt-1 p-2 btn btn-danger rounded-5 align-self-start text-white" disabled="@(!_previousPokemonId.HasValue)" @onclick="NavigateToPrevious">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-arrow-left-circle-fill" viewBox="0 0 16 16">
<path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0m3.5 7.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5z" />
</svg>
</button>
</div>
<hr class="mt-5" />
<!-- Pokemon Presentation -->
<div class="mt-5 mx-5 col justify-content-center">
@if (_variationPokemonId != null)
{
@if (_variationPokemonId != null && _pokemonVariant == null){
<Loading />
}
else
{
<!-- If Variation Pokemon have same PokemonId -->
@if(_pokemon.Id != _pokemonVariant.Id)
{
<div class="d-flex justify-content-center">
<div class="pokemoncard m-1">
<PokemonCard Pokemon="_pokemon" />
</div>
<div class="pokemoncard m-1">
<PokemonCard Pokemon="_pokemonVariant" />
</div>
</div>
}
<!-- If Variation Pokemon has diff PokemonId -->
else
{
<div class="d-flex justify-content-center align-items-center h-100">
<div class="pokemoncard">
<PokemonCard Pokemon="_pokemonVariant" />
</div>
</div>
}
}
}
else{
<!-- Standard Pokemon Display -->
<div class="d-flex justify-content-center align-items-center h-100">
<div class="pokemoncard m-1">
<PokemonCard Pokemon="_pokemon" />
</div>
</div>
}
</div>
<!-- Next Pokemon Button -->
<div class="col-auto">
<button class="mt-1 p-2 btn btn-danger rounded rounded-5 align-self-start text-white" disabled="@(!_nextPokemonId.HasValue)" @onclick="NavigateToNext">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-arrow-right-circle-fill" viewBox="0 0 16 16">
<path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0M4.5 7.5a.5.5 0 0 0 0 1h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5z" />
</svg>
</button>
</div>
</div>
</div>
}
</div>

View File

@ -1,52 +1,30 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Application.Services.PokemonService;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{
public partial class PokemonView
{
[Parameter] public int Id { get; set; }
private Pokemon? _pokemon;
private Pokemon? _pokemonVariant;
private List<int> _pokemonIds;
private int? _nextPokemonId;
private int? _previousPokemonId;
private int? _variationPokemonId;
private int _currentIndex;
public List<Pokemon> pokemons = new List<Pokemon>();
public List<string> pokemonImageUrls = new List<string>();
public List<string> pokemonShinyImageUrls = new List<string>();
protected override async Task OnParametersSetAsync()
protected override async Task OnInitializedAsync()
{
_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)
var result = await PokemonService.GetAllPokemonAsync();
if (result is not null)
{
Console.WriteLine(_variationPokemonId);
_pokemonVariant = await PokemonService.GetPokemonByIdAsync((int)_variationPokemonId);
Console.WriteLine(_pokemonVariant.VariationName);
pokemons = result;
pokemons.Sort((x, y) => x.PokemonId.CompareTo(y.PokemonId));
foreach (var pokemon in pokemons)
{
pokemonImageUrls.Add(pokemon.PokemonImageUrl);
pokemonShinyImageUrls.Add(pokemon.PokemonShinyImageUrl);
}
}
}
private void NavigateToNext()
{
if (_nextPokemonId.HasValue)
Navigation.NavigateTo($"/pokemon/{_nextPokemonId.Value}");
}
private void NavigateToPrevious()
{
if (_previousPokemonId.HasValue)
Navigation.NavigateTo($"/pokemon/{_previousPokemonId.Value}");
}
}
}

View File

@ -1,9 +1,93 @@
.pokemoncard {
width: 100%;
max-width: 350px;
flex: 0 0 auto; /* prevent flex shrink/grow */
display: flex;
justify-content: center;
align-items: center;
padding: 0.5rem;

.tableFixHead {
overflow: auto;
height: 600px;
}
.tableFixHead thead th {
position: sticky;
top: 0;
z-index: 10;
}
.flip-container {
perspective: 1000px;
display: inline-block;
width: 90px;
height: 90px;
cursor: pointer;
}
.flipper {
transition: transform 0.6s;
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: relative;
}
.flipped {
transform: rotateY(180deg);
}
.front, .back {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
backface-visibility: hidden;
}
.back {
transform: rotateY(180deg);
}
.badge {
width: 100px;
height: 30px;
color: white;
padding: 4px 8px;
text-align: center;
vertical-align: middle;
border-radius: 30px;
}
.statText {
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 13px;
}
.dozing {
background-color: #fcdc5e;
}
.snoozing {
background-color: #4ce8ed;
}
.slumbering {
background-color: #4588fb;
}
.berries {
background-color: #24d86b;
}
.ingredients {
background-color: #fdbe4d;
}
.skills {
background-color: #47a0fc;
}
.page-content {
position: relative;
z-index: 1; /* Higher than stickers */
background-color: rgba(255, 255, 255, 0.8); /* Optional translucent bg */
}