Compare commits

...

47 Commits

Author SHA1 Message Date
Kira Jiroux 0b316a3b4b STOP BEING ON MASTER. Anyways made the temp blanket visualizer fit in a bootstrap grid :) 2025-06-13 15:27:14 -04:00
Kira Jiroux da30a2b0d5 The RangeEditor looks sick now. 2025-06-11 16:24:54 -04:00
Kira Jiroux b1a9567d31 Made the TemperatureRangeEditor component better, but REALLY need to get off master. 2025-06-11 12:33:19 -04:00
Kira Jiroux 7b3761d6a5 Changed Pokemon to Pokémon 2025-06-11 11:45:32 -04:00
Kira Jiroux 89f60d8c29 When will I learn? 2025-06-09 22:58:38 -04:00
Kira Jiroux 87be31f2f5 OKAY I'm done on master now. 2025-06-09 22:55:01 -04:00
Kira Jiroux ef3e432347 Shouldn't have been on master, but everything is still working good. 2025-06-09 22:43:33 -04:00
Kira Jiroux 5b2aafc4e5 Made some responsive styling changes. 2025-06-09 18:21:34 -04:00
Kira Jiroux 04320dc9e1 Minor changes to tweak table, primarily bg. 2025-06-09 17:45:38 -04:00
Kira Jiroux 1955a210aa PokemonCard component visible in PokemonView now. Also added the search feature in PokemonSelector into the PokemonTable. Made other changes too but minor. 2025-06-09 16:55:52 -04:00
Kira Jiroux 17607b663b Okay made some serious headway, and now the rater is pretty much perfected. Really need to look into responsive styling if I want it to be mobile efficient though. 2025-06-06 00:09:47 -04:00
Kira Jiroux 9e765c776d Just moved the background-image onto singular lines. 2025-06-04 17:54:14 -04:00
Kira Jiroux 9b9a313411 Refactored the UI and made it possible to switch between pokemon; much better. 2025-06-04 17:52:56 -04:00
Kira Jiroux 6c879038f6 Quick changes. 2025-06-02 17:34:53 -04:00
Kira Jiroux 764c094a64 First time back after a while. Rolled back some changes that made the app inoperable (specifically NWSWeatherService). Temp Blanket visualizer works, including color editing for ranges. Need to plug in real data. 2025-06-02 17:12:40 -04:00
Kira Jiroux bdd0f2e8bb Saving Responsive Styling changes before shifting gears. 2025-04-21 14:33:50 -04:00
Kira Jiroux b881c06b83 Quick, quick revision elsewhere to account for Darkrai. 2025-04-16 10:21:31 -04:00
Kira Jiroux 79bda1d0fc Keep playing with the table gosh. I want to shower. 2025-04-11 20:26:18 -04:00
Kira Jiroux aea7c06819 Some responsive-ness is happenin. 2025-04-11 20:07:06 -04:00
Kira Jiroux d04f9b260b Reconfiguring the PokemonSleep homepage 2025-04-11 17:56:25 -04:00
Kira Jiroux 779ca3620c Pokemon Cards are beautiful, serving different card views based on type. Also got pokemon going to their own separate extension in order for table button to appear better. Excellent! 2025-04-10 18:16:07 -04:00
Kira Jiroux 17334250f9 Editing view to be compatible with new bootstrap-brite.css. Still compatible with bootstrap-lumen.css 2025-04-10 14:43:21 -04:00
Kira Jiroux 5dd07c7c3f Adjusted table row layouts, and edited buttons to appear circular to coincide with Pkmn theme. 2025-04-10 13:30:26 -04:00
Kira Jiroux 8953387f66 Made table rows vertically aligned. 2025-04-08 15:02:41 -04:00
Kira Jiroux 9c87f3e4cf Moved Pokemon Page files into corresponding Page folder w/in directory. 2025-04-07 13:59:56 -04:00
Kira Jiroux 8f1dbebb30 Readjusting the file structuring, moved Pokemon-specific components into respective folder 2025-04-07 13:39:03 -04:00
Kira Jiroux f12217839b Putting in Loading Bars where necessary. 2025-04-05 00:40:49 -04:00
Kira Jiroux b8f3b45d1c Put in proper Loading bar. 2025-04-05 00:37:26 -04:00
Kira Jiroux 99ac4581ae Individual pokemon views are being served and can move to the next pokemon. Edit form can be accessed from either table or individual view and goes back to where it was. 2025-04-05 00:28:36 -04:00
Kira Jiroux 42d3a864e4 Looking to make individual pokemon views but later 2025-04-04 15:44:03 -04:00
Kira Jiroux dd9b0006d3 Serving up Pokemon Types and Flavor Text, adjustments made where necessary. 2025-04-04 15:35:49 -04:00
Kira Jiroux 0f235895f4 Pokemon Cards made and Pokemon Rate revised (also Pokemon Edit but only cause name field was disabled). Will need to revise Pokemon Rate view to adjust right-side. 2025-04-04 13:17:10 -04:00
Kira Jiroux a8f2bfd2b8 Card created, Pokemon Rater page adjusted. But when running with debugger it doesn't come up right, does without the debugger tho. Idk. 2025-04-02 13:39:06 -04:00
Kira Jiroux 6c8119d83c Sidebar isn't toggling, but accidentally started moving into PokemonRate. Merging and switching. 2025-04-02 11:02:44 -04:00
Kira Jiroux 3aad300f33 Pokemon are being delivered to the pokemon table and pokemon background components; works for table, background still in need of work. But not pertinent. 2025-04-02 10:19:51 -04:00
Kira Jiroux b6b4be9dbb Data being passed from PokemonSleep parent to PokemonTable child. Getting ready for PokemonBackground! 2025-03-28 15:33:13 -04:00
Kira Jiroux 2e1063c741 Doing some things for Pokemon Background, but ultimately looking to pass data to components. Branch incomming. 2025-03-28 14:57:44 -04:00
Kira Jiroux 3552aa99e4 Quick minor fix of css class name 2025-03-21 15:07:09 -04:00
Kira Jiroux 3b0cbecfc1 Pokemon Entity has changed and reflected in DB. Included additional functionality to edit Pokemon entities to assist with the DB change. Changed how Pokemon Shiny Images/Pokemon Specialty presented in cases where those values are null (Cresselia). 2025-03-21 15:02:07 -04:00
Kira Jiroux d31699f2de Trying to figure out why it won't go the full width but whatevs 2025-03-07 17:42:44 -05:00
Kira Jiroux a740a4e602 Quick and cute sidebar 2025-03-07 17:17:04 -05:00
Kira Jiroux c9b584abcd Table appropriately parsed into correct files, same with Headers, Rating page,Create page, and Sleep Page. 2025-03-07 16:23:09 -05:00
Kira Jiroux 2a01c2fae2 Became more of a searchable field with viewable dropdown options, but I'm happy with it. 2025-03-07 13:42:09 -05:00
Kira Jiroux 536d28a54b Minor changes in an attempt to make a pokemon sticker background. Will return but will offer no issues. Plus NavMenu3 2025-03-07 12:31:34 -05:00
Kira Jiroux 7a3d524f91 Making some NavMenu's 2025-02-20 13:50:17 -05:00
Kira Jiroux 5a8f0263f0 Serving colors with ratings. 2025-02-19 16:57:49 -05:00
Kira Jiroux 14663ea0bb Altering layout 2025-02-19 16:19:24 -05:00
86 changed files with 18503 additions and 826 deletions

View File

@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Portfolio.Application.Services.Articles;
using Portfolio.Application.Services.NWSWeatherService;
using Portfolio.Application.Services.PokemonNatureService;
using Portfolio.Application.Services.PokemonService;
using Portfolio.Application.Services.PokemonSubskillService;
@ -19,6 +20,7 @@ namespace Portfolio.Application
services.AddScoped<IPokemonService, PokemonService>();
services.AddScoped<IPokemonSubskillService, PokemonSubskillService>();
services.AddScoped<IPokemonNatureService, PokemonNatureService>();
//services.AddScoped<INWSWeatherService, NWSWeatherService>();

View File

@ -0,0 +1,17 @@
using Portfolio.Domain.Features.TemperatureDay;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Portfolio.Application.Services.NWSWeatherService
{
public interface INWSWeatherService
{
Task<string> GetNearestStationAsync(double latitude, double longitude);
Task<double?> GetDailyAverageTempAsync(string stationId, DateTime date);
Task<List<TemperatureDay>> GetTemperatureDataAsync(double latitude, double longitude, int year);
}
}

View File

@ -0,0 +1,123 @@
using Portfolio.Domain.Features.TemperatureDay;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Portfolio.Application.Services.NWSWeatherService
{
public class NWSWeatherService : INWSWeatherService
{
private readonly HttpClient _httpClient;
public NWSWeatherService(HttpClient httpClient)
{
_httpClient = httpClient;
_httpClient.DefaultRequestHeaders.Add("User-Agent", "kira.jiroux@gmail.com"); // Replace with your email ideally
}
public async Task<string> GetNearestStationAsync(double latitude, double longitude)
{
var url = $"https://api.weather.gov/points/{latitude},{longitude}";
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
using var stream = await response.Content.ReadAsStreamAsync();
var json = await JsonDocument.ParseAsync(stream);
var stationUrl = json.RootElement
.GetProperty("properties")
.GetProperty("observationStations")
.GetString();
if (string.IsNullOrEmpty(stationUrl))
throw new Exception("Could not find station info.");
var stationListResponse = await _httpClient.GetAsync(stationUrl);
stationListResponse.EnsureSuccessStatusCode();
using var stationStream = await stationListResponse.Content.ReadAsStreamAsync();
var stationJson = await JsonDocument.ParseAsync(stationStream);
var firstStation = stationJson.RootElement
.GetProperty("features")[0]
.GetProperty("properties")
.GetProperty("stationIdentifier")
.GetString();
return firstStation;
}
public async Task<double?> GetDailyAverageTempAsync(string stationId, DateTime date)
{
var start = date.ToString("yyyy-MM-dd") + "T00:00:00Z";
var end = date.ToString("yyyy-MM-dd") + "T23:59:59Z";
var url = $"https://api.weather.gov/stations/{stationId}/observations?start={start}&end={end}";
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
using var stream = await response.Content.ReadAsStreamAsync();
var json = await JsonDocument.ParseAsync(stream);
var temps = new List<double>();
foreach (var feature in json.RootElement.GetProperty("features").EnumerateArray())
{
if (feature.TryGetProperty("properties", out var props) &&
props.TryGetProperty("temperature", out var tempObj) &&
tempObj.TryGetProperty("value", out var valueProp) &&
valueProp.ValueKind == JsonValueKind.Number)
{
var celsius = valueProp.GetDouble();
if (!double.IsNaN(celsius))
{
var fahrenheit = (celsius * 9.0 / 5.0) + 32.0;
temps.Add(fahrenheit);
}
}
}
if (temps.Count == 0)
return null;
return temps.Average();
}
public async Task<List<TemperatureDay>> GetTemperatureDataAsync(double latitude, double longitude, int year)
{
var stationId = await GetNearestStationAsync(latitude, longitude);
var result = new List<TemperatureDay>();
var startDate = new DateTime(year, 1, 1);
var endDate = new DateTime(year, 12, 31);
for (var date = startDate; date <= endDate; date = date.AddDays(1))
{
Console.WriteLine($"Fetching {date:yyyy-MM-dd}...");
var avgTemp = await GetDailyAverageTempAsync(stationId, date);
if (avgTemp.HasValue)
{
result.Add(new TemperatureDay
{
Date = date,
AvgTemp = Math.Round(avgTemp.Value, 1)
});
}
else
{
Console.WriteLine($"No data for {date:yyyy-MM-dd}");
}
await Task.Delay(600); // small delay to be nice to NWS
}
return result;
}
}
}

View File

@ -10,8 +10,14 @@ namespace Portfolio.Application.Services.PokemonService
public interface IPokemonService
{
Task<List<Pokemon>> GetAllPokemonAsync();
Task<Pokemon> GetPokemonByPokemonIdAsync(int id);
Task<Pokemon> GetPokemonByIdAsync(int id);
Task<List<int>> GetAllPokemonIdsAsync();
Task<int?> GetPreviousPokemonIdAsync(int id);
Task<int?> GetNextPokemonIdAsync(int id);
Task<int?> GetVariationPokemonIdAsync(int id);
Task AddPokemonAsync(Pokemon pokemon);
Task DeletePokemonAsync(int pokemonId);
Task UpdatePokemonAsync(Pokemon pokemon);
}
}

View File

@ -33,9 +33,40 @@ namespace Portfolio.Application.Services.PokemonService
}
public async Task<List<int>> GetAllPokemonIdsAsync()
{
return await _pokemonRepository.GetAllPokemonIdsAsync();
}
public async Task<int?> GetNextPokemonIdAsync(int id)
{
return await _pokemonRepository.GetNextPokemonIdAsync(id);
}
public async Task<Pokemon> GetPokemonByPokemonIdAsync(int id)
{
return await _pokemonRepository.GetPokemonByPokemonIdAsync(id);
}
public async Task<Pokemon> GetPokemonByIdAsync(int id)
{
return await _pokemonRepository.GetPokemonByIdAsync(id);
}
public async Task<int?> GetPreviousPokemonIdAsync(int id)
{
return await _pokemonRepository.GetPreviousPokemonIdAsync(id);
}
public async Task UpdatePokemonAsync(Pokemon pokemon)
{
await _pokemonRepository.UpdatePokemonAsync(pokemon);
}
public async Task<int?> GetVariationPokemonIdAsync(int id)
{
return await _pokemonRepository.GetVariationPokemonIdAsync(id);
}
}
}

View File

@ -18,6 +18,7 @@ namespace Portfolio.Application.Services.PokemonSubskillService
}
public async Task<List<PokemonSubskill>> GetAllPokemonSubskillsAsync()
{
Console.WriteLine("Does this fire every time");
return await _pokemonSubskillRepository.GetAllPokemonSubskillsAsync();
}
}

View File

@ -10,8 +10,14 @@ namespace Portfolio.Domain.Features.Pokemon
{
Task<List<Pokemon>> GetAllPokemonsAsync();
Task<Pokemon> GetPokemonByIdAsync(int id);
Task<Pokemon> GetPokemonByPokemonIdAsync(int id);
Task<List<int>> GetAllPokemonIdsAsync();
Task<int?> GetPreviousPokemonIdAsync(int currentPokemonId);
Task<int?> GetNextPokemonIdAsync(int currentPokemonId);
Task<int?> GetVariationPokemonIdAsync(int pokemonId);
Task AddPokemonAsync(Pokemon pokemon);
Task DeletePokemonAsync(int pokemonId);
Task UpdatePokemonAsync(Pokemon pokemon);
}
}

View File

@ -13,8 +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,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Portfolio.Domain.Features.TemperatureDay
{
public class TemperatureDay
{
public DateTime Date { get; set; }
public double AvgTemp { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Portfolio.Domain.Features.TemperatureRange
{
public class TemperatureRange
{
public double Min { get; set; }
public double Max { get; set; }
public string Color { get; set; } = "#ffffff";
}
}

View File

@ -2,6 +2,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Portfolio.Application.Services.Articles;
using Portfolio.Application.Services.NWSWeatherService;
using Portfolio.Application.Services.PokemonService;
using Portfolio.Domain.Features.Pokemon;
using Portfolio.Domain.Features.Pokemon_Natures;
@ -25,6 +26,7 @@ namespace Portfolio.Infrastructure
services.AddScoped<IPokemonRepository, PokemonRepository>();
services.AddScoped<IPokemonNatureRepository, PokemonNatureRepository>();
services.AddScoped<IPokemonSubskillRepository, PokemonSubskillRepository>();
//services.AddScoped<INWSWeatherService, NWSWeatherService>();
return services;
}

View File

@ -0,0 +1,118 @@
// <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("20250321161454_AddPokemonImageUrl")]
partial class AddPokemonImageUrl
{
/// <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>("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 AddPokemonImageUrl : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "PokemonImageUrl",
table: "Pokemons",
type: "nvarchar(max)",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "PokemonImageUrl",
table: "Pokemons");
}
}
}

View File

@ -0,0 +1,121 @@
// <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("20250321165440_AddPokemonShinyImageUrl")]
partial class AddPokemonShinyImageUrl
{
/// <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>("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 AddPokemonShinyImageUrl : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "PokemonShinyImageUrl",
table: "Pokemons",
type: "nvarchar(max)",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "PokemonShinyImageUrl",
table: "Pokemons");
}
}
}

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,16 +29,28 @@ namespace Portfolio.Infrastructure.Migrations
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)");
@ -55,7 +67,7 @@ namespace Portfolio.Infrastructure.Migrations
b.ToTable("Pokemons");
});
modelBuilder.Entity("Portfolio.Domain.Features.Pokemon_Natures.PokemonNatures", b =>
modelBuilder.Entity("Portfolio.Domain.Features.Pokemon_Natures.PokemonNature", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -81,7 +93,7 @@ namespace Portfolio.Infrastructure.Migrations
b.ToTable("PokemonNatures");
});
modelBuilder.Entity("Portfolio.Domain.Features.Pokemon_Subskills.PokemonSubskills", b =>
modelBuilder.Entity("Portfolio.Domain.Features.Pokemon_Subskills.PokemonSubskill", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()

View File

@ -21,11 +21,14 @@ namespace Portfolio.Infrastructure.Repositories
{
return await _context.Pokemons.ToListAsync();
}
public async Task<Pokemon> GetPokemonByPokemonIdAsync(int id)
{
return await _context.Pokemons.FirstOrDefaultAsync(p => p.PokemonId == id);
}
public async Task<Pokemon> GetPokemonByIdAsync(int id)
{
return await _context.Pokemons.FirstOrDefaultAsync(p => p.Id == id);
}
public async Task AddPokemonAsync(Pokemon pokemon)
{
_context.Pokemons.Add(pokemon);
@ -41,5 +44,73 @@ namespace Portfolio.Infrastructure.Repositories
}
}
public async Task UpdatePokemonAsync(Pokemon pokemon)
{
_context.Pokemons.Update(pokemon);
await _context.SaveChangesAsync();
}
public async Task<int?> GetPreviousPokemonIdAsync(int currentPokemonId)
{
// Get the previous Pokémon's PokemonId
var prevPokemonId = await _context.Pokemons
.Where(p => p.PokemonId < currentPokemonId)
.OrderByDescending(p => p.PokemonId)
.Select(p => (int?)p.PokemonId)
.FirstOrDefaultAsync();
// If no previous PokemonId is found, wrap around to the last one
if (prevPokemonId == null)
{
prevPokemonId = await _context.Pokemons
.OrderByDescending(p => p.PokemonId) // Get the last PokemonId
.Select(p => (int?)p.PokemonId)
.FirstOrDefaultAsync();
}
return prevPokemonId;
}
public async Task<int?> GetNextPokemonIdAsync(int currentPokemonId)
{
// Get the next Pokémon's PokemonId
var nextPokemonId = await _context.Pokemons
.Where(p => p.PokemonId > currentPokemonId)
.OrderBy(p => p.PokemonId)
.Select(p => (int?)p.PokemonId)
.FirstOrDefaultAsync();
// If no next PokemonId is found, wrap around to the first one
if (nextPokemonId == null)
{
nextPokemonId = await _context.Pokemons
.OrderBy(p => p.PokemonId) // Get the first PokemonId
.Select(p => (int?)p.PokemonId)
.FirstOrDefaultAsync();
}
return nextPokemonId;
}
public async Task<List<int>> GetAllPokemonIdsAsync()
{
return await _context.Pokemons
.OrderBy(p => p.PokemonId) // Ensure it's ordered by PokemonId
.Select(p => p.PokemonId)
.ToListAsync();
}
public async Task<int?> GetVariationPokemonIdAsync(int pokemonId)
{
// Find a variation for the given PokemonId (where IsVariation is true)
var variation = await _context.Pokemons
.Where(p => p.PokemonId == pokemonId && p.IsVariation)
.FirstOrDefaultAsync();
// Return the Id of the variation, or null if no variation is found
return variation?.Id;
}
}
}

View File

@ -6,16 +6,17 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
<link rel="stylesheet" href="bootstrap/bootstrap-brite.css" /> <!-- app.css -->
<link rel="stylesheet" href="Portfolio.WebUI.Server.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
</head>
<body>
<body class="bg-primary-subtle">
<Routes />
<script src="_framework/blazor.web.js"></script>
<script src="js/site.js"></script>
</body>
</html>

View File

@ -0,0 +1,36 @@
@attribute [StreamRendering]
@rendermode InteractiveServer
<div>
@if (TemperatureDays is null || TemperatureRanges is null)
{
<Loading />
}
else
{
<h3 class="text-xl font-bold mb-4">Temperature Blanket Visualizer</h3>
<div>
<div class="row">
<div class="col-10">
<div class="d-flex">
<div class="temperature-blanket">
@foreach (var day in TemperatureDays)
{
var color = GetColorForTemp(day.AvgTemp);
<div style="width: 6px; height: 600px; background-color:@color; margin-right: 1px;"
title="@day.Date.ToString("MMM dd") - @day.AvgTemp°F (@color)">
</div>
}
</div>
</div>
</div>
<div class="col">
<TemperatureRangeEditor TempRanges="@TemperatureRanges" OnRangesChanged="HandleRangesChanged" />
</div>
</div>
</div>
}
</div>

View File

@ -0,0 +1,46 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.TemperatureDay;
using Portfolio.Domain.Features.TemperatureRange;
namespace Portfolio.WebUI.Server.Components.Component.Crochet_Components
{
public partial class TemperatureBlanketVisualizer : ComponentBase
{
[Parameter] public List<TemperatureDay> TemperatureDays { get; set; }
public List<TemperatureRange> TemperatureRanges { get; set; } = new();
protected override void OnInitialized()
{
TemperatureRanges = new()
{
new() { Min = 0, Max = 21, Color = "#ffffff" },
new() { Min = 21, Max = 28, Color = "#ffc0cb" },
new() { Min = 28, Max = 35, Color = "#dda0dd" },
new() { Min = 35, Max = 42, Color = "#add8e6" },
new() { Min = 42, Max = 49, Color = "#00008b" },
new() { Min = 49, Max = 56, Color = "#006400" },
new() { Min = 56, Max = 63, Color = "#07ed07" },
new() { Min = 63, Max = 70, Color = "#ffff00" },
new() { Min = 70, Max = 77, Color = "#ffa500" },
new() { Min = 77, Max = 84, Color = "#ff0000" },
new() { Min = 84, Max = 100, Color = "#000000" }
};
}
private string GetColorForTemp(double temp)
{
var range = TemperatureRanges.FirstOrDefault(r => temp >= r.Min && temp < r.Max);
return range?.Color ?? "#888888";
}
private void HandleRangesChanged(List<TemperatureRange> updatedRanges)
{
TemperatureRanges = updatedRanges;
StateHasChanged();
}
}
}

View File

@ -0,0 +1,9 @@
body {
}
.temperature-blanket {
display: flex;
overflow-x: auto;
background-color: black;
padding: 10px;
}

View File

@ -0,0 +1,93 @@
@using Microsoft.AspNetCore.Components.Web
@attribute [StreamRendering]
@rendermode InteractiveServer
<div class="container" > @* @onmouseup="OnMouseUp" @onmousemove="OnMouseMove" *@
<!-- Base number line -->
@* <div class="absolute top-1/2 left-0 right-0 h-1 bg-gray-300 transform -translate-y-1/2"></div> *@
@* <!-- Draggable nodes for adjusting range breakpoints -->
@for (int i = 0; i < TempRanges.Count; i++)
{
var left = i == 0 ? 0 : TempRanges[i - 1].Max;
var leftPercent = (left / 100.0) * 100;
<div class="absolute top-1/2 transform -translate-y-1/2 -translate-x-1/2 w-4 h-4 rounded-full bg-blue-600 cursor-pointer"
style="left: @leftPercent%"
@onmousedown="(e) => OnMouseDown(i, e)"
title="@left°F">
</div>
} *@
<!-- Color pickers for each range -->
<div class="card rounded-3">
<div class="card-header mb-3 text-center fw-bold">
Temperature Blanket Colors
</div>
@for (int i = 0; i < TempRanges.Count; i++)
{
var localIndex = i;
@* if (i == 0)
{
<div class="row align-items-center mb-2">
<div class="col-6 text-end pe-2">
<label class="form-label mb-0">
@TempRanges[i].Max&deg;
</label>
</div>
<div class="col-6 ps-2">
<input type="color"
value="@TempRanges[i].Color"
@onchange="e => HandleColorChange(e, localIndex)" />
</div>
</div>
}
else if (i == 10)
{
<div class="row align-items-center mb-2">
<div class="col-6 text-end pe-2">
<label class="form-label mb-0">
@TempRanges[i].Min&deg; +
</label>
</div>
<div class="col-6 ps-2">
<input type="color"
value="@TempRanges[i].Color"
@onchange="e => HandleColorChange(e, localIndex)" />
</div>
</div>
}
else
{
<div class="row align-items-center mb-2">
<div class="col-6 text-end pe-2">
<label class="form-label mb-0">
@TempRanges[i].Min&deg; @TempRanges[i].Max&deg;
</label>
</div>
<div class="col-6 ps-2">
<input type="color"
value="@TempRanges[i].Color"
@onchange="e => HandleColorChange(e, localIndex)" />
</div>
</div>
} *@
<div class="row align-items-center mb-2 ms-1">
<div class="col-6 text-end pe-2">
<label class="form-label mb-0">
@TempRanges[i].Min&deg; @TempRanges[i].Max&deg;
</label>
</div>
<div class="col-6 ps-2">
<input type="color"
value="@TempRanges[i].Color"
@onchange="e => HandleColorChange(e, localIndex)" />
</div>
</div>
}
</div>
</div>

View File

@ -0,0 +1,88 @@
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Portfolio.Domain.Features.TemperatureRange;
namespace Portfolio.WebUI.Server.Components.Component.Crochet_Components
{
public partial class TemperatureRangeEditor : ComponentBase
{
[Parameter] public List<TemperatureRange> TempRanges { get; set; }
[Parameter] public EventCallback<List<TemperatureRange>> OnRangesChanged { get; set; }
[Inject] private IJSRuntime JS { get; set; }
private double windowWidth = 1000;
private int? draggingIndex = null;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
windowWidth = await JS.InvokeAsync<double>("eval", "window.innerWidth");
StateHasChanged();
}
}
private void OnMouseDown(int index, MouseEventArgs e)
{
draggingIndex = index;
}
private async void OnMouseUp()
{
if (draggingIndex != null)
{
draggingIndex = null;
await OnRangesChanged.InvokeAsync(TempRanges);
}
}
private void OnMouseMove(MouseEventArgs e)
{
if (draggingIndex is not int index || index <= 0 || index >= TempRanges.Count)
return;
var percent = (e.ClientX / windowWidth) * 100;
percent = Math.Clamp(percent, 0, 100);
var min = TempRanges[index - 1].Min;
var max = TempRanges[index].Max;
if (percent <= min + 1 || percent >= max - 1)
return;
TempRanges[index - 1].Max = percent;
TempRanges[index].Min = percent;
}
private void HandleColorChange(ChangeEventArgs e, int index)
{
if (index < 0 || index >= TempRanges.Count)
{
Console.WriteLine($"Color change requested for out-of-bounds index: {index}");
return;
}
var color = e?.Value?.ToString() ?? "#000000";
OnColorChanged(index, color);
}
private async void OnColorChanged(int index, string newColor)
{
//Console.WriteLine($"Color change at index {index} to {newColor}");
if (index >= 0 && index < TempRanges.Count)
{
TempRanges[index].Color = newColor;
Console.WriteLine($"Updated color: {TempRanges[index].Color}");
await OnRangesChanged.InvokeAsync(TempRanges);
}
else
{
Console.WriteLine($"Invalid index: {index} (Count: {TempRanges.Count})");
}
}
}
}

View File

@ -0,0 +1,6 @@
input[type="color"] {
width: 2.5rem;
height: 2rem;
padding: 0;
border: none;
}

View File

@ -0,0 +1,12 @@
<!--
https://codepen.io/dissimulate/pen/nmJyyg
-->
<div id="load">
<div>G</div>
<div>N</div>
<div>I</div>
<div>D</div>
<div>A</div>
<div>O</div>
<div>L</div>
</div>

View File

@ -0,0 +1,195 @@
body {
background: #000;
}
#load {
position: absolute;
width: 600px;
height: 36px;
left: 50%;
top: 40%;
margin-left: -300px;
overflow: visible;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: default;
}
#load div {
position: absolute;
width: 20px;
height: 36px;
opacity: 0;
font-family: Helvetica, Arial, sans-serif;
animation: move 3s linear infinite;
-o-animation: move 3s linear infinite;
-moz-animation: move 3s linear infinite;
-webkit-animation: move 3s linear infinite;
transform: rotate(180deg);
-o-transform: rotate(180deg);
-moz-transform: rotate(180deg);
-webkit-transform: rotate(180deg);
color: #35C4F0;
}
#load div:nth-child(2) {
animation-delay: 0.2s;
-o-animation-delay: 0.2s;
-moz-animation-delay: 0.2s;
-webkit-animation-delay: 0.2s;
}
#load div:nth-child(3) {
animation-delay: 0.4s;
-o-animation-delay: 0.4s;
-webkit-animation-delay: 0.4s;
-webkit-animation-delay: 0.4s;
}
#load div:nth-child(4) {
animation-delay: 0.6s;
-o-animation-delay: 0.6s;
-moz-animation-delay: 0.6s;
-webkit-animation-delay: 0.6s;
}
#load div:nth-child(5) {
animation-delay: 0.8s;
-o-animation-delay: 0.8s;
-moz-animation-delay: 0.8s;
-webkit-animation-delay: 0.8s;
}
#load div:nth-child(6) {
animation-delay: 1s;
-o-animation-delay: 1s;
-moz-animation-delay: 1s;
-webkit-animation-delay: 1s;
}
#load div:nth-child(7) {
animation-delay: 1.2s;
-o-animation-delay: 1.2s;
-moz-animation-delay: 1.2s;
-webkit-animation-delay: 1.2s;
}
@keyframes move {
0% {
left: 0;
opacity: 0;
}
35% {
left: 41%;
-moz-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
opacity: 1;
}
65% {
left: 59%;
-moz-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
opacity: 1;
}
100% {
left: 100%;
-moz-transform: rotate(-180deg);
-webkit-transform: rotate(-180deg);
-o-transform: rotate(-180deg);
transform: rotate(-180deg);
opacity: 0;
}
}
@-moz-keyframes move {
0% {
left: 0;
opacity: 0;
}
35% {
left: 41%;
-moz-transform: rotate(0deg);
transform: rotate(0deg);
opacity: 1;
}
65% {
left: 59%;
-moz-transform: rotate(0deg);
transform: rotate(0deg);
opacity: 1;
}
100% {
left: 100%;
-moz-transform: rotate(-180deg);
transform: rotate(-180deg);
opacity: 0;
}
}
@-webkit-keyframes move {
0% {
left: 0;
opacity: 0;
}
35% {
left: 41%;
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
opacity: 1;
}
65% {
left: 59%;
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
opacity: 1;
}
100% {
left: 100%;
-webkit-transform: rotate(-180deg);
transform: rotate(-180deg);
opacity: 0;
}
}
@-o-keyframes move {
0% {
left: 0;
opacity: 0;
}
35% {
left: 41%;
-o-transform: rotate(0deg);
transform: rotate(0deg);
opacity: 1;
}
65% {
left: 59%;
-o-transform: rotate(0deg);
transform: rotate(0deg);
opacity: 1;
}
100% {
left: 100%;
-o-transform: rotate(-180deg);
transform: rotate(-180deg);
opacity: 0;
}
}

View File

@ -0,0 +1,8 @@
<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>

View File

@ -0,0 +1,63 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
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,16 @@
.pokemon-background {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #f5f5f5; /* Optional, adjust to match your design */
pointer-events: none; /* So clicks pass through the background */
z-index: 0; /* Sits behind main content */
}
.pokemon-bg-img {
position: absolute;
object-fit: contain;
filter: drop-shadow(2px 2px 4px rgba(0,0,0,0.3)); /* Sticker-like shadow */
pointer-events: none; /* Just visual */
}

View File

@ -0,0 +1 @@
<div class="m-1 badge @_badgeitem.ToLower() border-0"><p class="statText">@_badgeitem</p></div>

View File

@ -0,0 +1,22 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
public partial class PokemonBadge
{
[Parameter]
public string BadgeItem { get; set; }
private string _badgeitem { get; set; }
protected override void OnParametersSet()
{
if (BadgeItem != null)
{
_badgeitem = BadgeItem;
}
}
}
}

View File

@ -0,0 +1,50 @@
.badge {
display: flex;
align-items: center;
justify-content: center;
width: 90px;
height: 30px;
padding: 0.25rem;
border-radius: 30px;
color: white;
font-size: clamp(0.7rem, 1vw, 0.9rem);
text-align: center;
white-space: nowrap;
}
.statText {
margin: 0;
padding: 0;
width: 100%;
text-align: center;
}
/* Sleep Type Badge Styling */
.dozing {
background-color: #fcdc5e;
}
.snoozing {
background-color: #4ce8ed;
}
.slumbering {
background-color: #4588fb;
}
/* Speciality Badge Styling */
.berries {
background-color: #24d86b;
}
.ingredients {
background-color: #fdbe4d;
}
.skills {
background-color: #47a0fc;
}
.all {
background-color: #fc7992;
}

View File

@ -0,0 +1,59 @@
@attribute [StreamRendering]
@rendermode InteractiveServer
<div class="card-wrapper d-flex flex-column align-items-center">
<div class="pokemon-card card-holo animated @GetTypeCssClass(_pokemon.PokemonType)">
<!-- 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>
<!-- Pokemon Flavortext -->
<div class="z-3 pokemon-flavor-text @(GetTypeCssClass(_pokemon.PokemonType))">
@if (string.IsNullOrEmpty(_pokemon.FlavorText))
{
<p class="fw-light">[ 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 z-2">
<div class="d-flex justify-content-between">
<PokemonBadge BadgeItem="@_pokemon.SleepType" />
<PokemonBadge BadgeItem="@_pokemon.Speciality" />
</div>
</div>
</div>
<div class="mt-3">
<PokemonEditButton PokemonId="@_pokemon.Id" />
</div>
</div>

View File

@ -0,0 +1,47 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
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";
}
private string GetTypeCssClass(string type)
{
return "pokemon-type-" + type.ToLower();
}
}
}

View File

@ -0,0 +1,346 @@
.pokemon-card {
position: relative;
width: 100%;
max-width: 350px; /* Prevent it from getting too huge */
aspect-ratio: 3 / 4; /* Maintains card shape dynamically */
overflow: hidden;
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);
}
.pokemon-card:hover {
z-index: 10;
box-shadow: 0 0 10px var(--border-color), 0 0 20px var(--border-color), 0 0 30px var(--border-color);
transition: box-shadow 0.3s ease, transform 0.2s ease;
animation: glowPulse 1.5s ease-in-out infinite;
transform: translateY(-13px);
}
.card-wrapper {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.pokemon-name {
position: absolute;
top: 5%;
left: 3%;
transform: translateY(-50%) !important;
font-size: clamp(1.2rem, 2vw, 2rem);
}
.pokemon-number {
position: absolute;
top: 10%;
left: 6%;
transform: translateY(-50%) !important;
font-size: clamp(0.7rem, 1.2vw, 0.75rem);
}
.pokemon-type {
position: absolute;
top: 2%;
right: 2%;
width: clamp(1.5rem, 2.5vw, 2.5rem);
height: clamp(1.5rem, 2.5vw, 2.5rem);
}
.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 */
border-width: 2px;
border-radius: 5% / 13%;
border-style: solid;
border-color: var(--border-color);
background-color: var(--flavor-bg-color);
}
.pokemon-flavor-text p {
margin: 0;
width: 100%;
text-align: start;
font-size: min(12.5px, 1.5vw); /* Scales font but won't exceed 12.5px */
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: 80%;
aspect-ratio: 1 / 1;
max-width: 280px;
margin: 0 auto;
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);
}
/* Type Card Styling */
.pokemon-type-grass {
--border-color: #45ca24;
--bg-color: #e5f8dc;
--flavor-bg-color: #f2fbe9;
}
.pokemon-type-fire {
--border-color: #ff662c;
--bg-color: #ffe3d5;
--flavor-bg-color: #fff0e9;
}
.pokemon-type-water {
--border-color: #2b99fe;
--bg-color: #d6ecff;
--flavor-bg-color: #eaf5ff;
}
.pokemon-type-normal {
--border-color: #ababab;
--bg-color: #ededed;
--flavor-bg-color: #f7f7f7;
}
.pokemon-type-flying {
--border-color: #9ed3ff;
--bg-color: #e7f5ff;
--flavor-bg-color: #f3fbff;
}
.pokemon-type-bug {
--border-color: #a7b023;
--bg-color: #f2f6cd;
--flavor-bg-color: #f9fbe4;
}
.pokemon-type-poison {
--border-color: #9f4ed7;
--bg-color: #edd6f8;
--flavor-bg-color: #f7ebfc;
}
.pokemon-type-electric {
--border-color: #ffdf00;
--bg-color: #fff8c6;
--flavor-bg-color: #fffbdf;
}
.pokemon-type-ground {
--border-color: #af7d38;
--bg-color: #f0ddc2;
--flavor-bg-color: #f8eee2;
}
.pokemon-type-rock {
--border-color: #bebd8d;
--bg-color: #f4f3dc;
--flavor-bg-color: #faf9ee;
}
.pokemon-type-ice {
--border-color: #45e0ff;
--bg-color: #d1f7ff;
--flavor-bg-color: #e9fbff;
}
.pokemon-type-steel {
--border-color: #6db7de;
--bg-color: #daedf7;
--flavor-bg-color: #eef6fb;
}
.pokemon-type-fighting {
--border-color: #ffa803;
--bg-color: #ffe9c6;
--flavor-bg-color: #fff3df;
}
.pokemon-type-psychic {
--border-color: #ff6887;
--bg-color: #ffd6df;
--flavor-bg-color: #ffe8ef;
}
.pokemon-type-dark {
--border-color: #544b4c;
--bg-color: #dedcdc;
--flavor-bg-color: #f1f0f0;
}
.pokemon-type-fairy {
--border-color: #ffb5ff;
--bg-color: #ffe6ff;
--flavor-bg-color: #fff2ff;
}
.pokemon-type-ghost {
--border-color: #714775;
--bg-color: #e2d2e4;
--flavor-bg-color: #f0e8f1;
}
.pokemon-type-dragon {
--border-color: #5669e2;
--bg-color: #d6dbfa;
--flavor-bg-color: #eaedfc;
}
.card-holo {
position: relative;
background-image:
url("https://assets.codepen.io/13471/sparkles.gif"),
url("https://assets.codepen.io/13471/holo.png"),
linear-gradient(125deg, #ff008450 15%, #fca40040 30%, #ffff0030 40%, #00ff8a20 60%, #00cfff40 70%, #cc4cfa50 85% );
background-blend-mode: screen;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
overflow: hidden;
border-radius: 1rem;
transition: transform 0.4s ease;
}
.card-holo:hover::before {
animation: holoGradient 12s ease 0s 1;
}
.card-holo:hover::after {
animation: holoSparkle 12s ease 0s 1;
}
@keyframes holoSparkle {
0%, 100% {
opacity: .75;
background-position: 50% 50%;
filter: brightness(1.2) contrast(1.25);
}
5%, 8% {
opacity: 1;
background-position: 40% 40%;
filter: brightness(.8) contrast(1.2);
}
13%, 16% {
opacity: .5;
background-position: 50% 50%;
filter: brightness(1.2) contrast(.8);
}
35%, 38% {
opacity: 1;
background-position: 60% 60%;
filter: brightness(1) contrast(1);
}
55% {
opacity: .33;
background-position: 45% 45%;
filter: brightness(1.2) contrast(1.25);
}
}
@keyframes holoGradient {
0%, 100% {
opacity: 0.5;
background-position: 50% 50%;
filter: brightness(.5) contrast(1);
}
5%, 9% {
background-position: 100% 100%;
opacity: 1;
filter: brightness(.75) contrast(1.25);
}
13%, 17% {
background-position: 0% 0%;
opacity: .88;
}
35%, 39% {
background-position: 100% 100%;
opacity: 1;
filter: brightness(.5) contrast(1);
}
55% {
background-position: 0% 0%;
opacity: 1;
filter: brightness(.75) contrast(1.25);
}
}
@keyframes glowPulse {
0% {
box-shadow: 0 0 15px var(--border-color);
}
50% {
box-shadow: 0 0 25px var(--border-color);
}
100% {
box-shadow: 0 0 15px var(--border-color);
}
}

View File

@ -0,0 +1,6 @@
@inject NavigationManager Navigation
<button class="btn btn-warning rounded rounded-5 text-white " @onclick="() => EditPokemon(PokemonId)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-fill" viewBox="0 0 16 16">
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.5.5 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11z" />
</svg>
</button>

View File

@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Components;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
public partial class PokemonEditButton
{
[Parameter] public int PokemonId { get; set; }
private void EditPokemon(int PokemonId)
{
Navigation.NavigateTo($"/pokemonsleep/edit/{PokemonId}");
}
}
}

View File

@ -0,0 +1,54 @@

<!-- Heading + Buttons -->
<div class="top-row row rounded-bottom-5 w-100 bg-secondary border-bottom border-start border-end border-2 border-primary py-3">
<div class="col-4"></div>
<div class="col-4 text-center">
<h1 class="text-primary">Pokémon Sleep</h1>
</div>
<div class="col-4">
<div class="btn-group d-flex justify-content-end my-1">
<!-- Home -->
<button class="btn btn-primary mx-1 align-content-center" style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/pokemonsleep">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFF" class="bi bi-house-fill mb-1" viewBox="0 0 16 16">
<path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L8 2.207l6.646 6.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293z" />
<path d="m8 3.293 6 6V13.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 13.5V9.293z" />
</svg>
</NavLink>
</button>
<!-- Pokemon Table-->
<button class="btn btn-info mx-1" style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFF" class="bi bi-table mb-1" 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>
</NavLink>
</button>
<!-- Rate Pokemon -->
<button class="btn btn-success mx-1" style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/rate-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFF" class="bi bi-award-fill mb-1" 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>
</NavLink>
</button>
<!-- Add Pokemon (Wrap in Auth) -->
<button class="btn btn-warning mx-1" style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/pokemonsleep/add-new-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFF" class="bi bi-plus-circle-fill mb-1" 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>
</NavLink>
</button>
</div>
</div>
</div>

View File

@ -0,0 +1,135 @@
<div class="pokemon-rating-panel">
<!-- Dropdown Selects -->
<div class="ratings bg-light-subtle col">
<h3 class="mb-3 row">Select Nature & Subskills</h3>
<!-- Nature -->
<div class="d-flex justify-content-between align-items-center row">
<div class="col">
<label>Select Nature</label>
<select class="form-control" @bind="SelectedNatureId">
<option value="" disabled selected >Choose Nature...</option>
@foreach (var nature in NatureList)
{
<option value="@nature.Id">@nature.Nature</option>
}
</select>
</div>
<div class="text-center col">
<h4 class="mt-4">
<span class="text-muted">@lastnaturescore</span>
</h4>
</div>
</div>
<!-- Subskill 1 -->
<div class="d-flex justify-content-between align-items-center mt-3 row">
<div class="col">
<label for="subskillSelect1">Select Level 10 Subskill</label>
<select id="subskillSelect1" class="form-control" @bind="Subskill1">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
<div class="text-center col">
<h4 class="mt-4">
<span class="text-muted">@lastS1score</span>
</h4>
</div>
</div>
<!-- Subskill 2 -->
<div class="d-flex justify-content-between align-items-center mt-3 row">
<div class="col">
<label for="subskillSelect2">Select Level 25 Subskill</label>
<select id="subskillSelect2" class="form-control" @bind="Subskill2">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
<div class="text-center col">
<h4 class="mt-4">
<span class="text-muted">@lastS2score</span>
</h4>
</div>
</div>
<!-- Subskill 3 -->
<div class="d-flex justify-content-between align-items-center mt-3 row">
<div class="col">
<label for="subskillSelect3">Select Level 50 Subskill</label>
<select id="subskillSelect3" class="form-control" @bind="Subskill3">
<option value="" selected disabled>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
<div class="text-center col">
<h4 class="mt-4">
<span class="text-muted">@lastS3score</span>
</h4>
</div>
</div>
<!-- Subskill 4 Disabled -->
<div class="d-flex justify-content-between align-items-center mt-3 row">
<div class="col">
<label for="subskillSelect4">Select Level 75 Subskill</label>
<select id="subskillSelect4" disabled class="form-control" @bind="Subskill4">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
<div class="text-center col">
<h4 class="mt-4">
<span class="text-muted">0</span>
</h4>
</div>
</div>
<!-- Subskill 5 Disabled -->
<div class="d-flex justify-content-between align-items-center mt-3 row">
<div class="col">
<label for="subskillSelect5">Select Level 100 Subskill</label>
<select id="subskillSelect5" disabled class="form-control mb-2" @bind="Subskill5">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
<div class="text-center col">
<h4 class="mt-4">
<span class="text-muted">0</span>
</h4>
</div>
</div>
<hr/>
<!-- Score Output -->
<div class="d-flex justify-content-between align-items-center mt-3 row">
<h3 class="col">Final Score:</h3>
<div class="p-2 rounded text-center col"
style="background-color:@ScoreBackgroundColor; color:@ScoreColor; ">
<h3>@FinalScore</h3>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,156 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.Pokemon;
using Portfolio.Domain.Features.Pokemon_Natures;
using Portfolio.Domain.Features.Pokemon_Subskills;
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
partial class PokemonRatingPanel
{
[Parameter] public Pokemon PokemonToRate { get; set; }
[Parameter] public List<PokemonNature> NatureList { get; set; } = new();
[Parameter] public List<PokemonSubskill> SubskillList { get; set; } = new();
private int SelectedNatureId;
private int Subskill1;
private int Subskill2;
private int Subskill3;
private int Subskill4;
private int Subskill5;
private int FinalScore;
private string ScoreBackgroundColor = "#FFFFFF";
private string ScoreColor = "#000000";
private int lastNatureId;
private int lastS1, lastS2, lastS3;
private int lastnaturescore, lastS1score, lastS2score, lastS3score;
protected override void OnAfterRender(bool firstRender)
{
// If last Nature or Subskill is different than previous
if (PokemonToRate != null) {
if (SelectedNatureId != lastNatureId)
{
lastNatureId = SelectedNatureId;
var nature = NatureList.FirstOrDefault(n => n.Id == lastNatureId);
lastnaturescore = PokemonToRate.Speciality switch
{
"Berries" => nature.BerryRating,
"Ingredients" => nature.IngredientRating,
"Skills" => nature.SkillRating,
_ => 0
};
}
if (Subskill1 != lastS1)
{
lastS1 = Subskill1;
var s1 = SubskillList.FirstOrDefault(s => s.Id == Subskill1);
lastS1score = PokemonToRate.Speciality switch
{
"Berries" => s1.BerryRank,
"Ingredients" => s1.IngredientRank,
"Skills" => s1.SkillRank,
_ => 0
};
}
if (Subskill2 != lastS2)
{
lastS2 = Subskill2;
var s2 = SubskillList.FirstOrDefault(s => s.Id == Subskill2);
lastS2score = PokemonToRate.Speciality switch
{
"Berries" => s2.BerryRank,
"Ingredients" => s2.IngredientRank,
"Skills" => s2.SkillRank,
_ => 0
};
}
if (Subskill3 != lastS3)
{
lastS3 = Subskill3;
var s3 = SubskillList.FirstOrDefault(s => s.Id == Subskill3);
lastS3score = PokemonToRate.Speciality switch
{
"Berries" => s3.BerryRank,
"Ingredients" => s3.IngredientRank,
"Skills" => s3.SkillRank,
_ => 0
};
}
}
CalculateScore();
StateHasChanged();
}
private void CalculateScore()
{
if (PokemonToRate == null || SelectedNatureId == 0 || lastS1 == 0 || lastS2 == 0 || lastS3 == 0)
{
FinalScore = 0;
ScoreBackgroundColor = "#FFFFFF";
ScoreColor = "#000000";
return;
}
var nature = NatureList.FirstOrDefault(n => n.Id == SelectedNatureId);
var s1 = SubskillList.FirstOrDefault(s => s.Id == Subskill1);
var s2 = SubskillList.FirstOrDefault(s => s.Id == Subskill2);
var s3 = SubskillList.FirstOrDefault(s => s.Id == Subskill3);
if (nature == null || s1 == null || s2 == null || s3 == null)
{
FinalScore = 0;
ScoreBackgroundColor = "#FFFFFF";
ScoreColor = "#000000";
return;
}
FinalScore = PokemonToRate.Speciality switch
{
"Berries" => nature.BerryRating + s1.BerryRank + s2.BerryRank + s3.BerryRank,
"Ingredients" => nature.IngredientRating + s1.IngredientRank + s2.IngredientRank + s3.IngredientRank,
"Skills" => nature.SkillRating + s1.SkillRank + s2.SkillRank + s3.SkillRank,
_ => 0
};
// Set score background based on value
ScoreBackgroundColor = FinalScore switch
{
8 => "#16C47F",
7 => "#33CB8F",
6 => "#50D39F",
5 => "#6DDAAF",
4 => "#8BE2BF",
3 => "#A8E9CF",
2 => "#C5F0DF",
1 => "#E2F8EF",
0 => "#FFFFFF",
-1 => "#FFE7E7",
-2 => "#FED0D0",
-3 => "#FEB8B8",
-4 => "#FDA0A0",
-5 => "#FD8888",
-6 => "#FC7171",
-7 => "#FC5959",
-8 => "#FB4141",
_ => "#FFFFFF"
};
ScoreColor = FinalScore == 0 ? "#000000" : "#FFFFFF";
}
}
}

View File

@ -0,0 +1,49 @@
.pokemon-rating-panel {
display: flex;
flex-direction: column;
gap: 1.5rem;
width: 100%;
height: 70vh;
}
.ratings {
flex: 2;
padding: 1.5rem;
border-radius: 5% / 3.5%;
}
.select-width {
width: 16rem;
}
.score-width {
width: 6rem;
}
@media (max-width: 768px) {
.pokemon-rating-panel {
padding: 1rem;
}
.ratings {
padding: 1rem;
}
.d-flex.justify-content-between.align-items-center {
flex-direction: column;
align-items: stretch !important;
gap: 0.5rem;
}
.select-width
{
width: 4rem;
text-align: center;
}
.score-width h4 {
margin-top: 0.5rem !important;
}
}

View File

@ -0,0 +1,27 @@

<!-- Search Input -->
<div class="pokemon-selector p-3 bg-light">
<input class="form-control mb-3" placeholder="Search Pokémon..." @bind="SearchTerm" @oninput="HandleSearch" />
<!-- Scrollable Pokémon Grid -->
<div class="row pokemon-grid">
@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")"
@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">
<small>@pokemon.PokemonId</small>
</div>
</div>
</div>
}
</div>
</div>
<style>
</style>

View File

@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
partial class PokemonSelector
{
[Parameter]
public List<Pokemon> PokemonList { get; set; } = new();
[Parameter]
public Pokemon? SelectedPokemon { get; set; }
[Parameter]
public EventCallback<Pokemon> OnPokemonSelected { get; set; }
private string SearchTerm { get; set; } = string.Empty;
private List<Pokemon> FilteredPokemon =>
string.IsNullOrWhiteSpace(SearchTerm)
? PokemonList
: PokemonList.Where(p =>
p.PokemonName.Contains(SearchTerm, StringComparison.OrdinalIgnoreCase)).ToList();
private async Task HandleSearch(ChangeEventArgs e)
{
SearchTerm = e?.Value?.ToString() ?? "";
}
private async Task SelectPokemon(Pokemon pokemon)
{
await OnPokemonSelected.InvokeAsync(pokemon);
}
}
}

View File

@ -0,0 +1,31 @@
.pokemon-selector {
height: 70vh;
width: 20vw;
border: 1px solid #ccc;
border-radius: 5% / 3.5%;
display: flex;
flex-direction: column;
}
.pokemon-grid {
flex: 1 1 auto;
overflow-y: auto;
}
.pokemon-card {
cursor: pointer;
transition: transform 0.15s ease-in-out;
}
.pokemon-card:hover {
transform: scale(1.05);
}
.small-card {
font-size: 0.75rem;
}

View File

@ -0,0 +1,206 @@
@inject IPokemonService PokemonService
@inject IJSRuntime JS
@inject NavigationManager Navigation
@attribute [StreamRendering]
@rendermode InteractiveServer
<!-- Table A: Desktop View-->
<div class="container d-none d-md-block " style="height: 70vh;">
<!-- Main UI -->
<div class="card shadow-sm border-0 mt-4 mx-auto col-12 col-md-10 col-lg-8 pokemontable">
<!-- Table Header -->
<div class="row card-header bg-secondary bg-gradient py-3 border-0">
<div class="flex-row justify-content-between">
<div class="text-center position-relative">
<input class="form-control position-absolute top-0 start-0 border-0 w-25" placeholder="Search Pokémon..." @bind="SearchTerm" @oninput="HandleSearch" />
<h2 class="text-info text-decoration-underline">Available Pokémon</h2>
<div class="m-1 badge bg-info position-absolute top-0 end-0 border-0"><p class="statText">@(pokemons.Count()) Pokémon</p></div>
</div>
</div>
</div>
<div class="tableFixHead d-flex flex-column justify-content-start bg-secondary table-responsive row">
<table class="table col table-borderless border-0 table-secondary table-striped align-middle">
<!-- Table Head -->
<thead class="">
<tr class="">
<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>
<th class="text-white text-bg-info col-1 text-center" scope="col">Type</th>
<th class="text-white text-bg-info col-2 text-center" scope="col">Sleep Type</th>
<th class="text-white text-bg-info col-2 text-center" scope="col">Speciality</th>
</tr>
</thead>
<!-- If/Else Pokemon Loaded-->
@if(pokemons == null)
{
<tbody>
<Loading />
</tbody>
}
else
{
<!-- Table Body -->
<tbody class="">
<tr></tr>
@if (FilteredPokemon != null && FilteredPokemon.Any())
{
@foreach (var pokemon in FilteredPokemon)
{
<tr class="flex-row">
<!-- Section: Pokemon Image -->
@{
string baseUrl = pokemon.PokemonImageUrl;
string shinyUrl = pokemon.PokemonShinyImageUrl;
}
<td class="text-center d-none d-md-table-cell">
@if (shinyUrl == null)
{
<div class="flip-container">
<div class="flipper">
<img class="front img-fluid" style="max-width: 100px;" src="@baseUrl" />
</div>
</div>
}
else
{
<div class="flip-container" @onclick="() => ToggleImage(pokemon.Id)">
<div class="flipper @(isShiny[pokemon.Id] ? "flipped" : "")">
<img class="front img-fluid" style="max-width: 100px;" src="@baseUrl" />
<img class="back img-fluid" style="max-width: 100px;" src="@shinyUrl" />
</div>
</div>
}
</td>
<!-- Section 2: Pokemon # -->
<th class="" scope="row" style="cursor: default;">@pokemon.PokemonId</th>
<!-- Section 3: Pokemon Name -->
<td @onclick="() => ViewPokemon(pokemon.PokemonId)" class="pokemon-name-style fw-light col-2">@(pokemon.IsVariation && ToggleVariationName(pokemon.Id, pokemon.PokemonId) ? $"{pokemon.VariationName} {pokemon.PokemonName}" : pokemon.PokemonName)</td>
<!-- Section 4: Pokemon Type -->
<td class="">
<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="">
<div class="d-flex justify-content-center ">
<PokemonBadge BadgeItem="@pokemon.SleepType" />
</div>
</td>
<!-- Section 6: Speciality -->
<td class="" style="">
<div class="d-flex justify-content-center">
<PokemonBadge BadgeItem="@pokemon.Speciality" />
</div>
</td>
</tr>
}
}
else
{
<tr>
<td colspan="100%">
<div class="d-flex justify-content-center align-items-center" style="height: 200px;">
<p class="text-muted">Pokémon could not be found.</p>
</div>
</td>
</tr>
}
</tbody>
}
</table>
</div>
</div>
</div>
<!-- Desktop B: Mobile View -->
<div class="container card border-0 d-block d-md-none mx-auto mt-4 shadow-sm">
<!-- Table Header -->
<div class="row card-header bg-secondary bg-gradient ml-0 py-3 border-0 bg-info">
<div class="flex-row justify-content-between">
<div class="text-center position-relative">
<h2 class="text-white text-decoration-underline">Pokémon</h2>
<div class="m-1 badge bg-white text-info position-absolute top-0 end-0 border-0 w-auto"><p class="statText">@(pokemons.Count()) Pokémon</p></div>
</div>
</div>
</div>
<!-- Table Body -->
<div class="tableFixHead row">
<table class="table table-striped border-0">
<tbody>
@if (pokemons == null)
{
<Loading />
}
else
{
@foreach (var pokemon in pokemons)
{
<tr class="border-0">
<div class="d-flex align-items-center">
<!-- Pokemon Image -->
<div class="me-3">
<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" />
@if (pokemon.PokemonShinyImageUrl != null)
{
<img class="back img-fluid" src="@pokemon.PokemonShinyImageUrl" />
}
</div>
</div>
</div>
<div class="">
<!-- Number and Name -->
<h5>
@pokemon.PokemonId -
<span class="pokemon-name-style fw-light" @onclick="() => ViewPokemon(pokemon.PokemonId)">
@(pokemon.IsVariation && ToggleVariationName(pokemon.Id, pokemon.PokemonId) ? $"{pokemon.VariationName} {pokemon.PokemonName}" : pokemon.PokemonName)
</span>
</h5>
<!-- Stats -->
<div class="d-flex flex-wrap align-items-center gap-2">
<img src="@GetTypeImageUrl(pokemon.PokemonType)" style="width:28px;" />
<PokemonBadge BadgeItem="@pokemon.SleepType" />
<PokemonBadge BadgeItem="@pokemon.Speciality" />
</div>
</div>
</div>
</tr>
}
}
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,97 @@
using Microsoft.AspNetCore.Components;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.JSInterop;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
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 void OnParametersSet()
{
if (AllPokemon != null) {
pokemons = AllPokemon.ToList();
foreach (var pokemon in pokemons)
{
isShiny[pokemon.Id] = false;
}
}
}
private string SearchTerm { get; set; } = string.Empty;
private List<Pokemon> FilteredPokemon =>
string.IsNullOrWhiteSpace(SearchTerm)
? AllPokemon
: AllPokemon.Where(p =>
p.PokemonName.Contains(SearchTerm, StringComparison.OrdinalIgnoreCase)).ToList();
private async Task HandleSearch(ChangeEventArgs e)
{
SearchTerm = e?.Value?.ToString() ?? "";
}
private void ToggleImage(int Id)
{
if (isShiny.ContainsKey(Id))
{
isShiny[Id] = !isShiny[Id];
}
}
private bool ToggleVariationName(int Id, int PokemonId)
{
foreach (var pokemon in pokemons)
{
if (pokemon.PokemonId == PokemonId && pokemon.Id != Id)
{
return true;
}
}
return false;
}
private async Task ConfirmDelete(int Id)
{
bool confirm = await JS.InvokeAsync<bool>("confirm", "Are you sure you want to delete this Pokémon?");
if (confirm)
{
await DeletePokemon(Id);
}
}
private async Task DeletePokemon(int Id)
{
await PokemonService.DeletePokemonAsync(Id);
pokemons.RemoveAll(p => p.Id == Id); // Remove from the list locally
StateHasChanged(); // Refresh the UI
}
private void EditPokemon(int id)
{
Navigation.NavigateTo($"/pokemonsleep/edit/{id}");
}
private void ViewPokemon(int id)
{
Navigation.NavigateTo($"/pokemon/{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

@ -0,0 +1,133 @@
.five-percent {
width: 5%;
}
.ten-percent {
width: 10%;
}
.pokemontable {
height: 69vh;
}
.tableFixHead {
overflow: auto;
height: 600px;
}
.tableFixHead thead th {
position: sticky;
top: 0;
z-index: 10;
}
.align-top-tbody {
vertical-align: top;
}
.pokemon-name-style {
cursor:pointer;
font-size: 1.3rem;
}
.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);
}
.flip-container-sm {
perspective: 1000px;
width: 64px;
height: 64px;
}
.flipper-sm {
transition: 0.6s;
transform-style: preserve-3d;
position: relative;
}
.flipper-sm img {
backface-visibility: hidden;
position: absolute;
width: 64px;
height: 64px;
}
.flipper-sm .front {
z-index: 2;
}
.flipper-sm .back {
transform: rotateY(180deg);
}
.flipper-sm.flipped {
transform: rotateY(180deg);
}
.fixed-row-height {
height: 90px; /* or any height that suits your card style */
}
.pokeimage {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
.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;
cursor: default;
}

View File

@ -0,0 +1,11 @@
<div class="position-absolute bottom-0 vw-100">
<footer class="d-flex flex-column align-content-center">
<hr class="border-bottom border-primary border-1 mx-5">
<p class="text-center text-primary">
Made with Love
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-through-heart" viewBox="0 0 16 16">
<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>
</p>
</footer>
</div>

View File

@ -1,17 +1,13 @@
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
<article class="content px-4">
<div class="page bg-primary-subtle h-100">
<main class="h-100 w-100">
<NavMenu3 />
<article class="container m-auto">
<div class="">
@Body
</div>
</article>
</main>
@* <MadeWithLoveFooter/> *@
</div>

View File

@ -0,0 +1,41 @@
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav">
<li class="nav-item active">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="articles">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-journal-richtext" viewBox="0 0 16 16">
<path d="M7.5 3.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m-.861 1.542 1.33.886 1.854-1.855a.25.25 0 0 1 .289-.047L11 4.75V7a.5.5 0 0 1-.5.5h-5A.5.5 0 0 1 5 7v-.5s1.54-1.274 1.639-1.208M5 9.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1-.5-.5" />
<path d="M3 0h10a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-1h1v1a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v1H1V2a2 2 0 0 1 2-2" />
<path d="M1 5v-.5a.5.5 0 0 1 1 0V5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm0 3v-.5a.5.5 0 0 1 1 0V8h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm0 3v-.5a.5.5 0 0 1 1 0v.5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z" />
</svg> Articles
</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="pokemonsleep">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-p-circle-fill" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.5 4.002V12h1.283V9.164h1.668C10.033 9.164 11 8.08 11 6.586c0-1.482-.955-2.584-2.538-2.584zm2.77 4.072c.893 0 1.419-.545 1.419-1.488s-.526-1.482-1.42-1.482H6.778v2.97z" />
</svg> Pokemon Sleep
</NavLink>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Dropdown link
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</li>
</ul>
</div>
</nav>

View File

@ -0,0 +1,35 @@
<div class="navbar navbar-expand bg-primary border-0">
<div class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-light text-decoration-none px-2">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#FFFFFF" class="bi bi-arrow-through-heart" viewBox="0 0 16 16" 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-1 text-white">Kira Jiroux</span>
</div>
<ul class="navbar-nav">
<li class="nav-item">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-house-heart-fill mb-1" viewBox="0 0 16 16">
<path d="M7.293 1.5a1 1 0 0 1 1.414 0L11 3.793V2.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v3.293l2.354 2.353a.5.5 0 0 1-.708.707L8 2.207 1.354 8.853a.5.5 0 1 1-.708-.707z" />
<path d="m14 9.293-6-6-6 6V13.5A1.5 1.5 0 0 0 3.5 15h9a1.5 1.5 0 0 0 1.5-1.5zm-6-.811c1.664-1.673 5.825 1.254 0 5.018-5.825-3.764-1.664-6.691 0-5.018" />
</svg> <span class="mx-2 mt-0">Home</span>
</NavLink>
</li>
<li>
<NavLink class="nav-link" href="temperature-blanket">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-border-outer" viewBox="0 0 16 16">
<path d="M7.5 1.906v.938h1v-.938zm0 1.875v.938h1V3.78h-1zm0 1.875v.938h1v-.938zM1.906 8.5h.938v-1h-.938zm1.875 0h.938v-1H3.78v1zm1.875 0h.938v-1h-.938zm2.813 0v-.031H8.5V7.53h-.031V7.5H7.53v.031H7.5v.938h.031V8.5zm.937 0h.938v-1h-.938zm1.875 0h.938v-1h-.938zm1.875 0h.938v-1h-.938zM7.5 9.406v.938h1v-.938zm0 1.875v.938h1v-.938zm0 1.875v.938h1v-.938z" />
<path d="M0 0v16h16V0zm1 1h14v14H1z" />
</svg><span class="mx-2 mt-0">Crochet</span>
</NavLink>
</li>
<li>
<NavLink class="nav-link" href="pokemonsleep">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-p-circle-fill" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.5 4.002V12h1.283V9.164h1.668C10.033 9.164 11 8.08 11 6.586c0-1.482-.955-2.584-2.538-2.584zm2.77 4.072c.893 0 1.419-.545 1.419-1.488s-.526-1.482-1.42-1.482H6.778v2.97z" />
</svg> <span class="mx-2 mt-0">Pokémon Sleep</span>
</NavLink>
</li>
</ul>
</div>

View File

@ -0,0 +1,68 @@
@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>
</div>
<hr>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-house-heart-fill mb-1" viewBox="0 0 16 16">
<path d="M7.293 1.5a1 1 0 0 1 1.414 0L11 3.793V2.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v3.293l2.354 2.353a.5.5 0 0 1-.708.707L8 2.207 1.354 8.853a.5.5 0 1 1-.708-.707z" />
<path d="m14 9.293-6-6-6 6V13.5A1.5 1.5 0 0 0 3.5 15h9a1.5 1.5 0 0 0 1.5-1.5zm-6-.811c1.664-1.673 5.825 1.254 0 5.018-5.825-3.764-1.664-6.691 0-5.018" />
</svg> <span class="mx-2 mt-0">Home</span>
</NavLink>
</li>
<li>
<NavLink class="nav-link" href="articles">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-journal-richtext" viewBox="0 0 16 16">
<path d="M7.5 3.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m-.861 1.542 1.33.886 1.854-1.855a.25.25 0 0 1 .289-.047L11 4.75V7a.5.5 0 0 1-.5.5h-5A.5.5 0 0 1 5 7v-.5s1.54-1.274 1.639-1.208M5 9.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1-.5-.5" />
<path d="M3 0h10a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-1h1v1a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v1H1V2a2 2 0 0 1 2-2" />
<path d="M1 5v-.5a.5.5 0 0 1 1 0V5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm0 3v-.5a.5.5 0 0 1 1 0V8h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm0 3v-.5a.5.5 0 0 1 1 0v.5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z" />
</svg> <span class="mx-2 mt-0">Articles</span>
</NavLink>
</li>
<li>
<NavLink class="nav-link" href="temperature-blanket">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-border-outer" viewBox="0 0 16 16">
<path d="M7.5 1.906v.938h1v-.938zm0 1.875v.938h1V3.78h-1zm0 1.875v.938h1v-.938zM1.906 8.5h.938v-1h-.938zm1.875 0h.938v-1H3.78v1zm1.875 0h.938v-1h-.938zm2.813 0v-.031H8.5V7.53h-.031V7.5H7.53v.031H7.5v.938h.031V8.5zm.937 0h.938v-1h-.938zm1.875 0h.938v-1h-.938zm1.875 0h.938v-1h-.938zM7.5 9.406v.938h1v-.938zm0 1.875v.938h1v-.938zm0 1.875v.938h1v-.938z" />
<path d="M0 0v16h16V0zm1 1h14v14H1z" />
</svg><span class="mx-2 mt-0">Crochet</span>
</NavLink>
</li>
<li>
<NavLink class="nav-link" href="pokemonsleep">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-p-circle-fill" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.5 4.002V12h1.283V9.164h1.668C10.033 9.164 11 8.08 11 6.586c0-1.482-.955-2.584-2.538-2.584zm2.77 4.072c.893 0 1.419-.545 1.419-1.488s-.526-1.482-1.42-1.482H6.778v2.97z" />
</svg> <span class="mx-2 mt-0">Pokémon Sleep</span>
</NavLink>
</li>
</ul>
<hr>
<div class="">
<a href="#" class="d-flex align-items-center justify-content-end link-primary text-decoration-none ">
<strong class="p-1">with love</strong>
<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">
<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>
</a>
</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

@ -0,0 +1,16 @@
@page "/temperature-blanket"
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Crochet Tools</PageTitle>
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-cosmo.css" /> <!-- app.css -->
</head>
<h3 class="text-xl font-bold mb-4">Crochet</h3>
<div class="">
<TemperatureBlanketVisualizer TemperatureDays="temperatureDays" />
</div>

View File

@ -0,0 +1,69 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.TemperatureDay;
using Portfolio.Domain.Features.TemperatureRange;
using Portfolio.WebUI.Server.Components.Component.Crochet_Components;
using static Portfolio.WebUI.Server.Components.Component.Crochet_Components.TemperatureRangeEditor;
namespace Portfolio.WebUI.Server.Components.Pages.Crochet_Pages
{
public partial class CrochetHome
{
public List<TemperatureDay> temperatureDays { get; set; }
public int YEAR = 2025;
protected override async Task OnInitializedAsync()
{
// Placeholder for loading temperature data
// Replace with actual API call
//temperatureDays = Enumerable.Range(0, 365).Select(i => new TemperatureDay
//{
// Date = new DateTime(DateTime.Now.Year - 1, 1, 1).AddDays(i),
// AvgTemp = Random.Shared.Next(10, 95)
//}).ToList();
temperatureDays = GenerateRealisticTemperatureDays(YEAR);
}
public static List<TemperatureDay> GenerateRealisticTemperatureDays(int year)
{
var temperatureDays = new List<TemperatureDay>();
var startDate = new DateTime(year, 1, 1);
int daysInYear = DateTime.IsLeapYear(year) ? 366 : 365;
// Adjusted parameters for desired range
double minAvgTemp = 20.0;
double maxAvgTemp = 84.0;
double amplitude = (maxAvgTemp - minAvgTemp) / 2.0; // 32.5
double avgAnnualTemp = (maxAvgTemp + minAvgTemp) / 2.0; // 52.5
double noiseMax = 3.0; // Small daily variation
for (int i = 0; i < daysInYear; i++)
{
var date = startDate.AddDays(i);
double dayOfYearRatio = (2 * Math.PI * i) / daysInYear;
// Peak is mid-summer, trough is mid-winter
double seasonalTemp = avgAnnualTemp + amplitude * Math.Sin(dayOfYearRatio - Math.PI / 2);
// Add gentle noise
double dailyNoise = Random.Shared.NextDouble() * noiseMax * 2 - noiseMax;
// Final temperature, clamped to min/max
double finalTemp = Math.Round(seasonalTemp + dailyNoise, 1);
finalTemp = Math.Clamp(finalTemp, minAvgTemp, maxAvgTemp);
temperatureDays.Add(new TemperatureDay
{
Date = date,
AvgTemp = finalTemp
});
}
return temperatureDays;
}
}
}

View File

@ -0,0 +1,24 @@
@* @page "/import-temperature-data"
@inject NWSWeatherService WeatherService
@inject IWebHostEnvironment Env
<h3>Import Temperature Data</h3>
@if (IsImporting)
{
<p>Importing temperatures... Please wait...</p>
}
else
{
<button class="btn btn-primary" @onclick="ImportAndSaveYearAsync">Import 2024 Data</button>
}
@if (!string.IsNullOrEmpty(Message))
{
<p>@Message</p>
}
*@

View File

@ -0,0 +1,69 @@
using Portfolio.Application.Services.NWSWeatherService;
using Portfolio.Domain.Features.TemperatureDay;
using System.Text.Json;
namespace Portfolio.WebUI.Server.Components.Pages.Crochet_Pages
{
public partial class TemperatureDataImporter
{
private readonly NWSWeatherService _weatherService;
private readonly string _storagePath = "Data/temperature_data_2024.json"; // changeable if needed
public bool IsImporting = true;
public TemperatureDataImporter(NWSWeatherService weatherService)
{
_weatherService = weatherService;
}
public async Task<List<TemperatureDay>> ImportAndSaveYearAsync(int year, double latitude, double longitude)
{
var days = new List<TemperatureDay>();
var stationId = await _weatherService.GetNearestStationAsync(latitude, longitude);
if (stationId == null)
{
throw new Exception("Failed to find nearest station.");
}
var startDate = new DateTime(year, 1, 1);
var endDate = new DateTime(year, 12, 31);
for (var date = startDate; date <= endDate; date = date.AddDays(1))
{
var avgTemp = await _weatherService.GetDailyAverageTempAsync(stationId, date);
if (avgTemp.HasValue)
{
days.Add(new TemperatureDay { Date = date, AvgTemp = avgTemp.Value });
}
await Task.Delay(600); // Respectful API use
}
await SaveToFileAsync(days);
return days;
}
private async Task SaveToFileAsync(List<TemperatureDay> days)
{
Directory.CreateDirectory(Path.GetDirectoryName(_storagePath)!);
var json = JsonSerializer.Serialize(days, new JsonSerializerOptions { WriteIndented = true });
await File.WriteAllTextAsync(_storagePath, json);
Console.WriteLine($"✅ Saved {days.Count} days to {_storagePath}");
}
public async Task<List<TemperatureDay>> LoadSavedDataAsync()
{
if (!File.Exists(_storagePath))
throw new FileNotFoundException($"No saved temperature data found at {_storagePath}");
var json = await File.ReadAllTextAsync(_storagePath);
var days = JsonSerializer.Deserialize<List<TemperatureDay>>(json);
return days ?? new List<TemperatureDay>();
}
}
}

View File

@ -2,7 +2,13 @@
<PageTitle>Home</PageTitle>
<!-- <PokemonBackground />-->
<h1 class="test">Hello, world!</h1>
<p>Ensuring that git is connected properly.</p>
Welcome to your new app.
<div class="w-75 h-100 bg-white">
</div>

View File

@ -0,0 +1,136 @@
@page "/pokemonsleep/add-new-pokemon"
@inject IPokemonService PokemonService
@inject NavigationManager Navigation
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Add New Pokémon</PageTitle>
<PokemonHeader />
@if (isSubmitting)
{
<p><em>Submitting...</em></p>
}
else
{
<div class="w-50 mt-5 m-auto create-container bg-info">
<div class="card-header bg-secondary bg-gradient ml-0 py-3">
<div class="row">
<div class="col-12 text-center">
<h2 class="text-info">Add New Pokémon</h2>
</div>
</div>
</div>
<div class="container m-lg-1" >
<EditForm class=" col mb-3" Model="NewPokemon" OnValidSubmit="HandleSubmit">
<DataAnnotationsValidator />
<!-- Section 1 -->
<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">
<!-- Pokemon Type -->
<div class="col mb-3 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 mb-3 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 mb-3 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>
<!-- Section 2 -->
<div class="row">
<div class="input-group mb-3">
<!-- Variation Check -->
<div class=" d-inline-flex">
<InputCheckbox id="IsVariation" @bind-Value="NewPokemon.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="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 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">Add Pokemon</button>
</div>
</EditForm>
</div>
</div>
}

View File

@ -0,0 +1,45 @@
using Microsoft.AspNetCore.Components.Web;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{
public partial class PokemonCreate
{
private bool HideLabel { get; set; } = true;
private void Toggle()
{
HideLabel = !HideLabel;
}
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
};
private bool isSubmitting = false;
private bool ToggleVariationName { get; set; }
private void onDisable()
{
this.ToggleVariationName = true;
}
private async Task HandleSubmit()
{
isSubmitting = true;
await PokemonService.AddPokemonAsync(NewPokemon);
isSubmitting = false;
Navigation.NavigateTo("/pokemon");
}
protected void Cancel(MouseEventArgs e)
{
Console.WriteLine("Testing in Cancel");
Navigation.NavigateTo("/pokemon");
}
}
}

View File

@ -0,0 +1,9 @@

.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;
}
.checkbox-styling {
border: 1px solid black;
}

View File

@ -0,0 +1,135 @@
@page "/pokemonsleep/edit/{id:int}"
@inject IPokemonService PokemonService
@inject NavigationManager Navigation
@inject IJSRuntime JSRuntime
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Edit Pokémon</PageTitle>
<PokemonHeader />
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
@if (pokemon == null)
{
<p><em>Loading...</em></p>
}
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>
</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>
}

View File

@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_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");
await JSRuntime.InvokeVoidAsync("history.back");
}
private async void Cancel()
{
await JSRuntime.InvokeVoidAsync("history.back");
}
private bool HideLabel { get; set; } = true;
private void Toggle()
{
HideLabel = !HideLabel;
}
}
}

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

@ -0,0 +1,66 @@
@page "/rate-pokemon"
@inject IPokemonService PokemonService
@inject IPokemonNatureService PokemonNatureService
@inject IPokemonSubskillService PokemonSubskillService
@attribute [StreamRendering]
@rendermode InteractiveServer
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
<PageTitle>Rate Pokémon</PageTitle>
<PokemonHeader />
<div class="mt-4">
<div class="row">
@if (PokemonList == null || NatureList == null || SubskillList == null)
{
<div class="d-flex justify-content-center align-items-center" style="height: 60vh;">
<Loading />
</div>
}
else
{
<!-- Left Panel: Selector -->
<div class="col-4">
<PokemonSelector
PokemonList="PokemonList"
SelectedPokemon="SelectedPokemon"
OnPokemonSelected="SelectPokemon" />
</div>
<!-- Center Panel: Pokemon View -->
<div class="col-4">
<div class="d-flex justify-content-center align-items-center h-100">
@if (SelectedPokemon != null)
{
<PokemonCard Pokemon="SelectedPokemon" />
}
else
{
<p class="text-muted">Please select a Pokémon to rate.</p>
}
</div>
</div>
<!-- Right Panel: Rating View -->
<div class="col-4">
<PokemonRatingPanel
PokemonToRate="SelectedPokemon"
NatureList="NatureList"
SubskillList="SubskillList"
/>
</div>
}
</div>
<hr class="mt-5"/>
</div>

View File

@ -0,0 +1,174 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.Pokemon;
using Portfolio.Domain.Features.Pokemon_Natures;
using Portfolio.Domain.Features.Pokemon_Subskills;
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{
public partial class PokemonRate
{
private List<Pokemon> PokemonList;
private List<PokemonNature> NatureList;
private List<PokemonSubskill> SubskillList;
private Dictionary<int, bool> isShiny = new Dictionary<int, bool>();
private int _selectedPokemonId;
private int SelectedPokemonId
{
get => _selectedPokemonId;
set
{
_selectedPokemonId = value;
OnPokemonSelected();
}
}
private int SelectedNatureId;
private int subskillSelect1;
private int subskillSelect2;
private int subskillSelect3;
private int subskillSelect4;
private int subskillSelect5;
private int[] SelectedSubskills = new int[3];
private Pokemon SelectedPokemon;
private int FinalScore;
private string ScoreBackgroundColor;
private string ScoreColor;
private string PokemonSearchTerm = string.Empty;
// Filter Pokémon based on the search input term
private List<Pokemon> FilteredPokemonList => string.IsNullOrWhiteSpace(PokemonSearchTerm)
? PokemonList
: PokemonList.Where(p => p.PokemonName.Contains(PokemonSearchTerm, StringComparison.OrdinalIgnoreCase)).ToList();
private async Task OnSearchTextChanged(ChangeEventArgs e)
{
// Trigger filtering as the user types
PokemonSearchTerm = e.Value.ToString();
}
private async Task SelectPokemon(Pokemon pokemon)
{
SelectedPokemon = pokemon;
PokemonSearchTerm = string.Empty; // Reset search term
StateHasChanged();
//FilteredPokemonList.Clear(); // Clear the filtered list to hide dropdown
}
private async void OnPokemonSelected()
{
if (SelectedPokemonId > 0)
{
SelectedPokemon = await PokemonService.GetPokemonByIdAsync(SelectedPokemonId);
StateHasChanged(); // Force UI to refresh
}
}
private void CalculateScore()
{
if (SelectedPokemon == null || SelectedNatureId == 0 || subskillSelect1 == 0 || subskillSelect2 == 0 || subskillSelect3 == 0)
{
return;
}
var nature = NatureList.FirstOrDefault(n => n.Id == SelectedNatureId);
var subskill1 = SubskillList.FirstOrDefault(s => s.Id == subskillSelect1);
var subskill2 = SubskillList.FirstOrDefault(s => s.Id == subskillSelect2);
var subskill3 = SubskillList.FirstOrDefault(s => s.Id == subskillSelect3);
if (nature == null || subskill1 == null || subskill2 == null || subskill3 == null)
{
return;
}
FinalScore = SelectedPokemon.Speciality switch
{
"Berries" => nature.BerryRating + subskill1.BerryRank + subskill2.BerryRank + subskill3.BerryRank,
"Ingredients" => nature.IngredientRating + subskill1.IngredientRank + subskill2.IngredientRank + subskill3.IngredientRank,
"Skills" => nature.SkillRating + subskill1.SkillRank + subskill2.SkillRank + subskill3.SkillRank,
_ => 0
};
ScoreBackgroundColor = FinalScore switch
{
8 => "#16C47F",
7 => "#33CB8F",
6 => "#50D39F",
5 => "#6DDAAF",
4 => "#8BE2BF",
3 => "#A8E9CF",
2 => "#C5F0DF",
1 => "#E2F8EF",
0 => "#FFFFFF",
-1 => "#FFE7E7",
-2 => "#FED0D0",
-3 => "#FEB8B8",
-4 => "#FDA0A0",
-5 => "#FD8888",
-6 => "#FC7171",
-7 => "#FC5959",
-8 => "#FB4141",
_ => "#FFFFFF"
};
ScoreColor = FinalScore switch
{
8 => "#FFFFFF",
7 => "#FFFFFF",
6 => "#FFFFFF",
5 => "#FFFFFF",
4 => "#FFFFFF",
3 => "#FFFFFF",
2 => "#FFFFFF",
1 => "#FFFFFF",
0 => "#000",
-1 => "#FFFFFF",
-2 => "#FFFFFF",
-3 => "#FFFFFF",
-4 => "#FFFFFF",
-5 => "#FFFFFF",
-6 => "#FFFFFF",
-7 => "#FFFFFF",
-8 => "#FFFFFF",
_ => "#FFFFFF"
};
}
protected override async Task OnInitializedAsync()
{
PokemonList = await PokemonService.GetAllPokemonAsync();
NatureList = await PokemonNatureService.GetAllPokemonNaturesAsync();
SubskillList = await PokemonSubskillService.GetAllPokemonSubskillsAsync();
if (PokemonList is not null)
{
PokemonList.Sort((x, y) => x.PokemonId.CompareTo(y.PokemonId));
// Initialize dictionary with false (show base image first)
foreach (var pokemon in PokemonList)
{
isShiny[pokemon.Id] = false;
}
}
}
private void ToggleImage(int Id)
{
if (isShiny.ContainsKey(Id))
{
isShiny[Id] = !isShiny[Id];
}
}
}
}

View File

@ -0,0 +1,26 @@
@page "/pokemon"
@inject IPokemonService PokemonService
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Pokémon Sleep</PageTitle>
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
<div class="w-100">
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
<PokemonHeader />
<PokemonTable AllPokemon="pokemons"/>
<hr class="mt-5" />
</div>

View File

@ -0,0 +1,30 @@
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

@ -0,0 +1,93 @@

.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

@ -0,0 +1,76 @@
@page "/pokemonsleep"
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Pokémon Sleep</PageTitle>
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
<div class="">
<div class="row w-100">
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
<div class="row"><PokemonHeader /></div>
<!-- Card -->
<div class="row 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 w-50 border-0 ">
<div>
</div>
<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="row btn-group d-flex flex-row justify-content-around mt-4">
<div class="w-auto">
<button class="btn btn-info">
<NavLink href="/pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-table mb-1" 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">Available Pokémon</span>
</NavLink>
</button>
</div>
<div class="w-auto">
<button class="btn btn-success link">
<NavLink href="/rate-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-award-fill mb-1" 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">Rate Pokémon</span>
</NavLink>
</button>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,95 @@
@page "/pokemon/{id:int}"
@inject IPokemonService PokemonService
@inject NavigationManager Navigation
@attribute [StreamRendering]
@rendermode InteractiveServer
<PokemonHeader />
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
@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 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;
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,134 +0,0 @@
@page "/pokemonsleep/add-new-pokemon"
@inject IPokemonService PokemonService
@inject NavigationManager Navigation
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Add New Pokémon</PageTitle>
<div class="card-header bg-secondary bg-gradient ml-0 py-3">
<div class="row">
<div class="col-12 text-center">
<h1 class="text-primary">Pokémon Sleep</h1>
</div>
</div>
</div>
@if (isSubmitting)
{
<p><em>Submitting...</em></p>
}
else
{
<div class="w-50 mt-5 m-auto " style="box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);">
<div class="card-header bg-secondary bg-gradient ml-0 py-3">
<div class="row">
<div class="col-12 text-center">
<h2 class="text-info">Add New Pokémon</h2>
</div>
</div>
</div>
<div class="container shadow m-lg-1" >
<EditForm class=" col mb-3" Model="NewPokemon" OnValidSubmit="HandleSubmit">
<DataAnnotationsValidator />
<!-- Section 1 -->
<div class="row m-auto">
<div class="col-sm-3 input-group input-group-sm 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" required />
</div>
</div>
<!-- Section 2 -->
<div class="row m-auto">
<div class="col-sm-3 input-group input-group-sm mb-3">
<!-- Variation Check -->
<InputCheckbox id="IsVariation" @bind-Value="NewPokemon.IsVariation" @onclick="@Toggle" class=" form-check-input" style="border: 1px solid black;" />
<span for="IsVariation" class="input-group-text">Variation?</span>
<!-- Variation Region Input -->
<InputText placeholder="What Variant? (Alolan, Paldean)" hidden="@HideLabel" id="VariationName" @bind-Value="NewPokemon.VariationName" class="form-control" />
</div>
</div>
<!-- Section 3 -->
<div class="row mb-3 m-auto" >
<label for="SleepType" class="form-label">Sleep Type</label>
<InputSelect id="SleepType" @bind-Value="NewPokemon.SleepType" class="form-select">
<option hidden value="">Dozing / Snoozing / Slumbering</option>
<option value="Dozing">Dozing</option>
<option value="Snoozing">Snoozing</option>
<option value="Slumbering">Slumbering</option>
</InputSelect>
</div>
<!-- Section 4 -->
<div class="row mb-3 m-auto">
<label for="Speciality" class="form-label">Specialty</label>
<InputSelect id="SleepType" @bind-Value="NewPokemon.Speciality" class="form-select">
<option hidden value="">Berries / Ingredients / Skills</option>
<option value="Berries">Berries</option>
<option value="Ingredients">Ingredients</option>
<option value="Skills">Skills</option>
</InputSelect>
</div>
<button type="submit" class="btn btn-primary mb-3">Add Pokémon</button>
<button type="button" class="btn btn-secondary mb-3" @onclick="@Cancel">Cancel</button>
</EditForm>
</div>
</div>
}
@code {
private bool HideLabel { get; set; } = true;
private void Toggle()
{
HideLabel = !HideLabel;
}
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
};
private bool isSubmitting = false;
private bool ToggleVariationName { get; set; }
private void onDisable()
{
this.ToggleVariationName = true;
}
private async Task HandleSubmit()
{
isSubmitting = true;
await PokemonService.AddPokemonAsync(NewPokemon);
isSubmitting = false;
Navigation.NavigateTo("/pokemonsleep");
}
protected void Cancel(MouseEventArgs e)
{
Console.WriteLine("Testing in Cancel");
Navigation.NavigateTo("/pokemonsleep");
}
}

View File

@ -1,381 +0,0 @@
@page "/pokemonsleep/rate-pokemon"
@inject IPokemonService PokemonService
@inject IPokemonNatureService PokemonNatureService
@inject IPokemonSubskillService PokemonSubskillService
@inject NavigationManager Navigation
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Rate Pokémon</PageTitle>
<style>
.flip-container {
perspective: 1000px;
display: inline-block;
width: 250px;
height: 250px;
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;
}
</style>
<div class="card-header bg-secondary bg-gradient ml-0 py-3">
<div class="row">
<div class="col-12 text-center">
<h1 class="text-primary">Pokémon Sleep</h1>
</div>
</div>
</div>
@if (PokemonList == null || NatureList == null || SubskillList == null)
{
<p>Loading...</p>
}
else
{
<div class="w-75 mt-3 m-auto " style="box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);">
<div class="card-header bg-secondary bg-gradient ml-0 py-3">
<div class="row">
<div class="col-12 text-center">
<h2 class="text-info">Pokémon Rater</h2>
</div>
</div>
</div>
<!-- Section 1: Pokemon Selection -->
<div class="card-body p-4 col-12" >
<div class="row pb-3">
<div class="col-3">
<label for="pokemonSelect">Select Pokémon</label>
<select class="form-control form-control-lg" @bind="SelectedPokemonId">
<option value="" disabled selected>Choose your Pokémon...</option>
@foreach (var pokemon in PokemonList)
{
if(pokemon.IsVariation)
{
<option value="@pokemon.Id">@pokemon.PokemonId @pokemon.VariationName @pokemon.PokemonName</option>
}
else
{
<option value="@pokemon.Id">@pokemon.PokemonId @pokemon.PokemonName</option>
}
}
</select>
</div>
</div>
<!-- Section 2: Pokemon Rating -->
@if (SelectedPokemon != null)
{
<div class="border rounded p-3 row" >
<div class="d-flex align-top col">
<!-- Pokemon Interface -->
<div class="m-1 p-1 col-5" >
<!-- Image -->
@if (SelectedPokemon.IsVariation)
{
<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>
}
else
{
<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" >
<!-- Name -->
<div class="row mb-0">
@if (SelectedPokemon.IsVariation) // If a Variant
{
<h2 class="">@SelectedPokemon.VariationName @SelectedPokemon.PokemonName</h2>
}
else // Otherwise, Base Case
{
<h3 class="">@SelectedPokemon.PokemonName</h3>
}
</div>
<!-- Pokedex Number -->
<div class="mt-0">
<p class="col-4">Pokédex #<strong>@SelectedPokemon.PokemonId</strong></p>
</div>
</div>
<!-- Pokemon Sleep Stats-->
<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>
<!-- Nature / Subskill Selection Dropdowns-->
<div class="m-1 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">
<div class="col-4">
<label for="subskillSelect1">Select Level 10 Subskill</label>
<select id="subskillSelect1" class="form-control form-control-lg 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 class="col-4">
<label for="subskillSelect2">Select Level 25 Subskill</label>
<select id="subskillSelect2" class="form-control form-control-lg 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 class="col-4">
<label for="subskillSelect3">Select Level 50 Subskill</label>
<select id="subskillSelect3" class="form-control form-control-lg 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>
</div>
<div class="d-flex justify-content-between align-items-center mt-3">
<button class="btn btn-primary" @onclick="CalculateScore">Calculate Final Score</button>
<h4>Final Score: <span>@FinalScore</span></h4>
</div>
</div>
}
</div>
</div>
}
@code {
private List<Pokemon> PokemonList;
private List<PokemonNature> NatureList;
private List<PokemonSubskill> SubskillList;
private Dictionary<int, bool> isShiny = new Dictionary<int, bool>();
private int _selectedPokemonId;
private int SelectedPokemonId
{
get => _selectedPokemonId;
set
{
_selectedPokemonId = value;
OnPokemonSelected();
}
}
private int SelectedNatureId;
private int subskillSelect1;
private int subskillSelect2;
private int subskillSelect3;
private int[] SelectedSubskills = new int[3];
private Pokemon SelectedPokemon;
private int FinalScore;
private string PokemonImageUrl => SelectedPokemon != null
? $"https://www.serebii.net/pokemonsleep/pokemon/{SelectedPokemon.Id}.png"
: string.Empty;
protected override async Task OnInitializedAsync()
{
PokemonList = await PokemonService.GetAllPokemonAsync();
NatureList = await PokemonNatureService.GetAllPokemonNaturesAsync();
SubskillList = await PokemonSubskillService.GetAllPokemonSubskillsAsync();
if (PokemonList is not null)
{
PokemonList.Sort((x, y) => x.PokemonId.CompareTo(y.PokemonId));
// Initialize dictionary with false (show base image first)
foreach (var pokemon in PokemonList)
{
isShiny[pokemon.Id] = false;
}
}
}
private async void OnPokemonSelected()
{
if (SelectedPokemonId > 0)
{
SelectedPokemon = await PokemonService.GetPokemonByIdAsync(SelectedPokemonId);
StateHasChanged(); // Force UI to refresh
}
}
private void ToggleImage(int Id)
{
if (isShiny.ContainsKey(Id))
{
isShiny[Id] = !isShiny[Id];
}
}
private void CalculateScore()
{
if (SelectedPokemon == null || SelectedNatureId == 0 || subskillSelect1 == 0 || subskillSelect2 == 0 || subskillSelect3 == 0 )
{
return;
}
var nature = NatureList.FirstOrDefault(n => n.Id == SelectedNatureId);
var subskill1 = SubskillList.FirstOrDefault(s => s.Id == subskillSelect1);
var subskill2 = SubskillList.FirstOrDefault(s => s.Id == subskillSelect2);
var subskill3 = SubskillList.FirstOrDefault(s => s.Id == subskillSelect3);
if (nature == null || subskill1 == null || subskill2 == null || subskill3 == null)
{
return;
}
FinalScore = SelectedPokemon.Speciality switch
{
"Berries" => nature.BerryRating + subskill1.BerryRank + subskill2.BerryRank + subskill3.BerryRank,
"Ingredients" => nature.IngredientRating + subskill1.IngredientRank + subskill2.IngredientRank + subskill3.IngredientRank,
"Skills" => nature.SkillRating + subskill1.SkillRank + subskill2.SkillRank + subskill3.SkillRank,
_ => 0
};
}
}

View File

@ -1,293 +0,0 @@
@page "/pokemonsleep"
@inject IPokemonService PokemonService
@inject IJSRuntime JS
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Pokémon Sleep</PageTitle>
<style>
.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;
}
</style>
<!-- Heading + Buttons -->
<div class="card-header bg-secondary bg-gradient ml-0 py-3">
<div class="row">
<div class="col-4"></div>
<div class="col-4 text-center">
<h1 class="text-primary">Pokémon Sleep</h1>
</div>
<div class="col-4 text-end">
<div class="btn-group col">
<NavLink class="btn btn-info" style="border-radius: 15px 50px;" href="/pokemonsleep/add-new-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" 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> Add New Pokémon
</NavLink>
<NavLink class="btn btn-primary" style="border-radius: 15px 50px;" href="/pokemonsleep/rate-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" 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> Rate Pokémon
</NavLink>
</div>
</div>
</div>
</div>
<!-- Main Body -->
@if (pokemons == null)
{
<p><em>Loading...</em></p>
}
else
{
<!-- Main UI -->
<div class="card shadow border-0 mt-4" style="margin: auto; width: 900px; max-width: 60%; ">
<!-- Table Header -->
<div class="card-header bg-secondary bg-gradient ml-0 py-3">
<div class="row">
<div class="col-12 text-center">
<h2 class="text-info">Available Pokémon</h2>
</div>
</div>
</div>
<!-- Table -->
<div class="tableFixHead" >
<table class="table table-striped" >
<!-- 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" style="padding-right: 30px;">Speciality</th>
<th class="text-bg-info" scope="col"></th>
</tr>
</thead>
<!-- Table Body -->
<tbody >
@foreach (var pokemon in pokemons)
{
<tr>
<!-- Section 1: Pokemon Image -->
@if (pokemon.IsVariation)
{
string BaseURL = $"/pokemon_images/normal/{pokemon.PokemonId}-{pokemon.VariationName.ToLower()}{pokemon.PokemonName.ToLower()}.png";
string ShinyURL = $"/pokemon_images/shiny/{pokemon.PokemonId}-{pokemon.VariationName.ToLower()}{pokemon.PokemonName.ToLower()}.png";
<td style="text-align: center;">
<div class="flip-container" @onclick="() => ToggleImage(pokemon.Id)">
<div class="flipper @(isShiny[pokemon.Id] ? "flipped" : "")">
<img class="front" src="/pokemon_images/normal/@(pokemon.IsVariation ? $"{pokemon.PokemonId}-{pokemon.VariationName.ToLower()}{pokemon.PokemonName.ToLower()}" : pokemon.PokemonId).png" />
<img class="back" src="/pokemon_images/shiny/@(pokemon.IsVariation ? $"{pokemon.PokemonId}-{pokemon.VariationName.ToLower()}{pokemon.PokemonName.ToLower()}" : pokemon.PokemonId).png" />
</div>
</div>
</td>
}
else // Base Case
{
string BaseURL = $"/pokemon_images/normal/{pokemon.PokemonId}.png"; ;
string ShinyURL = $"/pokemon_images/shiny/{pokemon.PokemonId}.png";
<td style="text-align: center;">
<div class="flip-container" @onclick="() => ToggleImage(pokemon.Id)">
<div class="flipper @(isShiny[pokemon.Id] ? "flipped" : "")">
<img class="front" src="/pokemon_images/normal/@(pokemon.IsVariation ? $"{pokemon.PokemonId}-{pokemon.VariationName.ToLower()}{pokemon.PokemonName.ToLower()}" : pokemon.PokemonId).png" />
<img class="back" src="/pokemon_images/shiny/@(pokemon.IsVariation ? $"{pokemon.PokemonId}-{pokemon.VariationName.ToLower()}{pokemon.PokemonName.ToLower()}" : pokemon.PokemonId).png" />
</div>
</div>
</td>
}
<!-- Section 2: Pokemon # -->
<th scope="row">@pokemon.PokemonId</th>
<!-- Section 3: Pokemon Name -->
@if (pokemon.IsVariation) // If a Variant
{
@if (pokemon.VariationName == "Alolan")
{
<td style="vertical-align: auto;"> Alolan @pokemon.PokemonName</td>
}
@if (pokemon.VariationName == "Paldean")
{
<td style="vertical-align: auto;"> Paldean @pokemon.PokemonName</td>
}
}
else // Otherwise, Base Case
{
<td style="vertical-align: auto;"> @pokemon.PokemonName</td>
}
<!-- Section 4: 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 -->
<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: Delete Button -->
<td>
<button class="btn btn-danger" @onclick="() => ConfirmDelete(pokemon.Id)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-trash" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z" />
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4zM2.5 3h11V2h-11z" />
</svg>
</button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
@code {
private List<Pokemon> pokemons = new List<Pokemon>();
private Dictionary<int, bool> isShiny = new Dictionary<int, bool>();
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));
// Initialize dictionary with false (show base image first)
foreach (var pokemon in pokemons)
{
isShiny[pokemon.Id] = false;
}
}
}
private void ToggleImage(int Id)
{
if (isShiny.ContainsKey(Id))
{
isShiny[Id] = !isShiny[Id];
}
}
private async Task ConfirmDelete(int Id)
{
bool confirm = await JS.InvokeAsync<bool>("confirm", "Are you sure you want to delete this Pokémon?");
if (confirm)
{
await DeletePokemon(Id);
}
}
private async Task DeletePokemon(int Id)
{
await PokemonService.DeletePokemonAsync(Id);
pokemons.RemoveAll(p => p.Id == Id); // Remove from the list locally
StateHasChanged(); // Refresh the UI
}
}

View File

@ -8,12 +8,16 @@
@using Microsoft.JSInterop
@using Portfolio.WebUI.Server
@using Portfolio.WebUI.Server.Components
@using Portfolio.WebUI.Server.Components.Component
@using Portfolio.WebUI.Server.Components.Component.Pokemon_Components
@using Portfolio.WebUI.Server.Components.Component.Crochet_Components
@using Portfolio.Domain.Features.Articles
@using Portfolio.Domain.Features.Pokemon
@using Portfolio.Domain.Features.Pokemon_Natures
@using Portfolio.Domain.Features.Pokemon_Subskills
@using Portfolio.Domain.Features.TemperatureDay
@using Portfolio.Application.Services.Articles
@using Portfolio.Application.Services.PokemonService
@using Portfolio.Application.Services.PokemonNatureService
@using Portfolio.Application.Services.PokemonSubskillService
@using Portfolio.Application.Services.NWSWeatherService

View File

@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazored.Typeahead" Version="4.7.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View File

@ -0,0 +1,9 @@
window.getScreenWidth = () => {
return window.innerWidth;
};
window.registerResizeCallback = (dotNetHelper) => {
window.onresize = () => {
dotNetHelper.invokeMethodAsync('UpdateScreenWidth', window.innerWidth);
};
};