Compare commits

...

8 Commits

25 changed files with 957 additions and 276 deletions

View File

@ -13,10 +13,12 @@ namespace Portfolio.Domain.Features.Pokemon
public required string PokemonName { get; set; }
public bool IsVariation { get; set; } = false;
public string? VariationName { get; set; }
public string? PokemonType { get; set; }
public required string SleepType { get; set; }
public required string Speciality { get; set; }
public string? PokemonImageUrl { get; set; }
public string? PokemonShinyImageUrl { get; set; }
public string? FlavorText { get; set; }
}

View File

@ -0,0 +1,124 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Portfolio.Infrastructure;
#nullable disable
namespace Portfolio.Infrastructure.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20250404172651_include_pokemon_type")]
partial class include_pokemon_type
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Portfolio.Domain.Features.Pokemon.Pokemon", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<bool>("IsVariation")
.HasColumnType("bit");
b.Property<int>("PokemonId")
.HasColumnType("int");
b.Property<string>("PokemonImageUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("PokemonName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PokemonShinyImageUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("PokemonType")
.HasColumnType("nvarchar(max)");
b.Property<string>("SleepType")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Speciality")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("VariationName")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Pokemons");
});
modelBuilder.Entity("Portfolio.Domain.Features.Pokemon_Natures.PokemonNature", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("BerryRating")
.HasColumnType("int");
b.Property<int>("IngredientRating")
.HasColumnType("int");
b.Property<string>("Nature")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("SkillRating")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PokemonNatures");
});
modelBuilder.Entity("Portfolio.Domain.Features.Pokemon_Subskills.PokemonSubskill", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("BerryRank")
.HasColumnType("int");
b.Property<int>("IngredientRank")
.HasColumnType("int");
b.Property<int>("SkillRank")
.HasColumnType("int");
b.Property<string>("SubSkill")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("PokemonSubskills");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Portfolio.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class include_pokemon_type : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "PokemonType",
table: "Pokemons",
type: "nvarchar(max)",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "PokemonType",
table: "Pokemons");
}
}
}

View File

@ -0,0 +1,127 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Portfolio.Infrastructure;
#nullable disable
namespace Portfolio.Infrastructure.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20250404173359_include_flavortext")]
partial class include_flavortext
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Portfolio.Domain.Features.Pokemon.Pokemon", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("FlavorText")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsVariation")
.HasColumnType("bit");
b.Property<int>("PokemonId")
.HasColumnType("int");
b.Property<string>("PokemonImageUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("PokemonName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PokemonShinyImageUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("PokemonType")
.HasColumnType("nvarchar(max)");
b.Property<string>("SleepType")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Speciality")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("VariationName")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Pokemons");
});
modelBuilder.Entity("Portfolio.Domain.Features.Pokemon_Natures.PokemonNature", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("BerryRating")
.HasColumnType("int");
b.Property<int>("IngredientRating")
.HasColumnType("int");
b.Property<string>("Nature")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("SkillRating")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("PokemonNatures");
});
modelBuilder.Entity("Portfolio.Domain.Features.Pokemon_Subskills.PokemonSubskill", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("BerryRank")
.HasColumnType("int");
b.Property<int>("IngredientRank")
.HasColumnType("int");
b.Property<int>("SkillRank")
.HasColumnType("int");
b.Property<string>("SubSkill")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("PokemonSubskills");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Portfolio.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class include_flavortext : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "FlavorText",
table: "Pokemons",
type: "nvarchar(max)",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "FlavorText",
table: "Pokemons");
}
}
}

View File

@ -29,6 +29,9 @@ namespace Portfolio.Infrastructure.Migrations
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("FlavorText")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsVariation")
.HasColumnType("bit");
@ -45,6 +48,9 @@ namespace Portfolio.Infrastructure.Migrations
b.Property<string>("PokemonShinyImageUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("PokemonType")
.HasColumnType("nvarchar(max)");
b.Property<string>("SleepType")
.IsRequired()
.HasColumnType("nvarchar(max)");

View File

@ -1,80 +1,8 @@
@inject NavigationManager NavManager
<div class="pokemon-background">
@foreach (var image in BackgroundImages)
<div class="pokemon-background">
@foreach (var image in _pokemonImages)
{
<img src="@image.Url"
class="pokemon-bg-img"
style="left:@image.Left%; top:@image.Top%; width:@image.Size}px; transform:rotate(@(image.Rotation)deg);" />
}
</div>
@code {
private List<PokemonImage> BackgroundImages = new();
private Random random = new();
// Your specified set of Pokémon numbers
private readonly int[] AllowedPokemonNumbers = new int[]
{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 19, 20, 23, 24, 25, 26, 35, 36,
37, 38, 39, 40, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 69, 70, 71, 74, 75, 76,
79, 80, 81, 82, 84, 85, 92, 93, 94, 95,
104, 105, 115, 122, 127, 132, 133, 134,
135, 136, 147, 148, 149, 152, 153, 154,
155, 156, 157, 158, 159, 160, 172, 173,
174, 175, 176, 179, 180, 181, 185, 194,
195, 196, 197, 199, 202, 208, 214, 215,
225, 228, 229, 243, 244, 245, 246, 247,
248, 280, 281, 282, 287, 288, 289, 302,
304, 305, 306, 316, 317, 333, 334, 353,
354, 359, 360, 363, 364, 365, 403, 404,
405, 425, 426, 438, 439, 447, 448, 453,
454, 459, 460, 461, 462, 468, 470, 471,
475, 627, 628, 700, 702, 736, 737, 738,
759, 760, 764, 778, 845, 906, 907, 908,
909, 910, 911, 912, 913, 914, 921, 922,
923, 980
};
protected override void OnInitialized()
{
GeneratePokemonBackground();
}
private void GeneratePokemonBackground()
{
var normalPath = $"/pokemon_images/normal/";
var shinyPath = $"/pokemon_images/shiny/";
for (int i = 0; i < 30; i++) // Generate 30 Pokémon images
{
int pokemonNumber = AllowedPokemonNumbers[random.Next(AllowedPokemonNumbers.Length)];
// 10% chance to use a shiny version
bool isShiny = random.NextDouble() < 0.1;
string imageUrl = isShiny
? $"{shinyPath}{pokemonNumber}.png"
: $"{normalPath}{pokemonNumber}.png";
BackgroundImages.Add(new PokemonImage
{
Url = imageUrl,
Left = random.Next(0, 100), // 0-100% of background container width
Top = random.Next(0, 100), // 0-100% of background container height
Size = random.Next(50, 130), // 50px to 130px
Rotation = random.Next(0, 360) // Random rotation
});
}
}
private class PokemonImage
{
public string Url { get; set; } = "";
public int Left { get; set; }
public int Top { get; set; }
public int Size { get; set; }
public int Rotation { get; set; }
}
}

View File

@ -0,0 +1,63 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Component
{
public partial class PokemonBackground
{
private class PokemonImage
{
public string Url { get; set; } = "";
public int Left { get; set; }
public int Top { get; set; }
public int Size { get; set; }
public int Rotation { get; set; }
}
[Parameter]
public List<string> PokemonImages { get; set; }
[Parameter]
public List<string> ShinyPokemonImages { get; set; }
private List<PokemonImage> _pokemonImages = new List<PokemonImage>();
private List<PokemonImage> _shinyPokemonImages = new List<PokemonImage>();
private Random random = new Random();
protected override async Task OnInitializedAsync()
{
await LoadPokemonBackgrounds();
}
private async Task LoadPokemonBackgrounds()
{
foreach (var pokemonimgurl in PokemonImages)
{
Console.WriteLine(pokemonimgurl);
_pokemonImages.Add(new PokemonImage
{
Url = pokemonimgurl, // URL retrieved from the database
Left = random.Next(0, 100),
Top = random.Next(0, 100),
Size = random.Next(50, 130),
Rotation = random.Next(0, 360)
});
}
foreach (var pokemonimgurl in ShinyPokemonImages)
{
_shinyPokemonImages.Add(new PokemonImage
{
Url = pokemonimgurl, // URL retrieved from the database
Left = random.Next(0, 100),
Top = random.Next(0, 100),
Size = random.Next(50, 130),
Rotation = random.Next(0, 360)
});
}
}
}
}

View File

@ -0,0 +1,52 @@
@attribute [StreamRendering]
@rendermode InteractiveServer
<div class="position-relative bg-white pokemon-card border border-5 border-info-subtle shadow-sm ">
<!-- Pokemon Name, Number, and Type -->
<div class="z-3">
@if (_pokemon.IsVariation)
{
<div class="pokemon-name"><p class="fw-bold card-title">@_pokemon.VariationName @_pokemon.PokemonName</p></div>
}
else
{
<div class="pokemon-name"><p class="fw-bold card-title">@_pokemon.PokemonName</p></div>
}
<div class="pokemon-number">
<p class="fw-light card-text">Pokédex #<strong>@_pokemon.PokemonId</strong></p>
</div>
<div >
<img class="pokemon-type" src="@GetTypeImageUrl(_pokemon.PokemonType)" />
</div>
</div>
<!-- Pokemon Image -->
<div class="flip-container z-1" @onclick="() => ToggleImage()">
<div class="flipper @(isShiny ? "flipped" : "")">
<img class="pokemon-image front" src="@_pokemon.PokemonImageUrl" />
<img class="pokemon-image back" src="@_pokemon.PokemonShinyImageUrl" />
</div>
</div>
<div class="bg-info-subtle border rounded border-2 border-info pokemon-flavor-text">
@if (string.IsNullOrEmpty(_pokemon.FlavorText))
{
<p class="">[ Pokemon Flavor Text Placeholder ]</p>
}
else
{
<p class="fw-light ">@_pokemon.FlavorText</p>
}
</div>
<!-- Pokemon Sleep Type and Specialty Badges -->
<div class="position-absolute bottom-0 end-0 mb-1 me-1 z-2">
<div class="d-flex justify-content-between">
<div class="m-1 badge @_pokemon.SleepType.ToLower()"><p class="statText">@_pokemon.SleepType</p></div>
<div class="m-1 badge @_pokemon.Speciality.ToLower()"><p class="statText">@_pokemon.Speciality</p></div>
</div>
</div>
</div>

View File

@ -0,0 +1,42 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Component
{
public partial class PokemonCard
{
[Parameter]
public Pokemon Pokemon { get; set; }
private Pokemon _pokemon { get; set; }
private bool isShiny = false;
protected override void OnParametersSet()
{
if (Pokemon != null)
{
_pokemon = Pokemon;
}
}
private void ToggleImage()
{
isShiny = !isShiny;
StateHasChanged();
}
private string GetTypeImageUrl(string pokemonType)
{
if (string.IsNullOrEmpty(pokemonType))
{
return "https://www.serebii.net/pokemonsleep/pokemon/type/normal.png"; // Fallback image
}
return $"https://www.serebii.net/pokemonsleep/pokemon/type/{pokemonType.ToLower()}.png";
}
}
}

View File

@ -0,0 +1,153 @@
.pokemon-card {
position: relative;
width: 24rem;
height: 32rem;
max-width: 24rem;
max-height: 32rem;
border-radius: 5% / 3.5%;
}
.pokemon-name {
position: absolute;
top: 0 !important;
left: 0 !important;
margin-top: 1.5rem !important;
margin-left: 0.5rem !important;
transform: translateY(-50%) !important;
font-size: 2.5rem;
}
.pokemon-number {
position: absolute;
top: 0 !important;
left: 0 !important;
margin-top: 3.3rem !important;
margin-left: 1.5rem !important;
transform: translateY(-50%) !important;
font-size: .75rem;
}
.pokemon-type {
position: absolute;
top: 0 !important;
right: 0 !important;
width: 2.5rem;
height: 2.5rem;
margin-top: .5rem !important;
margin-right: .5rem !important;
}
.pokemon-image {
width: 100%; /* Look to flip-container for the width/height of image */
height: 100%;
position: absolute;
top: 0;
left: 0;
}
.pokemon-flavor-text {
position: absolute;
top: 60% !important;
left: 50% !important;
width: 90%;
height: 20%;
padding: 0.25rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin-top: 3rem !important;
transform: translateX(-50%) !important;
display: flex; /* Centers text inside */
align-items: start;
justify-content: center; /* Horizontally centers text */
overflow: hidden; /* Ensures no scrollbar */
}
.pokemon-flavor-text p {
margin: 0;
width: 100%;
text-align: start;
font-size: min(14px, 1.5vw); /* Scales font but won't exceed 14px */
line-height: 1.2; /* Adjust spacing for readability */
white-space: normal; /* Ensures wrapping */
word-wrap: break-word;
hyphens: auto;
}
.flip-container {
position: absolute !important;
top: 40% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
perspective: 1000px;
display: inline-block;
width: 20rem;
height: 20rem;
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: 90px;
height: 30px;
color: white;
padding: 4px 8px;
border-radius: 30px;
}
.statText {
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: .8rem;
}
.dozing {
background-color: #fcdc5e;
}
.snoozing {
background-color: #4ce8ed;
}
.slumbering {
background-color: #4588fb;
}
.berries {
background-color: #24d86b;
}
.ingredients {
background-color: #fdbe4d;
}
.skills {
background-color: #47a0fc;
}

View File

@ -19,7 +19,7 @@ else
<!-- Table Header -->
<div class="card-header bg-secondary bg-gradient ml-0 py-3">
<div class="row">
<div class="col-12 text-center position-rel">
<div class="col-12 text-center position-relative">
<h2 class="text-info">Available Pokémon</h2>
<div class="m-1 col-1 badge bg-info position-absolute top-0 end-0"><p class="statText">@(pokemons.Count()) Pokemon</p></div>
</div>
@ -33,12 +33,13 @@ else
<!-- Table Head -->
<thead>
<tr>
<th class="text-bg-info" scope="col"></th>
<th class="text-bg-info" scope="col">#</th>
<th class="text-bg-info" scope="col">Pokemon</th>
<th class="text-bg-info" scope="col">Sleep Type</th>
<th class="text-bg-info" scope="col">Speciality</th>
<th class="text-bg-info" scope="col"></th>
<th class="text-bg-info col col-2" scope="col"></th>
<th class="text-bg-info col pokemon-dex-width" scope="col">#</th>
<th class="text-bg-info col pokemon-name-width" scope="col">Pokemon</th>
<th class="text-bg-info col pokemon-type-width" scope="col">Type</th>
<th class="text-bg-info col pokemon-type-width" scope="col">Sleep Type</th>
<th class="text-bg-info col pokemon-type-width" scope="col">Speciality</th>
<th class="text-bg-info col pokemon-type-width" scope="col"></th>
</tr>
</thead>
@ -94,17 +95,25 @@ else
<td style="vertical-align: auto;"> @pokemon.PokemonName</td>
}
<!-- Section 4: Sleep Type -->
<!-- Section 4: Pokemon Type -->
<td >
<div class="m-1 col-1" >
<img src="@GetTypeImageUrl(pokemon.PokemonType)" class="" style="width:60px; height:60px;" />
</div>
</td>
<!-- Section 5: Sleep Type -->
<td style="vertical-align: auto;">
<div class="m-1 col-1 badge @pokemon.SleepType.ToLower()"><p class="statText">@pokemon.SleepType</p></div>
</td>
<!-- Section 5: Speciality -->
<!-- Section 6: Speciality -->
<td style="padding-right: 30px; vertical-align: auto;">
<div class="m-1 col-1 badge @pokemon.Speciality.ToLower()"><p class="statText">@pokemon.Speciality</p></div>
</td>
<!-- Section 6: Functional Buttons -->
<!-- Section 7: Functional Buttons -->
<td>
<button class="btn btn-warning" @onclick="() => EditPokemon(pokemon.Id)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-fill" viewBox="0 0 16 16">

View File

@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.AspNetCore.Components;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.JSInterop;
using Portfolio.Domain.Features.Pokemon;
@ -6,19 +7,18 @@ namespace Portfolio.WebUI.Server.Components.Component
{
public partial class PokemonTable
{
[Parameter]
public List<Pokemon> AllPokemon { get; set; }
private List<Pokemon> pokemons = new List<Pokemon>();
private Dictionary<int, bool> isShiny = new Dictionary<int, bool>();
protected override async Task OnInitializedAsync()
protected override void OnParametersSet()
{
var result = await PokemonService.GetAllPokemonAsync();
if (result is not null)
{
pokemons = result;
pokemons.Sort((x, y) => x.PokemonId.CompareTo(y.PokemonId));
if (AllPokemon != null) {
pokemons = AllPokemon.ToList();
// Initialize dictionary with false (show base image first)
foreach (var pokemon in pokemons)
{
isShiny[pokemon.Id] = false;
@ -54,5 +54,16 @@ namespace Portfolio.WebUI.Server.Components.Component
{
Navigation.NavigateTo($"/pokemonsleep/edit/{id}");
}
private string GetTypeImageUrl(string pokemonType)
{
if (string.IsNullOrEmpty(pokemonType))
{
return "https://www.serebii.net/pokemonsleep/pokemon/type/normal.png"; // Fallback image
}
return $"https://www.serebii.net/pokemonsleep/pokemon/type/{pokemonType.ToLower()}.png";
}
}
}

View File

@ -94,3 +94,14 @@
}
.pokemon-dex-width {
width:3%;
}
.pokemon-name-width {
width:10%;
}
.pokemon-type-width {
width:5%;
}

View File

@ -1,10 +1,12 @@
<div class="d-flex flex-column flex-shrink-0 p-3 bg-info vh-100" style="width: 280px; ">
<a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-primary text-decoration-none">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-arrow-through-heart" viewBox="0 0 16 16">
@inject IJSRuntime JS
<div class="d-flex flex-column flex-shrink-0 p-3 bg-info vh-100 sidebar @(isSidebarOpen ? "open" : "closed") " style="transition: width 0.3s;">
<div class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-primary text-decoration-none">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-arrow-through-heart" viewBox="0 0 16 16" @onclick="ToggleSidebar" style="cursor: pointer;">
<path fill-rule="evenodd" d="M2.854 15.854A.5.5 0 0 1 2 15.5V14H.5a.5.5 0 0 1-.354-.854l1.5-1.5A.5.5 0 0 1 2 11.5h1.793l.53-.53c-.771-.802-1.328-1.58-1.704-2.32-.798-1.575-.775-2.996-.213-4.092C3.426 2.565 6.18 1.809 8 3.233c1.25-.98 2.944-.928 4.212-.152L13.292 2 12.147.854A.5.5 0 0 1 12.5 0h3a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-.854.354L14 2.707l-1.006 1.006c.236.248.44.531.6.845.562 1.096.585 2.517-.213 4.092-.793 1.563-2.395 3.288-5.105 5.08L8 13.912l-.276-.182a22 22 0 0 1-2.685-2.062l-.539.54V14a.5.5 0 0 1-.146.354zm2.893-4.894A20.4 20.4 0 0 0 8 12.71c2.456-1.666 3.827-3.207 4.489-4.512.679-1.34.607-2.42.215-3.185-.817-1.595-3.087-2.054-4.346-.761L8 4.62l-.358-.368c-1.259-1.293-3.53-.834-4.346.761-.392.766-.464 1.845.215 3.185.323.636.815 1.33 1.519 2.065l1.866-1.867a.5.5 0 1 1 .708.708z" />
</svg>
<span class="fs-4 mx-2">Kira Jiroux</span>
</a>
</div>
<hr>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
@ -43,4 +45,16 @@
</a>
</div>
</div>
</div>
@code {
private bool isSidebarOpen = true;
private async void ToggleSidebar()
{
isSidebarOpen = !isSidebarOpen;
await JS.InvokeVoidAsync("console.log", $"Sidebar toggled: {isSidebarOpen}");
StateHasChanged(); // Force the UI to update
}
}

View File

@ -0,0 +1,9 @@
.sidebar.closed {
width: 0;
overflow: hidden;
padding: 0;
}
.sidebar.open {
width: 256px;
}

View File

@ -2,7 +2,7 @@
<PageTitle>Home</PageTitle>
<PokemonBackground />
<!-- <PokemonBackground />-->
<h1 class="test">Hello, world!</h1>
<p>Ensuring that git is connected properly.</p>

View File

@ -14,8 +14,11 @@
}
else
{
<div class="w-50 mt-5 m-auto create-container">
<div class="card-header bg-secondary bg-gradient ml-0 py-3">
<!-- 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>
@ -23,7 +26,8 @@ else
</div>
</div>
<div class="container m-lg-1">
<!-- Body -->
<div class="p-3 w-100 flex-column ">
<EditForm class="col mb-3" Model="pokemon" OnValidSubmit="HandleSubmit">
<DataAnnotationsValidator />
@ -31,27 +35,55 @@ else
<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 disabled />
<InputText id="PokemonName" @bind-Value="pokemon.PokemonName" class="form-control w-75" required />
</div>
</div>
<div class="row 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>
<div class="row">
<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>
<div class="row 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 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>
<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>
<!-- New Image URL Field -->
<div class="row mb-3 m-auto">
@ -63,6 +95,11 @@ else
<InputText id="ImageUrl" @bind-Value="pokemon.PokemonShinyImageUrl" class="form-control" />
</div>
<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" required />
</div>
<button type="submit" class="btn btn-primary mb-3">Save Changes</button>
<button type="button" class="btn btn-secondary mb-3" @onclick="Cancel">Cancel</button>
</EditForm>
@ -71,22 +108,5 @@ else
}
@code {
[Parameter] public int Id { get; set; }
private Pokemon? pokemon;
protected override async Task OnInitializedAsync()
{
pokemon = await PokemonService.GetPokemonByIdAsync(Id);
}
private async Task HandleSubmit()
{
await PokemonService.UpdatePokemonAsync(pokemon);
Navigation.NavigateTo("/pokemonsleep");
}
private void Cancel()
{
Navigation.NavigateTo("/pokemonsleep");
}
}

View File

@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Pages
{
public partial class PokemonEdit
{
[Parameter] public int Id { get; set; }
private Pokemon? pokemon;
protected override async Task OnInitializedAsync()
{
pokemon = await PokemonService.GetPokemonByIdAsync(Id);
}
private async Task HandleSubmit()
{
await PokemonService.UpdatePokemonAsync(pokemon);
Navigation.NavigateTo("/pokemonsleep");
}
private void Cancel()
{
Navigation.NavigateTo("/pokemonsleep");
}
}
}

View File

@ -0,0 +1,7 @@
body {
}
.edit-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;
}

View File

@ -18,27 +18,31 @@
}
else
{
<div class="w-75 mt-3 m-auto rate-container">
<!-- Total Component -->
<div class="w-75 mt-3 m-auto rate-container bg-info ">
<div class="card-header bg-secondary bg-gradient ml-0 py-3">
<!-- Header -->
<div class="card-header bg-secondary ml-0 py-3 w-100 ">
<div class="row">
<div class="col-12 text-center">
<div class=" text-center">
<h2 class="text-info">Pokémon Rater</h2>
</div>
</div>
</div>
<div class="card-body p-4 col-12">
<!-- Section 1: Pokemon Selection -->
<div class="row pb-3">
<div class="col-3">
<div class="position-relative">
<!-- Body -->
<div class="p-3 w-100 flex-column ">
<!-- Section 1: Top - Pokemon Selection -->
<div class="row flex-row justify-content-end">
<div class="pokemon-search col">
<div class="position-relative pb-3" >
<!-- Pokemon Selection -->
<input type="text"
class="form-control form-control-lg"
class="form-control form-control"
@bind="PokemonSearchTerm"
placeholder="Search Pokémon..."
@oninput="OnSearchTextChanged" />
@oninput="OnSearchTextChanged"
/>
@if (FilteredPokemonList.Any() && !string.IsNullOrWhiteSpace(PokemonSearchTerm))
{
<ul class="list-group position-absolute w-100" style="z-index: 1000; max-height: 200px; overflow-y: auto;">
@ -59,137 +63,120 @@ else
}
</div>
</div>
<div class="col-2">
<button class="btn btn-danger">Clear</button>
</div>
</div>
<!-- Section 2: Pokemon Rating -->
<!-- Section 2: Bottom - Pokemon -->
@if (SelectedPokemon != null)
{
<div class="border rounded p-3 row">
<div class="d-flex align-top col">
<!-- Pokemon Interface -->
<div class="m-3 p-1 col-5">
<!-- Image and other Pokemon info -->
<div class="flip-container" @onclick="() => ToggleImage(SelectedPokemon.Id)">
<div class="flipper @(isShiny[SelectedPokemon.Id] ? "flipped" : "")">
<img class="front" src="/pokemon_images/normal/@(SelectedPokemon.IsVariation ? $"{SelectedPokemon.PokemonId}-{SelectedPokemon.VariationName.ToLower()}{SelectedPokemon.PokemonName.ToLower()}" : SelectedPokemon.PokemonId).png" />
<img class="back" src="/pokemon_images/shiny/@(SelectedPokemon.IsVariation ? $"{SelectedPokemon.PokemonId}-{SelectedPokemon.VariationName.ToLower()}{SelectedPokemon.PokemonName.ToLower()}" : SelectedPokemon.PokemonId).png" />
</div>
</div>
<div class="row">
<div class="col-7">
<div class="row mb-0">
@if (SelectedPokemon.IsVariation)
{
<h2>@SelectedPokemon.VariationName @SelectedPokemon.PokemonName</h2>
}
else
{
<h3>@SelectedPokemon.PokemonName</h3>
}
</div>
<div class="mt-0">
<p class="col-4">Pokédex #<strong>@SelectedPokemon.PokemonId</strong></p>
</div>
</div>
<div class="col-5 p-1">
<div class="row d-flex justify-content-between">
<div class="m-1 col badge @SelectedPokemon.SleepType.ToLower()"><p class="statText">@SelectedPokemon.SleepType</p></div>
<div class="m-1 col badge @SelectedPokemon.Speciality.ToLower()"><p class="statText">@SelectedPokemon.Speciality</p></div>
</div>
</div>
</div>
<div class="row w-100">
<!-- Section 2A: Left Side - Pokemon Card View -->
<div class="col">
<div class="position-relative d-flex justify-content-center">
<PokemonCard Pokemon="SelectedPokemon" />
</div>
<!-- Nature / Subskill Selection Dropdowns-->
<div class="m-3 p-1 col">
<h4 class="mb-3">Select Nature & Subskills</h4>
<!-- Nature -->
<div class="row">
<div class="col-12">
<label>Select Nature</label>
<select class="form-control form-control-lg mb-2" @bind="SelectedNatureId">
<option value="" disabled>Choose Nature...</option>
@foreach (var nature in NatureList)
{
<option value="@nature.Id">@nature.Nature</option>
}
</select>
</div>
</div>
<!-- Subskills -->
<div class="row">
<!-- Subskill 1 -->
<div class="col-4">
<label for="subskillSelect1">Select Level 10 Subskill</label>
<select id="subskillSelect1" class="form-control form-control mb-2" @bind="subskillSelect1">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
<!-- Subskill 2 -->
<div class="col-4">
<label for="subskillSelect2">Select Level 25 Subskill</label>
<select id="subskillSelect2" class="form-control form-control mb-2" @bind="subskillSelect2">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
<!-- Subskill 3 -->
<div class="col-4">
<label for="subskillSelect3">Select Level 50 Subskill</label>
<select id="subskillSelect3" class="form-control form-control mb-2" @bind="subskillSelect3">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
</div>
<div class="row">
<!-- Subskill 4 -->
<div class="col-4">
<label for="subskillSelect4">Select Level 75 Subskill</label>
<select id="subskillSelect4" disabled class="form-control form-control mb-2" @bind="subskillSelect4">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
<!-- Subskill 5 -->
<div class="col-4">
<label for="subskillSelect5">Select Level 100 Subskill</label>
<select id="subskillSelect5" disabled class="form-control form-control mb-2" @bind="subskillSelect5">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
</div>
</div>
</div>
<!-- Calculate -->
<div class="d-flex justify-content-between align-items-center mt-3 ">
<button class="btn btn-primary mx-2" @onclick="CalculateScore">Calculate Final Score</button>
<h4>Final Score: <span class="finalScore" style="background-color: @ScoreBackgroundColor">@FinalScore</span></h4>
<!-- Section 2B: Right Side - Stat Selection + Form Submission -->
<div class="col">
<h4 class="mb-3">Select Nature & Subskills</h4>
<!-- Nature -->
<div class="">
<div class="">
<label>Select Nature</label>
<select class="form-control form-control-sm mb-2" @bind="SelectedNatureId">
<option value="" disabled>Choose Nature...</option>
@foreach (var nature in NatureList)
{
<option value="@nature.Id">@nature.Nature</option>
}
</select>
</div>
</div>
<!-- Subskill 1 -->
<div class="">
<div class="">
<label for="subskillSelect1">Select Level 10 Subskill</label>
<select id="subskillSelect1" class="form-control form-control-sm mb-2" @bind="subskillSelect1">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
</div>
<!-- Subskill 2 -->
<div class="">
<div class="">
<label for="subskillSelect2">Select Level 25 Subskill</label>
<select id="subskillSelect2" class="form-control form-control-sm mb-2" @bind="subskillSelect2">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
</div>
<!-- Subskill 3 -->
<div class="">
<div class="">
<label for="subskillSelect3">Select Level 50 Subskill</label>
<select id="subskillSelect3" class="form-control form-control-sm mb-2" @bind="subskillSelect3">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
</div>
<!-- Subskill 4 Disabled -->
<div class="">
<div class="">
<label for="subskillSelect4">Select Level 75 Subskill</label>
<select id="subskillSelect4" disabled class="form-control form-control-sm mb-2" @bind="subskillSelect4">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
</div>
<!-- Subskill 5 Disabled -->
<div class="">
<div class="">
<label for="subskillSelect5">Select Level 100 Subskill</label>
<select id="subskillSelect5" disabled class="form-control form-control-sm mb-2" @bind="subskillSelect5">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
</div>
<!-- Calculate Score -->
<div class="">
<div class="d-flex justify-content-between align-items-center mt-3">
<button class="btn btn-primary" @onclick="CalculateScore">Calculate Final Score</button>
<h4 class="bg-white border border-1 rounded p-2 m-2">
<span class="finalScore rounded" style="background-color: @ScoreBackgroundColor">@FinalScore</span>
</h4>
</div>
</div>
</div>
</div>
}

View File

@ -39,9 +39,6 @@ namespace Portfolio.WebUI.Server.Components.Pages
private string ScoreBackgroundColor;
private string ScoreColor;
private string PokemonImageUrl => SelectedPokemon != null
? $"https://www.serebii.net/pokemonsleep/pokemon/{SelectedPokemon.Id}.png"
: string.Empty;
private string PokemonSearchTerm = string.Empty;
@ -54,6 +51,7 @@ namespace Portfolio.WebUI.Server.Components.Pages
{
// Trigger filtering as the user types
PokemonSearchTerm = e.Value.ToString();
}
private async Task SelectPokemon(Pokemon pokemon)

View File

@ -3,6 +3,11 @@
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;
}
.pokemon-search {
width: 95%;
}
.flip-container {
@ -79,8 +84,6 @@
}
.finalScore {
width: 30px;
height: 30px;
color: white;
padding: 4px 8px;
text-align: center;

View File

@ -1,5 +1,7 @@
@page "/pokemonsleep"
@inject IPokemonService PokemonService
@attribute [StreamRendering]
@rendermode InteractiveServer
@ -7,10 +9,10 @@
<PageTitle>Pokémon Sleep</PageTitle>
<div class="w-100">
<PokemonBackground />
<PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" />
<PokemonHeader />
<PokemonTable />
<PokemonTable AllPokemon="pokemons"/>
</div>

View File

@ -0,0 +1,30 @@
using Portfolio.Application.Services.PokemonService;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.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);
}
}
}
}
}