Compare commits

...

19 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
52 changed files with 1749 additions and 639 deletions

View File

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

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

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

View File

@ -13,9 +13,10 @@
<HeadOutlet /> <HeadOutlet />
</head> </head>
<body> <body class="bg-primary-subtle">
<Routes /> <Routes />
<script src="_framework/blazor.web.js"></script> <script src="_framework/blazor.web.js"></script>
<script src="js/site.js"></script>
</body> </body>
</html> </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

@ -25,38 +25,38 @@ namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
private List<PokemonImage> _shinyPokemonImages = new List<PokemonImage>(); private List<PokemonImage> _shinyPokemonImages = new List<PokemonImage>();
private Random random = new Random(); private Random random = new Random();
protected override async Task OnInitializedAsync() //protected override async Task OnInitializedAsync()
{ //{
await LoadPokemonBackgrounds(); // await LoadPokemonBackgrounds();
} //}
private async Task LoadPokemonBackgrounds() //private async Task LoadPokemonBackgrounds()
{ //{
foreach (var pokemonimgurl in PokemonImages) // foreach (var pokemonimgurl in PokemonImages)
{ // {
Console.WriteLine(pokemonimgurl); // Console.WriteLine(pokemonimgurl);
_pokemonImages.Add(new PokemonImage // _pokemonImages.Add(new PokemonImage
{ // {
Url = pokemonimgurl, // URL retrieved from the database // Url = pokemonimgurl, // URL retrieved from the database
Left = random.Next(0, 100), // Left = random.Next(0, 100),
Top = random.Next(0, 100), // Top = random.Next(0, 100),
Size = random.Next(50, 130), // Size = random.Next(50, 130),
Rotation = random.Next(0, 360) // Rotation = random.Next(0, 360)
}); // });
} // }
foreach (var pokemonimgurl in ShinyPokemonImages) // foreach (var pokemonimgurl in ShinyPokemonImages)
{ // {
_shinyPokemonImages.Add(new PokemonImage // _shinyPokemonImages.Add(new PokemonImage
{ // {
Url = pokemonimgurl, // URL retrieved from the database // Url = pokemonimgurl, // URL retrieved from the database
Left = random.Next(0, 100), // Left = random.Next(0, 100),
Top = random.Next(0, 100), // Top = random.Next(0, 100),
Size = random.Next(50, 130), // Size = random.Next(50, 130),
Rotation = random.Next(0, 360) // Rotation = random.Next(0, 360)
}); // });
} // }
} //}
} }

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

@ -1,53 +1,59 @@
@attribute [StreamRendering] @attribute [StreamRendering]
@rendermode InteractiveServer @rendermode InteractiveServer
<div class="mx-2 pokemon-card card-holo animated @GetTypeCssClass(_pokemon.PokemonType)"> <div class="card-wrapper d-flex flex-column align-items-center">
<!-- Pokemon Name, Number, and Type --> <div class="pokemon-card card-holo animated @GetTypeCssClass(_pokemon.PokemonType)">
<div class="z-3"> <!-- Pokemon Name, Number, and Type -->
@if (_pokemon.IsVariation) <div class="z-3">
{ @if (_pokemon.IsVariation)
<div class="pokemon-name"><p class="fw-bold card-title">@_pokemon.VariationName @_pokemon.PokemonName</p></div> {
} <div class="pokemon-name"><p class="fw-bold card-title">@_pokemon.VariationName @_pokemon.PokemonName</p></div>
else }
{ else
<div class="pokemon-name"><p class="fw-bold card-title">@_pokemon.PokemonName</p></div> {
} <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 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> </div>
<div >
<img class="pokemon-type" src="@GetTypeImageUrl(_pokemon.PokemonType)" /> <!-- Pokemon Image -->
<div class="flip-container z-1" @onclick="() => ToggleImage()">
<div class="flipper @(isShiny ? "flipped" : "")">
<img class="pokemon-image front" src="@_pokemon.PokemonImageUrl" />
<img class="pokemon-image back" src="@_pokemon.PokemonShinyImageUrl" />
</div>
</div> </div>
<!-- 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>
<!-- Pokemon Image --> <div class="mt-3">
<div class="flip-container z-1" @onclick="() => ToggleImage()"> <PokemonEditButton PokemonId="@_pokemon.Id" />
<div class="flipper @(isShiny ? "flipped" : "")">
<img class="pokemon-image front" src="@_pokemon.PokemonImageUrl" />
<img class="pokemon-image back" src="@_pokemon.PokemonShinyImageUrl" />
</div>
</div> </div>
</div>
<!-- Pokemon Flavortext -->
<div class="z-3 pokemon-flavor-text @(GetTypeCssClass(_pokemon.PokemonType))">
@if (string.IsNullOrEmpty(_pokemon.FlavorText))
{
<p class="">[ Pokemon Flavor Text Placeholder ]</p>
}
else
{
<p class="fw-light ">@_pokemon.FlavorText</p>
}
</div>
<!-- Pokemon Sleep Type and Specialty Badges -->
<div class="position-absolute bottom-0 end-0 mb-1 me-1 z-2">
<div class="d-flex justify-content-between">
<div class="m-1 badge @_pokemon.SleepType.ToLower() border-0"><p class="statText">@_pokemon.SleepType</p></div>
<div class="m-1 badge @_pokemon.Speciality.ToLower() border-0"><p class="statText">@_pokemon.Speciality</p></div>
</div>
</div>
</div>

View File

@ -42,5 +42,6 @@ namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
return "pokemon-type-" + type.ToLower(); return "pokemon-type-" + type.ToLower();
} }
} }
} }

View File

@ -1,9 +1,9 @@
.pokemon-card { .pokemon-card {
position: relative; position: relative;
width: 24rem; width: 100%;
height: 32rem; max-width: 350px; /* Prevent it from getting too huge */
max-width: 24rem; aspect-ratio: 3 / 4; /* Maintains card shape dynamically */
max-height: 32rem; overflow: hidden;
background-color: var(--bg-color); background-color: var(--bg-color);
border-width: .5rem; border-width: .5rem;
border-style: solid; border-style: solid;
@ -20,36 +20,37 @@
transform: translateY(-13px); transform: translateY(-13px);
} }
.card-wrapper {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.pokemon-name { .pokemon-name {
position: absolute; position: absolute;
top: 0 !important; top: 5%;
left: 0 !important; left: 3%;
margin-top: 1.5rem !important;
margin-left: 0.5rem !important;
transform: translateY(-50%) !important; transform: translateY(-50%) !important;
font-size: 2.5rem; font-size: clamp(1.2rem, 2vw, 2rem);
} }
.pokemon-number { .pokemon-number {
position: absolute; position: absolute;
top: 0 !important; top: 10%;
left: 0 !important; left: 6%;
margin-top: 3.3rem !important;
margin-left: 1.5rem !important;
transform: translateY(-50%) !important; transform: translateY(-50%) !important;
font-size: .75rem; font-size: clamp(0.7rem, 1.2vw, 0.75rem);
} }
.pokemon-type { .pokemon-type {
position: absolute; position: absolute;
top: 0 !important; top: 2%;
right: 0 !important; right: 2%;
width: 2.5rem; width: clamp(1.5rem, 2.5vw, 2.5rem);
height: 2.5rem; height: clamp(1.5rem, 2.5vw, 2.5rem);
margin-top: .5rem !important;
margin-right: .5rem !important;
} }
.pokemon-image { .pokemon-image {
@ -76,7 +77,6 @@
justify-content: center; /* Horizontally centers text */ justify-content: center; /* Horizontally centers text */
overflow: hidden; /* Ensures no scrollbar */ overflow: hidden; /* Ensures no scrollbar */
border-width: 2px; border-width: 2px;
border-radius: 5% / 13%; border-radius: 5% / 13%;
border-style: solid; border-style: solid;
@ -88,7 +88,7 @@
margin: 0; margin: 0;
width: 100%; width: 100%;
text-align: start; text-align: start;
font-size: min(13px, 1.5vw); /* Scales font but won't exceed 13px */ font-size: min(12.5px, 1.5vw); /* Scales font but won't exceed 12.5px */
line-height: 1.2; /* Adjust spacing for readability */ line-height: 1.2; /* Adjust spacing for readability */
white-space: normal; /* Ensures wrapping */ white-space: normal; /* Ensures wrapping */
word-wrap: break-word; word-wrap: break-word;
@ -102,8 +102,10 @@
transform: translate(-50%, -50%) !important; transform: translate(-50%, -50%) !important;
perspective: 1000px; perspective: 1000px;
display: inline-block; display: inline-block;
width: 20rem; width: 80%;
height: 20rem; aspect-ratio: 1 / 1;
max-width: 280px;
margin: 0 auto;
cursor: pointer; cursor: pointer;
} }
@ -133,51 +135,8 @@
transform: rotateY(180deg); transform: rotateY(180deg);
} }
.badge {
width: 90px;
height: 30px;
color: white;
padding: 4px 8px;
border-radius: 30px;
}
.statText {
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: .8rem;
}
/* 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;
}
/* Type Card Styling */ /* Type Card Styling */
/* Type Themes - define vars only */
.pokemon-type-grass { .pokemon-type-grass {
--border-color: #45ca24; --border-color: #45ca24;
--bg-color: #e5f8dc; --bg-color: #e5f8dc;
@ -288,7 +247,10 @@
.card-holo { .card-holo {
position: relative; 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-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-blend-mode: screen;
background-size: cover; background-size: cover;
background-position: center; background-position: center;

View File

@ -13,7 +13,7 @@
<!-- Home --> <!-- Home -->
<button class="btn btn-primary mx-1 align-content-center" style="border-radius: 50px 15px; max-width: 60px;"> <button class="btn btn-primary mx-1 align-content-center" style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/pokemonsleep"> <NavLink href="/pokemonsleep">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-house-fill" viewBox="0 0 16 16"> <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.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" /> <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> </svg>
@ -23,7 +23,7 @@
<!-- Pokemon Table--> <!-- Pokemon Table-->
<button class="btn btn-info mx-1" style="border-radius: 50px 15px; max-width: 60px;"> <button class="btn btn-info mx-1" style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/pokemon"> <NavLink href="/pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-table" viewBox="0 0 16 16"> <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" /> <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> </svg>
</NavLink> </NavLink>
@ -31,8 +31,8 @@
<!-- Rate Pokemon --> <!-- Rate Pokemon -->
<button class="btn btn-success mx-1" style="border-radius: 50px 15px; max-width: 60px;"> <button class="btn btn-success mx-1" style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/pokemonsleep/rate-pokemon"> <NavLink href="/rate-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-award-fill" viewBox="0 0 16 16"> <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="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" /> <path d="M4 11.794V16l4-1 4 1v-4.206l-2.018.306L8 13.126 6.018 12.1z" />
</svg> </svg>
@ -42,7 +42,7 @@
<!-- Add Pokemon (Wrap in Auth) --> <!-- Add Pokemon (Wrap in Auth) -->
<button class="btn btn-warning mx-1" style="border-radius: 50px 15px; max-width: 60px;"> <button class="btn btn-warning mx-1" style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/pokemonsleep/add-new-pokemon"> <NavLink 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"> <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" /> <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> </svg>
</NavLink> </NavLink>

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

@ -5,141 +5,202 @@
@attribute [StreamRendering] @attribute [StreamRendering]
@rendermode InteractiveServer @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" />
<!-- Main UI --> <h2 class="text-info text-decoration-underline">Available Pokémon</h2>
<div class="card shadow border-0 mt-4 m-auto w-50"> <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 --> <!-- Table Header -->
<div class="card-header bg-secondary bg-gradient ml-0 py-3 border-0"> <div class="row card-header bg-secondary bg-gradient ml-0 py-3 border-0 bg-info">
<div class="row"> <div class="flex-row justify-content-between">
<div class="text-center position-relative"> <div class="text-center position-relative">
<h2 class="text-info text-decoration-underline">Available Pokémon</h2> <h2 class="text-white text-decoration-underline">Pokémon</h2>
<div class="m-1 badge bg-info position-absolute top-0 end-0 border-0"><p class="statText">@(pokemons.Count()) Pokemon</p></div> <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> </div>
</div> </div>
<!-- Table Body -->
<div class="tableFixHead row">
<table class="table table-striped border-0">
<!-- Table -->
<div class="tableFixHead"> <tbody>
<table class="table table-borderless border-0 table-secondary table-striped align-middle"> @if (pokemons == null)
<!-- Table Head -->
<thead class="">
<tr class="">
<th class="text-white text-bg-info col-2" 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">Pokemon</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>
<!-- <th class="text-bg-info text-end" scope="col">Control Buttons</th> -->
</tr>
</thead>
@if(pokemons == null)
{ {
<tbody> <Loading />
<Loading />
</tbody>
} }
else else
{ {
<!-- Table Body -->
<tbody >
@foreach (var pokemon in pokemons)
{
<tr class="">
<!-- Section: Pokemon Image --> @foreach (var pokemon in pokemons)
@{ {
string baseUrl = pokemon.PokemonImageUrl; <tr class="border-0">
string shinyUrl = pokemon.PokemonShinyImageUrl; <div class="d-flex align-items-center">
}
<td style="text-align: center;"> <!-- Pokemon Image -->
@if (shinyUrl == null) <div class="me-3">
{ <div class="flip-container-sm" @onclick="() => ToggleImage(pokemon.Id)">
<div class="flip-container"> <div class="flipper-sm @(isShiny[pokemon.Id] ? "flipped" : "")">
<div class="flipper"> <img class="front img-fluid" src="@pokemon.PokemonImageUrl" />
<img class="front" src="@baseUrl" /> @if (pokemon.PokemonShinyImageUrl != null)
</div> {
<img class="back img-fluid" src="@pokemon.PokemonShinyImageUrl" />
}
</div> </div>
}
else
{
<div class="flip-container" @onclick="() => ToggleImage(pokemon.Id)">
<div class="flipper @(isShiny[pokemon.Id] ? "flipped" : "")">
<img class="front" src="@baseUrl" />
<img class="back" src="@shinyUrl" />
</div>
</div>
}
</td>
<!-- Section 2: Pokemon # -->
<th scope="row" style="cursor: default;">@pokemon.PokemonId</th>
<!-- Section 3: Pokemon Name -->
@if (pokemon.IsVariation) // If a Variant
{
@if (pokemon.VariationName == "Alolan")
{
<td @onclick="() => ViewPokemon(pokemon.PokemonId)" class="pokemon-name-style"> Alolan @pokemon.PokemonName</td>
}
@if (pokemon.VariationName == "Paldean")
{
<td @onclick="() => ViewPokemon(pokemon.PokemonId)" class="pokemon-name-style"> Paldean @pokemon.PokemonName</td>
}
}
else // Otherwise, Base Case
{
<td @onclick="() => ViewPokemon(pokemon.PokemonId)" class="pokemon-name-style"> @pokemon.PokemonName</td>
}
<!-- Section 4: Pokemon Type -->
<td>
<div class="d-flex justify-content-center">
<img src="@GetTypeImageUrl(pokemon.PokemonType)" style="width:36px; height:36px;" />
</div> </div>
</td> </div>
<!-- Section 5: Sleep Type --> <div class="">
<td style=""> <!-- Number and Name -->
<div class="d-flex justify-content-center "> <h5>
<div class=" badge @pokemon.SleepType.ToLower() border-0"><p class="statText">@pokemon.SleepType</p></div> @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>
</td> </div>
<!-- Section 6: Speciality --> </div>
<td style=""> </tr>
<div class="d-flex justify-content-center"> }
<div class=" badge @pokemon.Speciality.ToLower() border-0"><p class="statText">@pokemon.Speciality</p></div>
</div>
</td>
<!-- Section 7: Functional Buttons -->
<!--
<td>
<div class="d-flex justify-content-end">
<PokemonEditButton PokemonId="pokemon.Id" />
<button class="btn btn-danger rounded rounded-5 mx-1" @onclick="() => ConfirmDelete(pokemon.Id)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash3-fill" viewBox="0 0 16 16">
<path d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5m-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5M4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06m6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528M8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5" />
</svg>
</button>
</div>
</td>
-->
</tr>
}
</tbody>
} }
</tbody>
</table> </table>
</div> </div>
</div> </div>

View File

@ -25,7 +25,18 @@ namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
} }
} }
} }
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) private void ToggleImage(int Id)
{ {
if (isShiny.ContainsKey(Id)) if (isShiny.ContainsKey(Id))
@ -34,6 +45,18 @@ namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
} }
} }
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) private async Task ConfirmDelete(int Id)
{ {
bool confirm = await JS.InvokeAsync<bool>("confirm", "Are you sure you want to delete this Pokémon?"); bool confirm = await JS.InvokeAsync<bool>("confirm", "Are you sure you want to delete this Pokémon?");

View File

@ -5,6 +5,10 @@
width: 10%; width: 10%;
} }
.pokemontable {
height: 69vh;
}
.tableFixHead { .tableFixHead {
overflow: auto; overflow: auto;
height: 600px; height: 600px;
@ -16,8 +20,13 @@
z-index: 10; z-index: 10;
} }
.align-top-tbody {
vertical-align: top;
}
.pokemon-name-style { .pokemon-name-style {
cursor:pointer; cursor:pointer;
font-size: 1.3rem;
} }
@ -41,6 +50,43 @@
transform: rotateY(180deg); 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 { .pokeimage {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -83,28 +129,5 @@
cursor: default; cursor: default;
} }
.dozing {
background-color: #fcdc5e;
}
.snoozing {
background-color: #4ce8ed;
}
.slumbering {
background-color: #4588fb;
}
.berries {
background-color: #24d86b;
}
.ingredients {
background-color: #fdbe4d;
}
.skills {
background-color: #47a0fc;
}

View File

@ -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,12 +1,13 @@
@inherits LayoutComponentBase @inherits LayoutComponentBase
<div class="page" > <div class="page bg-primary-subtle h-100">
<main class="h-100 w-100">
<Sidebar/> <NavMenu3 />
<article class="container m-auto">
<main class="bg-primary-subtle"> <div class="">
<article class="content px-4"> @Body
@Body </div>
</article> </article>
</main> </main>
@* <MadeWithLoveFooter/> *@
</div> </div>

View File

@ -1,45 +1,35 @@
<div class="navbar navbar-expand-lg navbar-light bg-info justify-content-md-center" id="navbarsExample08"> <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"> <ul class="navbar-nav">
<li class="nav-item"> <li class="nav-item">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All"> <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Kira Jiroux <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> </NavLink>
</li> </li>
<li class="nav-item"> <li>
<NavLink class="nav-link" href="articles"> <NavLink class="nav-link" href="temperature-blanket">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-journal-richtext" viewBox="0 0 16 16"> <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 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="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="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="M0 0v16h16V0zm1 1h14v14H1z" />
<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">Crochet</span>
</svg> Articles
</NavLink> </NavLink>
</li> </li>
<li class="nav-item"> <li>
<NavLink class="nav-link" href="pokemonsleep"> <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"> <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" /> <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 </svg> <span class="mx-2 mt-0">Pokémon Sleep</span>
</NavLink> </NavLink>
</li> </li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="dropdown08" data-bs-toggle="dropdown" aria-expanded="false">Pokemon Sleep</a>
<ul class="dropdown-menu" aria-labelledby="dropdown08">
<li>
<NavLink class="nav-link" href="pokemonsleep/add-new-pokemon">
<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>
<NavLink class="nav-link" href="pokemonsleep/rate-pokemon">
<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><a class="dropdown-item" href="#">Something else here</a></li>
</ul>
</li>
</ul> </ul>
</div> </div>

View File

@ -26,6 +26,14 @@
</svg> <span class="mx-2 mt-0">Articles</span> </svg> <span class="mx-2 mt-0">Articles</span>
</NavLink> </NavLink>
</li> </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> <li>
<NavLink class="nav-link" href="pokemonsleep"> <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"> <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">

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

@ -7,3 +7,8 @@
<p>Ensuring that git is connected properly.</p> <p>Ensuring that git is connected properly.</p>
Welcome to your new app. Welcome to your new app.
<div class="w-75 h-100 bg-white">
</div>

View File

@ -17,7 +17,7 @@
} }
else else
{ {
<div class="w-50 mt-5 m-auto create-container"> <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="card-header bg-secondary bg-gradient ml-0 py-3">
<div class="row"> <div class="row">
@ -86,6 +86,7 @@ else
<option value="Berries">Berries</option> <option value="Berries">Berries</option>
<option value="Ingredients">Ingredients</option> <option value="Ingredients">Ingredients</option>
<option value="Skills">Skills</option> <option value="Skills">Skills</option>
<option value="All">All</option>
</InputSelect> </InputSelect>
</div> </div>
</div> </div>
@ -111,11 +112,18 @@ else
<label for="ImageUrl" class="form-label">Base Image URL</label> <label for="ImageUrl" class="form-label">Base Image URL</label>
<InputText id="ImageUrl" @bind-Value="NewPokemon.PokemonImageUrl" class="form-control" /> <InputText id="ImageUrl" @bind-Value="NewPokemon.PokemonImageUrl" class="form-control" />
</div> </div>
<!-- Shiny Image URL Input -->
<div class="row mb-3 m-auto"> <div class="row mb-3 m-auto">
<label for="ImageUrl" class="form-label">Shiny Image URL</label> <label for="ImageUrl" class="form-label">Shiny Image URL</label>
<InputText id="ImageUrl" @bind-Value="NewPokemon.PokemonShinyImageUrl" class="form-control" /> <InputText id="ImageUrl" @bind-Value="NewPokemon.PokemonShinyImageUrl" class="form-control" />
</div> </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 --> <!-- Form Buttons -->
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<button type="button" class="btn btn-secondary mb-3" @onclick="Cancel">Cancel</button> <button type="button" class="btn btn-secondary mb-3" @onclick="Cancel">Cancel</button>

View File

@ -32,13 +32,13 @@ namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
isSubmitting = true; isSubmitting = true;
await PokemonService.AddPokemonAsync(NewPokemon); await PokemonService.AddPokemonAsync(NewPokemon);
isSubmitting = false; isSubmitting = false;
Navigation.NavigateTo("/pokemonsleep"); Navigation.NavigateTo("/pokemon");
} }
protected void Cancel(MouseEventArgs e) protected void Cancel(MouseEventArgs e)
{ {
Console.WriteLine("Testing in Cancel"); Console.WriteLine("Testing in Cancel");
Navigation.NavigateTo("/pokemonsleep"); Navigation.NavigateTo("/pokemon");
} }
} }

View File

@ -8,6 +8,9 @@
<PageTitle>Edit Pokémon</PageTitle> <PageTitle>Edit Pokémon</PageTitle>
<PokemonHeader /> <PokemonHeader />
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
@if (pokemon == null) @if (pokemon == null)
{ {

View File

@ -1,185 +1,66 @@
@page "/pokemonsleep/rate-pokemon" @page "/rate-pokemon"
@inject IPokemonService PokemonService @inject IPokemonService PokemonService
@inject IPokemonNatureService PokemonNatureService @inject IPokemonNatureService PokemonNatureService
@inject IPokemonSubskillService PokemonSubskillService @inject IPokemonSubskillService PokemonSubskillService
@inject NavigationManager Navigation
@attribute [StreamRendering] @attribute [StreamRendering]
@rendermode InteractiveServer @rendermode InteractiveServer
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
<PageTitle>Rate Pokémon</PageTitle> <PageTitle>Rate Pokémon</PageTitle>
<PokemonHeader /> <PokemonHeader />
@if (PokemonList == null || NatureList == null || SubskillList == null) <div class="mt-4">
{ <div class="row">
<p>Loading...</p> @if (PokemonList == null || NatureList == null || SubskillList == null)
} {
else <div class="d-flex justify-content-center align-items-center" style="height: 60vh;">
{ <Loading />
<!-- Total Component --> </div>
<div class="w-75 mt-3 m-auto rate-container bg-info "> }
else
<!-- Header --> {
<div class="card-header bg-secondary ml-0 py-3 w-100 "> <!-- Left Panel: Selector -->
<div class="row"> <div class="col-4">
<div class=" text-center"> <PokemonSelector
<h2 class="text-info">Pokémon Rater</h2> PokemonList="PokemonList"
</div> SelectedPokemon="SelectedPokemon"
OnPokemonSelected="SelectPokemon" />
</div> </div>
</div>
<!-- Body --> <!-- Center Panel: Pokemon View -->
<div class="p-3 w-100 flex-column "> <div class="col-4">
<!-- Section 1: Top - Pokemon Selection --> <div class="d-flex justify-content-center align-items-center h-100">
<div class="row flex-row justify-content-end">
<div class="pokemon-search col">
<div class="position-relative pb-3" > @if (SelectedPokemon != null)
<!-- Pokemon Selection --> {
<input type="text" <PokemonCard Pokemon="SelectedPokemon" />
class="form-control form-control" }
@bind="PokemonSearchTerm" else
placeholder="Search Pokémon..." {
@oninput="OnSearchTextChanged" <p class="text-muted">Please select a Pokémon to rate.</p>
/> }
@if (FilteredPokemonList.Any() && !string.IsNullOrWhiteSpace(PokemonSearchTerm))
{
<ul class="list-group position-absolute w-100" style="z-index: 1000; max-height: 200px; overflow-y: auto;">
@foreach (var pokemon in FilteredPokemonList)
{
<li class="list-group-item list-group-item-action" @onclick="() => SelectPokemon(pokemon)">
@if (pokemon.IsVariation)
{
@($"{pokemon.PokemonId} {pokemon.VariationName} {pokemon.PokemonName}")
}
else
{
@($"{pokemon.PokemonId} {pokemon.PokemonName}")
}
</li>
}
</ul>
}
</div>
</div>
<div class="col-2">
<button class="btn btn-danger">Clear</button>
</div> </div>
</div> </div>
<!-- Section 2: Bottom - Pokemon -->
@if (SelectedPokemon != null) <!-- Right Panel: Rating View -->
{ <div class="col-4">
<div class="row w-100"> <PokemonRatingPanel
<!-- Section 2A: Left Side - Pokemon Card View --> PokemonToRate="SelectedPokemon"
<div class="col"> NatureList="NatureList"
<div class="position-relative d-flex justify-content-center"> SubskillList="SubskillList"
<PokemonCard Pokemon="SelectedPokemon" /> />
</div> </div>
</div>
<!-- Section 2B: Right Side - Stat Selection + Form Submission --> }
<div class="col">
<h4 class="mb-3">Select Nature & Subskills</h4>
<!-- Nature -->
<div class="">
<div class="">
<label>Select Nature</label>
<select class="form-control form-control-sm mb-2" @bind="SelectedNatureId">
<option value="" disabled>Choose Nature...</option>
@foreach (var nature in NatureList)
{
<option value="@nature.Id">@nature.Nature</option>
}
</select>
</div>
</div>
<!-- Subskill 1 -->
<div class="">
<div class="">
<label for="subskillSelect1">Select Level 10 Subskill</label>
<select id="subskillSelect1" class="form-control form-control-sm mb-2" @bind="subskillSelect1">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
</div>
<!-- Subskill 2 -->
<div class="">
<div class="">
<label for="subskillSelect2">Select Level 25 Subskill</label>
<select id="subskillSelect2" class="form-control form-control-sm mb-2" @bind="subskillSelect2">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
</div>
<!-- Subskill 3 -->
<div class="">
<div class="">
<label for="subskillSelect3">Select Level 50 Subskill</label>
<select id="subskillSelect3" class="form-control form-control-sm mb-2" @bind="subskillSelect3">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
</div>
<!-- Subskill 4 Disabled -->
<div class="">
<div class="">
<label for="subskillSelect4">Select Level 75 Subskill</label>
<select id="subskillSelect4" disabled class="form-control form-control-sm mb-2" @bind="subskillSelect4">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
</div>
<!-- Subskill 5 Disabled -->
<div class="">
<div class="">
<label for="subskillSelect5">Select Level 100 Subskill</label>
<select id="subskillSelect5" disabled class="form-control form-control-sm mb-2" @bind="subskillSelect5">
<option value="" disabled selected>Choose Subskill...</option>
@foreach (var subskill in SubskillList)
{
<option value="@subskill.Id">@subskill.SubSkill</option>
}
</select>
</div>
</div>
<!-- Calculate Score -->
<div class="">
<div class="d-flex justify-content-between align-items-center mt-3">
<button class="btn btn-primary" @onclick="CalculateScore">Calculate Final Score</button>
<h4 class="bg-white border border-1 rounded p-2 m-2">
<span class="finalScore rounded" style="background-color: @ScoreBackgroundColor">@FinalScore</span>
</h4>
</div>
</div>
</div>
</div>
}
</div>
</div> </div>
} <hr class="mt-5"/>
</div>

View File

@ -58,7 +58,8 @@ namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{ {
SelectedPokemon = pokemon; SelectedPokemon = pokemon;
PokemonSearchTerm = string.Empty; // Reset search term PokemonSearchTerm = string.Empty; // Reset search term
FilteredPokemonList.Clear(); // Clear the filtered list to hide dropdown StateHasChanged();
//FilteredPokemonList.Clear(); // Clear the filtered list to hide dropdown
} }
private async void OnPokemonSelected() private async void OnPokemonSelected()

View File

@ -1,92 +1 @@
 
.rate-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;
}
.pokemon-search {
width: 95%;
}
.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: 90px;
height: 30px;
color: white;
padding: 4px 8px;
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;
}
.finalScore {
color: white;
padding: 4px 8px;
text-align: center;
vertical-align: middle;
border-radius: 3px;
}

View File

@ -9,6 +9,10 @@
<PageTitle>Pokémon Sleep</PageTitle> <PageTitle>Pokémon Sleep</PageTitle>
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
<div class="w-100"> <div class="w-100">
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> --> <!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
@ -16,4 +20,7 @@
<PokemonTable AllPokemon="pokemons"/> <PokemonTable AllPokemon="pokemons"/>
<hr class="mt-5" />
</div> </div>

View File

@ -6,33 +6,71 @@
<PageTitle>Pokémon Sleep</PageTitle> <PageTitle>Pokémon Sleep</PageTitle>
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
<div class="w-100"> <div class="">
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> --> <div class="row w-100">
<PokemonHeader /> <!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
<div class="card shadow border-0 p-3 mt-4 m-auto w-75"> <div class="row"><PokemonHeader /></div>
<div class="d-flex flex-row justify-content-around">
<div class="card border-0 shadow-sm"><img class="card-img shadow-sm w-auto" style="width: 540px; height: 540px;" src="images/Pokemon_Sleep_ID.jpg" /></div> <!-- Card -->
<div class="border border-warning w-50 p-3"> <div class="row card shadow-sm border-0 p-3 pb-4 mt-4 m-auto w-75 mb-5 bg-white">
<p class="fw-bold fst-italic fs-3">Pokémon Sleep? Really?</p> <!-- Top Row-->
<p class="fw-normal fs-6 text-start"> <div class="row d-flex flex-row">
Yes, really! Pokémon Sleep has become a fun addition to my day since it's release. <!-- Image side -->
</p> <div class="col w-50 border-0 ">
<p class="fw-bold fst-italic fs-3">But what do you even do?</p> <div>
<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. </div>
</p> <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-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"> <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. 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> </p>
</div> </div>
</div> <!-- Button Row-->
<div class="btn-group d-flex flex-row justify-content-around mt-4"> <div class="row btn-group d-flex flex-row justify-content-around mt-4">
<button class="btn btn-info">Available Pokémon</button> <div class="w-auto">
<button class="btn btn-success">Rate Pokémon</button> <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> </div>
</div>
</div>

View File

@ -8,6 +8,10 @@
<PokemonHeader /> <PokemonHeader />
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
@if (_pokemon == null) @if (_pokemon == null)
{ {
<Loading /> <Loading />
@ -17,14 +21,20 @@ else
<PageTitle>@_pokemon.PokemonName</PageTitle> <PageTitle>@_pokemon.PokemonName</PageTitle>
<!-- Total Componenet--> <!-- Total Componenet-->
<div class="w-100"> <div class="container">
<div class="d-flex justify-content-center mt-4"> <div class="w-75 row mt-4 m-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"> <!-- Previous Pokemon Button -->
<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" /> <div class="col-auto">
</svg> <button class="mt-1 p-2 btn btn-danger rounded-5 align-self-start text-white" disabled="@(!_previousPokemonId.HasValue)" @onclick="NavigateToPrevious">
</button> <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">
<div class="mt-5 mx-5 d-flex justify-content-center"> <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)
{ {
@if (_variationPokemonId != null && _pokemonVariant == null){ @if (_variationPokemonId != null && _pokemonVariant == null){
@ -32,50 +42,54 @@ else
} }
else else
{ {
<!-- If Variation Pokemon have same PokemonId -->
@if(_pokemon.Id != _pokemonVariant.Id) @if(_pokemon.Id != _pokemonVariant.Id)
{ {
<div class="d-flex justify-content-center"> <div class="d-flex justify-content-center">
<div class="position-relative"> <div class="pokemoncard m-1">
<PokemonCard Pokemon="_pokemon" /> <PokemonCard Pokemon="_pokemon" />
<div class="position-absolute top-100 start-50 translate-middle mt-5">
<PokemonEditButton PokemonId="_pokemon.Id" />
</div> </div>
</div>
<div class="position-relative"> <div class="pokemoncard m-1">
<PokemonCard Pokemon="_pokemonVariant" /> <PokemonCard Pokemon="_pokemonVariant" />
<div class="position-absolute top-100 start-50 translate-middle mt-5">
<PokemonEditButton PokemonId="_pokemonVariant.Id" />
</div> </div>
</div>
</div> </div>
} }
<!-- If Variation Pokemon has diff PokemonId -->
else else
{ {
<div class="position-relative"> <div class="d-flex justify-content-center align-items-center h-100">
<PokemonCard Pokemon="_pokemonVariant" /> <div class="pokemoncard">
<div class="position-absolute top-100 start-50 translate-middle mt-5"> <PokemonCard Pokemon="_pokemonVariant" />
<PokemonEditButton PokemonId="_pokemonVariant.Id" />
</div> </div>
</div> </div>
} }
} }
} }
else{ else{
<div class="position-relative"> <!-- Standard Pokemon Display -->
<PokemonCard Pokemon="_pokemon" /> <div class="d-flex justify-content-center align-items-center h-100">
<div class="position-absolute top-100 start-50 translate-middle mt-5"> <div class="pokemoncard m-1">
<PokemonEditButton PokemonId="_pokemon.Id" /> <PokemonCard Pokemon="_pokemon" />
</div> </div>
</div> </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"> <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"> <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" /> <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> </svg>
</button> </button>
</div>
</div> </div>
</div> </div>
} }

View File

@ -1,2 +1,9 @@
body { .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

@ -10,11 +10,14 @@
@using Portfolio.WebUI.Server.Components @using Portfolio.WebUI.Server.Components
@using Portfolio.WebUI.Server.Components.Component @using Portfolio.WebUI.Server.Components.Component
@using Portfolio.WebUI.Server.Components.Component.Pokemon_Components @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.Articles
@using Portfolio.Domain.Features.Pokemon @using Portfolio.Domain.Features.Pokemon
@using Portfolio.Domain.Features.Pokemon_Natures @using Portfolio.Domain.Features.Pokemon_Natures
@using Portfolio.Domain.Features.Pokemon_Subskills @using Portfolio.Domain.Features.Pokemon_Subskills
@using Portfolio.Domain.Features.TemperatureDay
@using Portfolio.Application.Services.Articles @using Portfolio.Application.Services.Articles
@using Portfolio.Application.Services.PokemonService @using Portfolio.Application.Services.PokemonService
@using Portfolio.Application.Services.PokemonNatureService @using Portfolio.Application.Services.PokemonNatureService
@using Portfolio.Application.Services.PokemonSubskillService @using Portfolio.Application.Services.PokemonSubskillService
@using Portfolio.Application.Services.NWSWeatherService

View File

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