Compare commits

...

16 Commits

Author SHA1 Message Date
Kira Jiroux b8bc2fdfbe Reconfigured home page; actually made to include real portfolio shit. Maybe someday I can actually launch this but who knows :) 2025-06-18 14:02:38 -04:00
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
50 changed files with 1703 additions and 519 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

@ -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

@ -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

@ -1,21 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap-brite.css" /> <!-- app.css -->
<link rel="stylesheet" href="Portfolio.WebUI.Server.styles.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>
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
</head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
</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

@ -25,38 +25,38 @@ namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
private List<PokemonImage> _shinyPokemonImages = new List<PokemonImage>();
private Random random = new Random();
protected override async Task OnInitializedAsync()
{
await LoadPokemonBackgrounds();
}
//protected override async Task OnInitializedAsync()
//{
// await LoadPokemonBackgrounds();
//}
private async Task 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)
});
}
}
// 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

@ -1,17 +1,22 @@
.badge {
display: flex;
align-items: center;
justify-content: center;
width: 90px;
height: 30px;
color: white;
padding: 4px 8px;
padding: 0.25rem;
border-radius: 30px;
color: white;
font-size: clamp(0.7rem, 1vw, 0.9rem);
text-align: center;
white-space: nowrap;
}
.statText {
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: .8rem;
margin: 0;
padding: 0;
width: 100%;
text-align: center;
}
/* Sleep Type Badge Styling */

View File

@ -1,53 +1,59 @@
@attribute [StreamRendering]
@rendermode InteractiveServer
<div class="mx-2 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 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>
<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>
<!-- 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>
<!-- 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 class="mt-3">
<PokemonEditButton PokemonId="@_pokemon.Id" />
</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">
<PokemonBadge BadgeItem="@_pokemon.SleepType" />
<PokemonBadge BadgeItem="@_pokemon.Speciality" />
</div>
</div>
</div>
</div>

View File

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

View File

@ -1,9 +1,9 @@
.pokemon-card {
position: relative;
width: 24rem;
height: 32rem;
max-width: 24rem;
max-height: 32rem;
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;
@ -20,36 +20,37 @@
transform: translateY(-13px);
}
.card-wrapper {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.pokemon-name {
position: absolute;
top: 0 !important;
left: 0 !important;
margin-top: 1.5rem !important;
margin-left: 0.5rem !important;
top: 5%;
left: 3%;
transform: translateY(-50%) !important;
font-size: 2.5rem;
font-size: clamp(1.2rem, 2vw, 2rem);
}
.pokemon-number {
position: absolute;
top: 0 !important;
left: 0 !important;
margin-top: 3.3rem !important;
margin-left: 1.5rem !important;
top: 10%;
left: 6%;
transform: translateY(-50%) !important;
font-size: .75rem;
font-size: clamp(0.7rem, 1.2vw, 0.75rem);
}
.pokemon-type {
position: absolute;
top: 0 !important;
right: 0 !important;
width: 2.5rem;
height: 2.5rem;
margin-top: .5rem !important;
margin-right: .5rem !important;
top: 2%;
right: 2%;
width: clamp(1.5rem, 2.5vw, 2.5rem);
height: clamp(1.5rem, 2.5vw, 2.5rem);
}
.pokemon-image {
@ -76,7 +77,6 @@
justify-content: center; /* Horizontally centers text */
overflow: hidden; /* Ensures no scrollbar */
border-width: 2px;
border-radius: 5% / 13%;
border-style: solid;
@ -88,7 +88,7 @@
margin: 0;
width: 100%;
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 */
white-space: normal; /* Ensures wrapping */
word-wrap: break-word;
@ -102,8 +102,10 @@
transform: translate(-50%, -50%) !important;
perspective: 1000px;
display: inline-block;
width: 20rem;
height: 20rem;
width: 80%;
aspect-ratio: 1 / 1;
max-width: 280px;
margin: 0 auto;
cursor: pointer;
}
@ -245,7 +247,10 @@
.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-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;

View File

@ -13,7 +13,7 @@
<!-- 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="currentColor" class="bi bi-house-fill mb-1" 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 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>
@ -23,7 +23,7 @@
<!-- 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="currentColor" class="bi bi-table mb-1" 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" />
</svg>
</NavLink>
@ -32,7 +32,7 @@
<!-- 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="currentColor" class="bi bi-award-fill mb-1" 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="M4 11.794V16l4-1 4 1v-4.206l-2.018.306L8 13.126 6.018 12.1z" />
</svg>
@ -42,7 +42,7 @@
<!-- 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="currentColor" class="bi bi-plus-circle-fill mb-1" 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" />
</svg>
</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

@ -6,28 +6,30 @@
@rendermode InteractiveServer
<!-- Table A: Desktop View-->
<div class="container d-none d-md-block">
<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">
<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 ml-0 py-3 border-0">
<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()) Pokemon</p></div>
<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 table-responsive row">
<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">Pokemon</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>
@ -44,69 +46,84 @@
else
{
<!-- Table Body -->
<tbody>
@foreach (var pokemon in pokemons)
<tbody class="">
<tr></tr>
@if (FilteredPokemon != null && FilteredPokemon.Any())
{
<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>
@foreach (var pokemon in FilteredPokemon)
{
<tr class="flex-row">
<!-- Section: Pokemon Image -->
@{
string baseUrl = pokemon.PokemonImageUrl;
string shinyUrl = pokemon.PokemonShinyImageUrl;
}
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" />
<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>
</div>
}
</td>
}
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>
<!-- Section 2: Pokemon # -->
<th class="" scope="row" style="cursor: default;">@pokemon.PokemonId</th>
}
</td>
<!-- Section 2: Pokemon # -->
<th class="" scope="row" style="cursor: default;">@pokemon.PokemonId</th>
<!-- Section 3: Pokemon Name -->
<!-- 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>
<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;" />
<!-- 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>
<!-- 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>
}
</tbody>
@ -128,7 +145,7 @@
<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())</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>
@ -163,7 +180,7 @@
</div>
</div>
<div>
<div class="">
<!-- Number and Name -->
<h5>
@pokemon.PokemonId -
@ -174,8 +191,8 @@
<!-- Stats -->
<div class="d-flex flex-wrap align-items-center gap-2">
<img src="@GetTypeImageUrl(pokemon.PokemonType)" style="width:28px;" />
<div class=" badge @pokemon.SleepType.ToLower() border-0"><p class="statText">@pokemon.SleepType</p></div>
<div class=" badge @pokemon.Speciality.ToLower() border-0"><p class="statText">@pokemon.Speciality</p></div>
<PokemonBadge BadgeItem="@pokemon.SleepType" />
<PokemonBadge BadgeItem="@pokemon.Speciality" />
</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)
{
if (isShiny.ContainsKey(Id))

View File

@ -5,6 +5,10 @@
width: 10%;
}
.pokemontable {
height: 69vh;
}
.tableFixHead {
overflow: auto;
height: 600px;
@ -16,6 +20,10 @@
z-index: 10;
}
.align-top-tbody {
vertical-align: top;
}
.pokemon-name-style {
cursor:pointer;
font-size: 1.3rem;
@ -74,7 +82,9 @@
transform: rotateY(180deg);
}
.fixed-row-height {
height: 90px; /* or any height that suits your card style */
}
.pokeimage {

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
<div class="page bg-primary-subtle ">
<Sidebar/>
<main class="border border-3 border-dark w-100">
<article class="container border border-3 border-black px-4 w-100 m-auto">
@Body
<div class="page">
<main class="">
<NavMenu3 />
<article class="container ">
<div class="">
@Body
</div>
</article>
</main>
@* <MadeWithLoveFooter/> *@
</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">
<li class="nav-item">
<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>
</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
<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 class="nav-item">
<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> Pokemon Sleep
</svg> <span class="mx-2 mt-0">Pokémon Sleep</span>
</NavLink>
</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>
</div>

View File

@ -26,6 +26,14 @@
</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">

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,8 +2,217 @@
<PageTitle>Home</PageTitle>
<!-- <PokemonBackground />-->
<h1 class="test">Hello, world!</h1>
<p>Ensuring that git is connected properly.</p>
<div class="container d-none d-md-block mt-5 pt-3 pb-5 ps-5 pe-5 ">
<h1 class="fst-italic fw-light fs-1 font-monospace">Hello, World!</h1>
<div class="row">
<div class="col-auto">
<div class="card" style="width: 18em;">
<img class="card-img" src="https://i.pinimg.com/736x/54/63/ed/5463ed9471053712a76c0dd0b301a7ea.jpg" />
</div>
</div>
<div class="col">
<div class="row card fs-4 mb-3 p-2 ps-3 pe-3 border-0">
I am a full-stack web developer with additional
experience in Data Analysis and Visualization, as well as
Simulation/ VR development, and AI/ ML programming.
I am a friendly team worker with strong analytical, math
and problem-solving skills that adapts well to an
Agile/SCRUM environment and development lifecycle.
</div>
<div class="row ">
<div class="col">
<div class="card mb-3 p-2 ps-3 pe-3 border border-1 border-primary">
<p class="fs-6 fw-lighter card-subtitle m-0">September 2018 March 2022</p>
<p class="fs-4 card-title m-0">B.S. in Computer Science</p>
<p class="fs-6 fw-lighter card-subtitle">Oregon State University</p>
<p class="fs-6 fw-lighter card-subtitle">Graduated Cum Laude</p>
<p class="fs-6 fw-lighter card-subtitle">Deans List: Fall 2020, Fall 2021</p>
<p class="fs-6 fw-lighter card-subtitle">Final GPA: 3.33</p>
@* <label>Courses</label>
<ul class="list-group list-group-flush">
<li class="list-group-item fs-6 fw-lighter">Advanced Web Development</li>
<li class="list-group-item fs-6 fw-lighter">Operating Systems</li>
<li class="list-group-item fs-6 fw-lighter">Game Development</li>
<li class="list-group-item fs-6 fw-lighter">Parallel Programming</li>
<li class="list-group-item fs-6 fw-lighter">VR \ AR Development</li>
<li class="list-group-item fs-6 fw-lighter">Artificial Intelligence</li>
<li class="list-group-item fs-6 fw-lighter">Machine Learning</li>
<li class="list-group-item fs-6 fw-lighter">Computer Graphics</li>
<li class="list-group-item fs-6 fw-lighter">Data Visualization</li>
<li class="list-group-item fs-6 fw-lighter">Database Management</li>
<li class="list-group-item fs-6 fw-lighter">Analysis of Algorithms</li>
<li class="list-group-item fs-6 fw-lighter">VR \ AR Development</li>
</ul> *@
</div>
<div class="ps-3 pe-3">
<ul class="list-group list-group-flush">
<li class="">Organized</li>
<li class="">Responsible</li>
<li class="">Meticulous</li>
<li class="">Analytical</li>
<li class="">Self-motivating</li>
</ul>
</div>
</div>
<div class="col ">
<div class="bg-white rounded p-2 border border-1 border-primary">
<div class="card-header fs-3">
Skills \ Languages
</div>
<div class="overflow-auto" style="max-height: 15rem">
<ul class="list-group list-group-flush">
<li class="list-group-item">C# / C++ / C</li>
<li class="list-group-item">ASP.NET / .NET Core</li>
<li class="list-group-item">Blazor WASM</li>
<li class="list-group-item">React</li>
<li class="list-group-item">Java</li>
<li class="list-group-item">JavaScript</li>
<li class="list-group-item">HTML / CSS</li>
<li class="list-group-item">SQL / MYSQL / MSSQL</li>
<li class="list-group-item">JSON / XML</li>
<li class="list-group-item">Python</li>
<li class="list-group-item">Swift</li>
<li class="list-group-item">TypeScript</li>
<li class="list-group-item">MATLAB</li>
<li class="list-group-item">R</li>
<li class="list-group-item">LaTeX</li>
</ul>
</div>
</div>
</div>
<div class="col ">
<div class="bg-white rounded p-2 border border-1 border-primary">
<div class="card-header fs-3">
Technology
</div>
<div class="overflow-auto" style="max-height: 15rem">
<ul class="list-group list-group-flush">
<li class="list-group-item">MS Visual Studio 2022</li>
<li class="list-group-item">Git / GitHub / GitLab</li>
<li class="list-group-item">RESTful API</li>
<li class="list-group-item">Docker</li>
<li class="list-group-item">Terraform</li>
<li class="list-group-item">Jira</li>
<li class="list-group-item">Linux / UNIX</li>
<li class="list-group-item">SQL / MYSQL / MSSQL</li>
<li class="list-group-item">Unreal Engine</li>
<li class="list-group-item">Unity</li>
<li class="list-group-item">Blendr</li>
<li class="list-group-item">Windows, Android & iOS</li>
<li class="list-group-item">Gephi</li>
<li class="list-group-item">Excel / Microsoft Office Suite</li>
<li class="list-group-item">Figma</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Desktop B: Mobile View -->
<div class="container card border-0 d-block d-md-none mx-auto mt-4 shadow-sm pb-2">
<h1 class="fst-italic fw-light fs-1 font-monospace">Hello, World!</h1>
<div class="row">
<div class="col-auto">
<div class="card" style="width: 10em;">
<img class="card-img" src="https://i.pinimg.com/736x/54/63/ed/5463ed9471053712a76c0dd0b301a7ea.jpg" />
</div>
</div>
<div class="col">
<div class="card fs-6 mb-3 p-2 ps-1 pe-1 border-0">
I am a full-stack web developer with additional
experience in Data Analysis and Visualization, as well as
Simulation/ VR development, and AI/ ML programming.
I am a friendly team worker with strong analytical, math
and problem-solving skills that adapts well to an
Agile/SCRUM environment and development lifecycle.
</div>
</div>
</div>
<div class="row mb-3 bg-info rounded rounded-5 pt-1 pb-1">
<p class="fw-lighter fs-6 text-nowrap p-0 m-auto text-center text-white">
Organized • Responsible • Meticulous • Analytical • Self-motivating
</p>
</div>
<hr class="text-primary" />
<div class="row mb-3 ms-2 me-2">
<div class="card p-2 ps-3 pe-3 border border-1 border-primary">
<p class="fs-6 fw-lighter card-subtitle m-0">September 2018 March 2022</p>
<p class="fs-4 card-title m-0">B.S. in Computer Science</p>
<p class="fs-6 fw-lighter card-subtitle">Oregon State University</p>
<p class="fs-6 fw-lighter card-subtitle">Graduated Cum Laude</p>
<p class="fs-6 fw-lighter card-subtitle">Deans List: Fall 2020, Fall 2021</p>
<p class="fs-6 fw-lighter card-subtitle">Final GPA: 3.33</p>
</div>
</div>
<div class="row mb-3 ms-2 me-2">
<div class="bg-white rounded p-2 border border-1 border-primary">
<div class="card-header fs-3">
Skills \ Languages
</div>
<div class="overflow-auto" style="max-height: 15rem">
<ul class="list-group list-group-flush">
<li class="list-group-item">C# / C++ / C</li>
<li class="list-group-item">ASP.NET / .NET Core</li>
<li class="list-group-item">Blazor WASM</li>
<li class="list-group-item">React</li>
<li class="list-group-item">Java</li>
<li class="list-group-item">JavaScript</li>
<li class="list-group-item">HTML / CSS</li>
<li class="list-group-item">SQL / MYSQL / MSSQL</li>
<li class="list-group-item">JSON / XML</li>
<li class="list-group-item">Python</li>
<li class="list-group-item">Swift</li>
<li class="list-group-item">TypeScript</li>
<li class="list-group-item">MATLAB</li>
<li class="list-group-item">R</li>
<li class="list-group-item">LaTeX</li>
</ul>
</div>
</div>
</div>
<div class="row mb-3 ms-2 me-2">
<div class="bg-white rounded p-2 border border-1 border-primary">
<div class="card-header fs-3">
Technology
</div>
<div class="overflow-auto" style="max-height: 15rem">
<ul class="list-group list-group-flush">
<li class="list-group-item">MS Visual Studio 2022</li>
<li class="list-group-item">Git / GitHub / GitLab</li>
<li class="list-group-item">RESTful API</li>
<li class="list-group-item">Docker</li>
<li class="list-group-item">Terraform</li>
<li class="list-group-item">Jira</li>
<li class="list-group-item">Linux / UNIX</li>
<li class="list-group-item">SQL / MYSQL / MSSQL</li>
<li class="list-group-item">Unreal Engine</li>
<li class="list-group-item">Unity</li>
<li class="list-group-item">Blendr</li>
<li class="list-group-item">Windows, Android & iOS</li>
<li class="list-group-item">Gephi</li>
<li class="list-group-item">Excel / Microsoft Office Suite</li>
<li class="list-group-item">Figma</li>
</ul>
</div>
</div>
</div>
</div>
Welcome to your new app.

View File

@ -112,11 +112,18 @@ else
<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>

View File

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

View File

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

View File

@ -3,183 +3,64 @@
@inject IPokemonService PokemonService
@inject IPokemonNatureService PokemonNatureService
@inject IPokemonSubskillService PokemonSubskillService
@inject NavigationManager Navigation
@attribute [StreamRendering]
@rendermode InteractiveServer
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
<PageTitle>Rate Pokémon</PageTitle>
<PokemonHeader />
@if (PokemonList == null || NatureList == null || SubskillList == null)
{
<p>Loading...</p>
}
else
{
<!-- Total Component -->
<div class="w-75 mt-3 m-auto rate-container bg-info ">
<!-- Header -->
<div class="card-header bg-secondary ml-0 py-3 w-100 ">
<div class="row">
<div class=" text-center">
<h2 class="text-info">Pokémon Rater</h2>
</div>
<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>
</div>
<!-- Body -->
<div class="p-3 w-100 flex-column ">
<!-- Section 1: Top - Pokemon Selection -->
<div class="row flex-row justify-content-end">
<div class="pokemon-search col">
<div class="position-relative pb-3" >
<!-- Pokemon Selection -->
<input type="text"
class="form-control form-control"
@bind="PokemonSearchTerm"
placeholder="Search Pokémon..."
@oninput="OnSearchTextChanged"
/>
@if (FilteredPokemonList.Any() && !string.IsNullOrWhiteSpace(PokemonSearchTerm))
{
<ul class="list-group position-absolute w-100" style="z-index: 1000; max-height: 200px; overflow-y: auto;">
@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>
<!-- 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>
<!-- Section 2: Bottom - Pokemon -->
@if (SelectedPokemon != null)
{
<div class="row w-100">
<!-- Section 2A: Left Side - Pokemon Card View -->
<div class="col">
<div class="position-relative d-flex justify-content-center">
<PokemonCard Pokemon="SelectedPokemon" />
</div>
</div>
<!-- Right Panel: Rating View -->
<div class="col-4">
<PokemonRatingPanel
PokemonToRate="SelectedPokemon"
NatureList="NatureList"
SubskillList="SubskillList"
/>
</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>
}
<hr class="mt-5"/>
</div>

View File

@ -58,7 +58,8 @@ namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{
SelectedPokemon = pokemon;
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()

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>
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
<div class="w-100">
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
@ -16,4 +20,7 @@
<PokemonTable AllPokemon="pokemons"/>
<hr class="mt-5" />
</div>

View File

@ -6,14 +6,17 @@
<PageTitle>Pokémon Sleep</PageTitle>
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
<div class="container">
<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">
<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 -->

View File

@ -8,6 +8,10 @@
<PokemonHeader />
<head>
<link rel="stylesheet" href="bootstrap/bootstrap-lumen.css" /> <!-- app.css -->
</head>
@if (_pokemon == null)
{
<Loading />
@ -17,14 +21,20 @@ else
<PageTitle>@_pokemon.PokemonName</PageTitle>
<!-- Total Componenet-->
<div class="w-100">
<div class="d-flex justify-content-center mt-4">
<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 class="mt-5 mx-5 d-flex justify-content-center">
<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){
@ -32,50 +42,54 @@ else
}
else
{
<!-- If Variation Pokemon have same PokemonId -->
@if(_pokemon.Id != _pokemonVariant.Id)
{
<div class="d-flex justify-content-center">
<div class="position-relative">
<PokemonCard Pokemon="_pokemon" />
<div class="position-absolute top-100 start-50 translate-middle mt-5">
<PokemonEditButton PokemonId="_pokemon.Id" />
<div class="pokemoncard m-1">
<PokemonCard Pokemon="_pokemon" />
</div>
</div>
<div class="position-relative">
<PokemonCard Pokemon="_pokemonVariant" />
<div class="position-absolute top-100 start-50 translate-middle mt-5">
<PokemonEditButton PokemonId="_pokemonVariant.Id" />
<div class="pokemoncard m-1">
<PokemonCard Pokemon="_pokemonVariant" />
</div>
</div>
</div>
}
<!-- If Variation Pokemon has diff PokemonId -->
else
{
<div class="position-relative">
<PokemonCard Pokemon="_pokemonVariant" />
<div class="position-absolute top-100 start-50 translate-middle mt-5">
<PokemonEditButton PokemonId="_pokemonVariant.Id" />
<div class="d-flex justify-content-center align-items-center h-100">
<div class="pokemoncard">
<PokemonCard Pokemon="_pokemonVariant" />
</div>
</div>
}
}
}
else{
<div class="position-relative">
<PokemonCard Pokemon="_pokemon" />
<div class="position-absolute top-100 start-50 translate-middle mt-5">
<PokemonEditButton PokemonId="_pokemon.Id" />
<!-- 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>
</button>
</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.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.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);
};
};