Compare commits

...

48 Commits

Author SHA1 Message Date
Kira Jiroux 1daad24db7 Minor changes to Edit page to make it centered 2025-10-01 12:20:38 -04:00
Kira Jiroux 40a79f1191 Modified Pokemon Sleep Home Page to serve responsive pages at different screen sizes 2025-09-30 19:08:48 -04:00
Kira Jiroux e45d80143c Just needed to update url extensions. Back to responsive styling 2025-09-30 17:57:01 -04:00
Kira Jiroux f10e2941ab Needed to change individual pokemon routing 2025-09-26 14:09:44 -04:00
Kira Jiroux 7f54f5b479 Minor edit 2025-09-26 14:00:46 -04:00
Kira Jiroux 84aeec64e8 Ugh its so so so good 2025-09-26 13:54:32 -04:00
Kira Jiroux 22a6e68fbd Wow I think it's pretty (for desktop). Need to push now before I push it too far 2025-09-26 13:35:48 -04:00
Kira Jiroux 2f210a0e1a Fixed the PokemonSelector visual issue where filtered pokemon appear super spaced out; now they appear naturally. Additional changes are just removing minor things. 2025-09-24 15:13:20 -04:00
Kira Jiroux 37d8fdaca2 Made form slightly better by activating it onchange rather than by onclick. Much more responsive, not absolutely certain it works. But! did put back in a pseudo delete button. It works, but not perfect. 2025-09-23 17:17:25 -04:00
Kira Jiroux 286b913832 Made reusable PokemonForm to replace Add/Edit forms. Also, renamed Download button to better fit the schema. Plus additional decorative changes. 2025-09-23 16:42:40 -04:00
Kira Jiroux d977e08650 Added multi-pokemon adding functionality including proper structuring and UI management; renamed some pages for clarity 2025-09-22 16:13:52 -04:00
Kira Jiroux bc07f56c97 Pokemon Download button fits better. 2025-09-04 13:27:28 -04:00
Kira Jiroux 2bf560f6f0 Made a download button and made an edit button toggleable 2025-06-27 21:07:34 -04:00
Kira Jiroux 568e66f7aa Got it serving projects. 2025-06-27 19:31:41 -04:00
Kira Jiroux a844aa54dc Added experience/projects into other views. 2025-06-23 13:05:21 -04:00
Kira Jiroux 498bda2cf4 All projects and experiences are put in; projects are not entirely where I want it but it's also more complicated. It looks nice tho. 2025-06-20 19:10:08 -04:00
Kira Jiroux a4a22ed586 Home page is really serving. Also, we have found THE bootswatch that I'm happy with for the entirety of the webpage so that has been adjusted. 2025-06-20 17:11:35 -04:00
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
Kira Jiroux bdd0f2e8bb Saving Responsive Styling changes before shifting gears. 2025-04-21 14:33:50 -04:00
Kira Jiroux b881c06b83 Quick, quick revision elsewhere to account for Darkrai. 2025-04-16 10:21:31 -04:00
Kira Jiroux 79bda1d0fc Keep playing with the table gosh. I want to shower. 2025-04-11 20:26:18 -04:00
Kira Jiroux aea7c06819 Some responsive-ness is happenin. 2025-04-11 20:07:06 -04:00
Kira Jiroux d04f9b260b Reconfiguring the PokemonSleep homepage 2025-04-11 17:56:25 -04:00
Kira Jiroux 779ca3620c Pokemon Cards are beautiful, serving different card views based on type. Also got pokemon going to their own separate extension in order for table button to appear better. Excellent! 2025-04-10 18:16:07 -04:00
Kira Jiroux 17334250f9 Editing view to be compatible with new bootstrap-brite.css. Still compatible with bootstrap-lumen.css 2025-04-10 14:43:21 -04:00
Kira Jiroux 5dd07c7c3f Adjusted table row layouts, and edited buttons to appear circular to coincide with Pkmn theme. 2025-04-10 13:30:26 -04:00
Kira Jiroux 8953387f66 Made table rows vertically aligned. 2025-04-08 15:02:41 -04:00
Kira Jiroux 9c87f3e4cf Moved Pokemon Page files into corresponding Page folder w/in directory. 2025-04-07 13:59:56 -04:00
Kira Jiroux 8f1dbebb30 Readjusting the file structuring, moved Pokemon-specific components into respective folder 2025-04-07 13:39:03 -04:00
Kira Jiroux f12217839b Putting in Loading Bars where necessary. 2025-04-05 00:40:49 -04:00
Kira Jiroux b8f3b45d1c Put in proper Loading bar. 2025-04-05 00:37:26 -04:00
Kira Jiroux 99ac4581ae Individual pokemon views are being served and can move to the next pokemon. Edit form can be accessed from either table or individual view and goes back to where it was. 2025-04-05 00:28:36 -04:00
Kira Jiroux 42d3a864e4 Looking to make individual pokemon views but later 2025-04-04 15:44:03 -04:00
118 changed files with 32982 additions and 1431 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Portfolio.Domain.Features.Portfolio
{
public class ProjectEntry
{
public string Title { get; set; }
public List<string> Descriptions { get; set; }
public List<ProjectLink> Links { 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.Portfolio
{
public class ProjectLink
{
public string Type { get; set; }
public string Url { get; set; }
public string Label { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Portfolio.Domain.Features.Portfolio
{
public class WorkExperience
{
public string Title { get; set; }
public string Company { get; set; }
public int StartYear { get; set; }
public int EndYear { get; set; }
public string Details { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Portfolio.Domain.Features.TemperatureDay
{
public class TemperatureDay
{
public DateTime Date { get; set; }
public double AvgTemp { get; set; }
}
}

View File

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

View File

@ -1,7 +1,9 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Identity.Client;
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 +27,7 @@ namespace Portfolio.Infrastructure
services.AddScoped<IPokemonRepository, PokemonRepository>();
services.AddScoped<IPokemonNatureRepository, PokemonNatureRepository>();
services.AddScoped<IPokemonSubskillRepository, PokemonSubskillRepository>();
//services.AddScoped<INWSWeatherService, NWSWeatherService>();
return services;
}

View File

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

View File

@ -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-lumen.css" /> <!-- app.css -->
<link rel="stylesheet" href="Portfolio.WebUI.Server.styles.css" />
<link rel="stylesheet" href="bootstrap/bootstrap-pulse.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,34 @@
@attribute [StreamRendering]
@rendermode InteractiveServer
<div class="pt-4">
@if (TemperatureDays is null || TemperatureRanges is null)
{
<Loading />
}
else
{
<div class="container">
<h3 class="text-xl font-bold mb-4 mt-4">Temperature Blanket Visualizer</h3>
<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 d-flex align-items-center">
<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 text-nowrap">
@TempRanges[i].Min&deg; @TempRanges[i].Max&deg;
</label>
</div>
<div class="col-6 ps-2">
<input type="color"
value="@TempRanges[i].Color"
@onchange="e => HandleColorChange(e, localIndex)" />
</div>
</div>
}
</div>
</div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,56 @@
@attribute [StreamRendering]
@rendermode InteractiveServer
<div class="card-wrapper d-flex flex-column align-items-center">
<div class="pokemon-card card-holo animated @GetTypeCssClass(_pokemon.PokemonType)">
<!-- Pokemon Name, Number, and Type -->
<div class="z-3">
@if (_pokemon.IsVariation)
{
<div class="pokemon-name"><p class="fw-bold card-title">@_pokemon.VariationName @_pokemon.PokemonName</p></div>
}
else
{
<div class="pokemon-name"><p class="fw-bold card-title">@_pokemon.PokemonName</p></div>
}
<div class="pokemon-number">
<p class="fw-light card-text">Pokédex #<strong>@_pokemon.PokemonId</strong></p>
</div>
<div >
<img class="pokemon-type" src="@GetTypeImageUrl(_pokemon.PokemonType)" />
</div>
</div>
<!-- Pokemon Image -->
<div class="card-image-slot z-1">
<PokemonImage baseUrl="@_pokemon.PokemonImageUrl" shinyUrl="@_pokemon.PokemonShinyImageUrl" />
</div>
<!-- Pokemon Flavortext -->
<div class="z-3 pokemon-flavor-text @(GetTypeCssClass(_pokemon.PokemonType))">
@if (string.IsNullOrEmpty(_pokemon.FlavorText))
{
<p class="fw-light">[ Pokemon Flavor Text Placeholder ]</p>
}
else
{
<p class="fw-light">@_pokemon.FlavorText</p>
}
</div>
<!-- Pokemon Sleep Type and Specialty Badges -->
<div class="position-absolute bottom-0 end-0 z-2">
<div class="d-flex justify-content-between">
<PokemonBadge BadgeItem="@_pokemon.SleepType" />
<PokemonBadge BadgeItem="@_pokemon.Speciality" />
</div>
</div>
</div>
<div class="mt-5">
<PokemonEditButton PokemonId="@_pokemon.Id" />
</div>
</div>

View File

@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Component
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
public partial class PokemonCard
{
@ -37,6 +37,11 @@ namespace Portfolio.WebUI.Server.Components.Component
return $"https://www.serebii.net/pokemonsleep/pokemon/type/{pokemonType.ToLower()}.png";
}
private string GetTypeCssClass(string type)
{
return "pokemon-type-" + type.ToLower();
}
}
}

View File

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

View File

@ -0,0 +1,9 @@
@inject IJSRuntime JS
<div class="">
<button class="btn btn-sm btn-primary p-1 rounded rounded-5 align-self-start text-white " @onclick="DownloadPokemonJson">
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="currentColor" class="bi bi-arrow-down-circle" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8m15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0M8.5 4.5a.5.5 0 0 0-1 0v5.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293z" />
</svg>
</button>
</div>

View File

@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Portfolio.Domain.Features.Pokemon;
using System.Text.Json;
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
partial class PokemonDownloadButton
{
[Parameter]
public List<Pokemon> _Pokemon { get; set; }
private List<Pokemon> pokemons = new List<Pokemon>();
protected override void OnParametersSet()
{
if (_Pokemon != null)
{
pokemons = _Pokemon.ToList();
}
}
private async Task DownloadPokemonJson()
{
var json = JsonSerializer.Serialize(pokemons, new JsonSerializerOptions { WriteIndented = true });
await JS.InvokeVoidAsync("downloadFileFromText", "pokemon.json", "application/json", json);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,25 @@

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

View File

@ -0,0 +1,22 @@
<div class="wrapper">
@if (shinyUrl == null)
{
<div class="flip-container">
<div class="flipper">
<img class="front" src="@baseUrl" />
</div>
</div>
}
else
{
<div class="flip-container" @onclick="() => ToggleImage()">
<div class="flipper @(isShiny ? "flipped" : "")">
<img class="front" src="@baseUrl" />
<img class="back" src="@shinyUrl" />
</div>
</div>
}
</div>

View File

@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Components;
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
public partial class PokemonImage
{
[Parameter]
public string baseUrl { get; set; }
[Parameter]
public string shinyUrl { get; set; }
private bool isShiny = false;
private void ToggleImage()
{
isShiny = !isShiny;
StateHasChanged();
}
}
}

View File

@ -0,0 +1,50 @@
.wrapper {
display: inline-block;
width: 90%;
height: 90%;
}
.flip-container {
position: absolute !important;
top: 40% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
perspective: 1000px;
display: inline-block;
width: 80%;
aspect-ratio: 1 / 1;
max-width: 280px;
margin: 0 auto;
cursor: pointer;
}
.flipper {
transition: transform 0.6s;
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: relative;
}
.flipped {
transform: rotateY(180deg);
}
.wrapper {
position: relative;
max-height: 100px;
width: 100%;
}
.front, .back {
width: 100%;
height: 100%;
position: absolute;
inset: 0; /* shorthand for top/right/bottom/left: 0 */
backface-visibility: hidden;
object-fit: contain; /* keep aspect ratio inside the box */
}
.back {
transform: rotateY(180deg);
}

View File

@ -0,0 +1,43 @@
<!-- Pokemon Navigation Buttons -->
<div class="mx-auto w-75 top-row row" >
<div class="row btn-group d-flex justify-content-end" >
<!-- Home -->
<button class="btn btn-primary mx-1 align-content-center" style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/pokemon-sleep">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-house-fill mb-1 text-white" viewBox="0 0 16 16">
<path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L8 2.207l6.646 6.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293z" />
<path d="m8 3.293 6 6V13.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 13.5V9.293z" />
</svg>
</NavLink>
</button>
<!-- Pokemon Table-->
<button class="btn btn-info mx-1" style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/pokemon-sleep/pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-table mb-1 text-white" viewBox="0 0 16 16">
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 2h-4v3h4zm0 4h-4v3h4zm0 4h-4v3h3a1 1 0 0 0 1-1zm-5 3v-3H6v3zm-5 0v-3H1v2a1 1 0 0 0 1 1zm-4-4h4V8H1zm0-4h4V4H1zm5-3v3h4V4zm4 4H6v3h4z" />
</svg>
</NavLink>
</button>
<!-- Rate Pokemon -->
<button class="btn btn-success mx-1" style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/pokemon-sleep/rate-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-award-fill mb-1 text-white" viewBox="0 0 16 16">
<path d="m8 0 1.669.864 1.858.282.842 1.68 1.337 1.32L13.4 6l.306 1.854-1.337 1.32-.842 1.68-1.858.282L8 12l-1.669-.864-1.858-.282-.842-1.68-1.337-1.32L2.6 6l-.306-1.854 1.337-1.32.842-1.68L6.331.864z" />
<path d="M4 11.794V16l4-1 4 1v-4.206l-2.018.306L8 13.126 6.018 12.1z" />
</svg>
</NavLink>
</button>
<!-- Add Pokemon (Wrap in Auth) -->
<button class="btn btn-warning mx-1 " style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/pokemon-sleep/add-new-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-circle-fill text-white" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3z" />
</svg>
</NavLink>
</button>
</div>
</div>

View File

@ -0,0 +1,6 @@
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
public partial class PokemonNavMenu
{
}
}

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

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

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,33 @@
.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;
align-content: flex-start;
}
.pokemon-card {
cursor: pointer;
transition: transform 0.15s ease-in-out;
}
.pokemon-card:hover {
transform: scale(1.13);
}
.small-card {
font-size: 0.75rem;
}

View File

@ -0,0 +1,372 @@
@inject IPokemonService PokemonService
@inject IJSRuntime JS
@inject NavigationManager Navigation
@attribute [StreamRendering]
@rendermode InteractiveServer
<!-- Table A: Desktop View-->
<div class="container d-none d-lg-block d-md-none mt-4">
<!-- Main UI -->
<div class="border-0 mx-auto col-8 ">
<!-- UI Header -->
<div class="row bg-secondary py-3 border-0 rounded-top">
<div class="d-flex align-items-center justify-content-between w-100 position-relative px-3">
<!-- Left: Search -->
<input class="form-control w-25 me-3 rounded rounded-5"
placeholder="Search Pokémon..."
@bind="SearchTerm"
@oninput="HandleSearch" />
<!-- Center: Title -->
<h2 class="text-white mb-0 position-absolute start-50 translate-middle-x">
Available Pokémon
</h2>
<!-- Right: Count + Download -->
<div class="d-flex align-items-center gap-2">
<div class="badge bg-light">
<p class="statText text-primary fw-medium shadow mb-0">@(pokemons.Count()) Pokémon</p>
</div>
<PokemonDownloadButton _Pokemon="pokemons" />
</div>
</div>
</div>
<!-- Table -->
<div class="tableFixHead d-flex flex-column justify-content-start bg-secondary table-responsive row ">
<table class="table table-borderless border-0 table-secondary table-striped align-middle">
<!-- Table Head -->
<thead>
<tr>
<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">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-1 text-center" scope="col">Sleep Type</th>
<th class="text-white text-bg-info col-1 text-center" scope="col">Speciality</th>
@if (adminToggle)
{
<th class="text-white text-bg-info col-2 text-center" scope="col">Admin</th>
}
</tr>
</thead>
<!-- If/Else Pokemon Loaded-->
@if(pokemons == null)
{
<tbody>
<Loading />
</tbody>
}
else
{
<!-- Table Body -->
<tbody>
@if (FilteredPokemon != null && FilteredPokemon.Any())
{
@foreach (var pokemon in FilteredPokemon)
{
<tr>
<!-- Section 1: Pokemon Image -->
<td class="pokeimage">
<PokemonImage baseUrl="@pokemon.PokemonImageUrl" shinyUrl="@pokemon.PokemonShinyImageUrl" />
</td>
<!-- Section 2: Pokemon # -->
<th 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>
<div class="d-flex justify-content-center">
<img src="@GetTypeImageUrl(pokemon.PokemonType)" style="width:36px; height:36px;" />
</div>
</td>
<!-- Section 5: Sleep Type -->
<td>
<div class="d-flex justify-content-center ">
<PokemonBadge BadgeItem="@pokemon.SleepType" />
</div>
</td>
<!-- Section 6: Speciality -->
<td>
<div class="d-flex justify-content-center">
<PokemonBadge BadgeItem="@pokemon.Speciality" />
</div>
</td>
<!-- Section 7: Admin Controls -->
@if (adminToggle)
{
<td>
<div class="d-flex justify-content-center gap-1">
<PokemonEditButton PokemonId="@pokemon.PokemonId" />
<button class="btn btn-danger rounded rounded-5 text-white " @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>
}
}
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>
<!-- Wrap in Auth -->
<div class="d-flex justify-content-end mt-1">
<div class="form-check form-switch">
<input class="form-check-input rounded rounded-3" type="checkbox" role="switch" id="flexSwitchCheckDefault" @bind="adminToggle">
</div>
</div>
</div>
</div>
<div class="container d-none d-lg-none d-md-block mt-4">
<!-- Main UI -->
<div class="border-0 mx-auto col-8 ">
<!-- UI Header -->
<div class="row bg-secondary py-3 border-0 rounded-top">
<div class="d-flex flex-row align-items-center justify-content-between w-100 gap-3 ms-0 me-0">
<div class="col">
<input class="form-control rounded rounded-5 fs-6 fw-lighter"
placeholder="Search Pokémon..."
@bind="SearchTerm"
@oninput="HandleSearch" />
</div>
<!-- Left: Search -->
<!-- Center: Title -->
<div class="col">
<h2 class="text-white text-center pt-2 fs-6 fw-light">
Available Pokémon
</h2>
</div>
<!-- Right: Count + Download -->
<div class="col">
<div class="d-flex align-items-center gap-1">
<div class="badge bg-light">
<p class="statText text-primary fw-medium shadow mb-0">@(pokemons.Count()) Pokémon</p>
</div>
<PokemonDownloadButton _Pokemon="pokemons" />
</div>
</div>
</div>
</div>
<!-- Table -->
<div class="tableFixHead d-flex flex-column justify-content-start bg-secondary table-responsive row ">
<table class="table table-borderless border-0 table-secondary table-striped align-middle">
<!-- Table Head -->
<thead>
<tr>
<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">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-1 text-center" scope="col">Sleep Type</th>
<th class="text-white text-bg-info col-1 text-center" scope="col">Speciality</th>
@if (adminToggle)
{
<th class="text-white text-bg-info col-2 text-center" scope="col">Admin</th>
}
</tr>
</thead>
<!-- If/Else Pokemon Loaded-->
@if (pokemons == null)
{
<tbody>
<Loading />
</tbody>
}
else
{
<!-- Table Body -->
<tbody>
@if (FilteredPokemon != null && FilteredPokemon.Any())
{
@foreach (var pokemon in FilteredPokemon)
{
<tr>
<!-- Section 1: Pokemon Image -->
<td class="">
<img src="@pokemon.PokemonImageUrl" style="width:100px;" />
</td>
<!-- Section 2: Pokemon # -->
<th 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>
<div class="d-flex justify-content-center">
<img src="@GetTypeImageUrl(pokemon.PokemonType)" style="width:36px; height:36px;" />
</div>
</td>
<!-- Section 5: Sleep Type -->
<td>
<div class="d-flex justify-content-center ">
<PokemonBadge BadgeItem="@pokemon.SleepType" />
</div>
</td>
<!-- Section 6: Speciality -->
<td>
<div class="d-flex justify-content-center">
<PokemonBadge BadgeItem="@pokemon.Speciality" />
</div>
</td>
<!-- Section 7: Admin Controls -->
@if (adminToggle)
{
<td>
<div class="d-flex justify-content-center gap-1">
<PokemonEditButton PokemonId="@pokemon.PokemonId" />
<button class="btn btn-danger rounded rounded-5 text-white " @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>
}
}
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>
<!-- Wrap in Auth -->
<div class="d-flex justify-content-end mt-1">
<div class="form-check form-switch">
<input class="form-check-input rounded rounded-3" type="checkbox" role="switch" id="flexSwitchCheckDefault" @bind="adminToggle">
</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">
<!-- Table Header -->
<div class="row card-header bg-secondary bg-gradient ml-0 py-3 border-0 bg-info">
<div class="flex-row justify-content-between">
<div class="text-center position-relative">
<h2 class="text-white text-decoration-underline">Pokémon</h2>
<div class="m-1 badge bg-white text-info position-absolute top-0 end-0 border-0 w-auto"><p class="statText">@(pokemons.Count()) Pokémon</p></div>
</div>
</div>
</div>
<!-- Table Body -->
<div class="tableFixHead row">
<table class="table table-striped border-0">
<tbody>
@if (pokemons == null)
{
<Loading />
}
else
{
@foreach (var pokemon in pokemons)
{
<tr class="border-0">
<div class="d-flex align-items-center" style="border: 1px dashed hotpink;">
<!-- Pokemon Image -->
<div class="me-3" style="border: 1px dashed hotpink;">
<div class="flip-container-sm" @onclick="() => ToggleImage(pokemon.Id)">
<div class="flipper-sm @(isShiny[pokemon.Id] ? "flipped" : "")">
<img class="front img-fluid" src="@pokemon.PokemonImageUrl" />
@if (pokemon.PokemonShinyImageUrl != null)
{
<img class="back img-fluid" src="@pokemon.PokemonShinyImageUrl" />
}
</div>
</div>
</div>
<div style="border: 1px dashed hotpink;">
<!-- Number and Name -->
<h5>
@pokemon.PokemonId -
<span class="pokemon-name-style fw-light" @onclick="() => ViewPokemon(pokemon.PokemonId)">
@(pokemon.IsVariation && ToggleVariationName(pokemon.Id, pokemon.PokemonId) ? $"{pokemon.VariationName} {pokemon.PokemonName}" : pokemon.PokemonName)
</span>
</h5>
<!-- Stats -->
<div class="d-flex flex-wrap align-items-center gap-2">
<img src="@GetTypeImageUrl(pokemon.PokemonType)" style="width:28px;" />
<PokemonBadge BadgeItem="@pokemon.SleepType" />
<PokemonBadge BadgeItem="@pokemon.Speciality" />
</div>
</div>
</div>
</tr>
}
}
</tbody>
</table>
</div>
</div> *@

View File

@ -2,8 +2,9 @@
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.JSInterop;
using Portfolio.Domain.Features.Pokemon;
using System.Text.Json;
namespace Portfolio.WebUI.Server.Components.Component
namespace Portfolio.WebUI.Server.Components.Component.Pokemon_Components
{
public partial class PokemonTable
{
@ -11,7 +12,8 @@ namespace Portfolio.WebUI.Server.Components.Component
public List<Pokemon> AllPokemon { get; set; }
private List<Pokemon> pokemons = new List<Pokemon>();
private Dictionary<int, bool> isShiny = new Dictionary<int, bool>();
private bool adminToggle = false;
protected override void OnParametersSet()
@ -19,19 +21,31 @@ namespace Portfolio.WebUI.Server.Components.Component
if (AllPokemon != null) {
pokemons = AllPokemon.ToList();
foreach (var pokemon in pokemons)
{
isShiny[pokemon.Id] = false;
}
}
}
private string SearchTerm { get; set; } = string.Empty;
private void ToggleImage(int Id)
private List<Pokemon> FilteredPokemon =>
string.IsNullOrWhiteSpace(SearchTerm)
? AllPokemon
: AllPokemon.Where(p =>
p.PokemonName.Contains(SearchTerm, StringComparison.OrdinalIgnoreCase)).ToList();
private async Task HandleSearch(ChangeEventArgs e)
{
if (isShiny.ContainsKey(Id))
SearchTerm = e?.Value?.ToString() ?? "";
}
private bool ToggleVariationName(int Id, int PokemonId)
{
foreach (var pokemon in pokemons)
{
isShiny[Id] = !isShiny[Id];
if (pokemon.PokemonId == PokemonId && pokemon.Id != Id)
{
return true;
}
}
return false;
}
private async Task ConfirmDelete(int Id)
@ -52,7 +66,12 @@ namespace Portfolio.WebUI.Server.Components.Component
private void EditPokemon(int id)
{
Navigation.NavigateTo($"/pokemonsleep/edit/{id}");
Navigation.NavigateTo($"/pokemon-sleep/edit/{id}");
}
private void ViewPokemon(int id)
{
Navigation.NavigateTo($"/pokemon-sleep/pokemon/{id}");
}
private string GetTypeImageUrl(string pokemonType)
@ -65,5 +84,9 @@ namespace Portfolio.WebUI.Server.Components.Component
return $"https://www.serebii.net/pokemonsleep/pokemon/type/{pokemonType.ToLower()}.png";
}
private void HandleToggleChange(ChangeEventArgs e)
{
Console.WriteLine($"Admin Toggle is now: {adminToggle}");
}
}
}

View File

@ -0,0 +1,122 @@
.five-percent {
width: 5%;
}
.ten-percent {
width: 10%;
}
.pokemontable {
height: 65vh;
}
.tableFixHead {
overflow: auto;
height: 600px;
}
.tableFixHead thead th {
position: sticky;
top: 0;
z-index: 10;
}
.align-top-tbody {
vertical-align: top;
}
.pokemon-name-style {
cursor: pointer;
font-size: 1.3rem;
}
.flip-container {
perspective: 1000px;
display: inline-block;
width: 90px;
height: 90px;
cursor: pointer;
}
.flipper {
transition: transform 0.6s;
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: relative;
}
.flipped {
transform: rotateY(180deg);
}
.flip-container-sm {
perspective: 1000px;
width: 64px;
height: 64px;
}
.flipper-sm {
transition: 0.6s;
transform-style: preserve-3d;
position: relative;
}
.flipper-sm img {
backface-visibility: hidden;
position: absolute;
width: 64px;
height: 64px;
}
.flipper-sm .front {
z-index: 2;
}
.flipper-sm .back {
transform: rotateY(180deg);
}
.flipper-sm.flipped {
transform: rotateY(180deg);
}
.fixed-row-height {
height: 90px; /* or any height that suits your card style */
}
.badge {
width: 100px;
height: 30px;
color: white;
padding: 4px 8px;
text-align: center;
vertical-align: middle;
border-radius: 30px;
}
.statText {
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 13px;
cursor: default;
}
.pokeimage {
width: 100px;
height: 130px;
}

View File

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

View File

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

View File

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

View File

@ -1,37 +0,0 @@

<!-- Heading + Buttons -->
<div class="top-row row w-100 mx-0 bg-secondary py-3">
<div class="col-4"></div>
<div class="col-4 text-center">
<h1 class="text-primary">Pokémon Sleep</h1>
</div>
<div class="col-4 text-end">
<div class="btn-group col my-1">
<NavLink class="btn btn-danger" style="border-radius: 50px 15px;" href="/pokemonsleep">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-table" viewBox="0 0 16 16">
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 2h-4v3h4zm0 4h-4v3h4zm0 4h-4v3h3a1 1 0 0 0 1-1zm-5 3v-3H6v3zm-5 0v-3H1v2a1 1 0 0 0 1 1zm-4-4h4V8H1zm0-4h4V4H1zm5-3v3h4V4zm4 4H6v3h4z" />
</svg>
<span class="mx-1">Pokémon</span>
</NavLink>
<NavLink class="btn btn-primary" style="border-radius: 50px 15px;" href="/pokemonsleep/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">
<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="mx-1">Rate Pokémon</span>
</NavLink>
<NavLink class="btn btn-info" style="border-radius: 50px 15px;" href="/pokemonsleep/add-new-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-circle-fill" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3z" />
</svg>
<span class="mx-1">Add New Pokémon</span>
</NavLink>
</div>
</div>
</div>

View File

@ -1,140 +0,0 @@
@inject IPokemonService PokemonService
@inject IJSRuntime JS
@inject NavigationManager Navigation
@attribute [StreamRendering]
@rendermode InteractiveServer
<!-- Main Body -->
@if (pokemons == null)
{
<p><em>Loading...</em></p>
}
else
{
<!-- Main UI -->
<div class="card shadow border-0 mt-4 m-auto w-50">
<!-- Table Header -->
<div class="card-header bg-secondary bg-gradient ml-0 py-3">
<div class="row">
<div class="col-12 text-center position-relative">
<h2 class="text-info">Available Pokémon</h2>
<div class="m-1 col-1 badge bg-info position-absolute top-0 end-0"><p class="statText">@(pokemons.Count()) Pokemon</p></div>
</div>
</div>
</div>
<!-- Table -->
<div class="tableFixHead">
<table class="table table-striped">
<!-- Table Head -->
<thead>
<tr>
<th class="text-bg-info col col-2" scope="col"></th>
<th class="text-bg-info col pokemon-dex-width" scope="col">#</th>
<th class="text-bg-info col pokemon-name-width" scope="col">Pokemon</th>
<th class="text-bg-info col pokemon-type-width" scope="col">Type</th>
<th class="text-bg-info col pokemon-type-width" scope="col">Sleep Type</th>
<th class="text-bg-info col pokemon-type-width" scope="col">Speciality</th>
<th class="text-bg-info col pokemon-type-width" scope="col"></th>
</tr>
</thead>
<!-- Table Body -->
<tbody>
@foreach (var pokemon in pokemons)
{
<tr>
<!-- Section: Pokemon Image -->
@{
string baseUrl = pokemon.PokemonImageUrl;
string shinyUrl = pokemon.PokemonShinyImageUrl;
}
<td style="text-align: center;">
@if(shinyUrl == null){
<div class="flip-container">
<div class="flipper">
<img class="front" src="@baseUrl" />
</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">@pokemon.PokemonId</th>
<!-- Section 3: Pokemon Name -->
@if (pokemon.IsVariation) // If a Variant
{
@if (pokemon.VariationName == "Alolan")
{
<td style="vertical-align: auto;"> Alolan @pokemon.PokemonName</td>
}
@if (pokemon.VariationName == "Paldean")
{
<td style="vertical-align: auto;"> Paldean @pokemon.PokemonName</td>
}
}
else // Otherwise, Base Case
{
<td style="vertical-align: auto;"> @pokemon.PokemonName</td>
}
<!-- Section 4: Pokemon Type -->
<td >
<div class="m-1 col-1" >
<img src="@GetTypeImageUrl(pokemon.PokemonType)" class="" style="width:60px; height:60px;" />
</div>
</td>
<!-- Section 5: Sleep Type -->
<td style="vertical-align: auto;">
<div class="m-1 col-1 badge @pokemon.SleepType.ToLower()"><p class="statText">@pokemon.SleepType</p></div>
</td>
<!-- Section 6: Speciality -->
<td style="padding-right: 30px; vertical-align: auto;">
<div class="m-1 col-1 badge @pokemon.Speciality.ToLower()"><p class="statText">@pokemon.Speciality</p></div>
</td>
<!-- Section 7: Functional Buttons -->
<td>
<button class="btn btn-warning" @onclick="() => EditPokemon(pokemon.Id)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-fill" viewBox="0 0 16 16">
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.5.5 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11z" />
</svg>
</button>
<button class="btn btn-danger" @onclick="() => ConfirmDelete(pokemon.Id)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-trash" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z" />
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4zM2.5 3h11V2h-11z" />
</svg>
</button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}

View File

@ -1,107 +0,0 @@

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

View File

@ -0,0 +1,34 @@
<div class=" mx-auto w-75" >
<header class="pt-5 w-100">
<div class="row flex-nowrap justify-content-evenly align-items-center" >
<div class="col pt-1">
<div class="d-flex align-items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="@_primaryColor" 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-2 text-primary">Kira Jiroux</span>
</div>
</div>
<div class="col d-flex justify-content-end align-items-center gap-5">
<NavLink class="nav-link d-flex align-items-center rounded rounded-5" href="" Match="NavLinkMatch.All" ActiveClass="nav-active">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="@_primaryColor" class="bi bi-house-heart-fill" 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 text-primary">Home</span>
</NavLink>
<NavLink class="nav-link d-flex align-items-center rounded rounded-5" href="temperature-blanket" ActiveClass="nav-active">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="@_primaryColor" class="bi bi-grid-3x3" viewBox="0 0 16 16">
<path d="M0 1.5A1.5 1.5 0 0 1 1.5 0h13A1.5 1.5 0 0 1 16 1.5v13a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 14.5zM1.5 1a.5.5 0 0 0-.5.5V5h4V1zM5 6H1v4h4zm1 4h4V6H6zm-1 1H1v3.5a.5.5 0 0 0 .5.5H5zm1 0v4h4v-4zm5 0v4h3.5a.5.5 0 0 0 .5-.5V11zm0-1h4V6h-4zm0-5h4V1.5a.5.5 0 0 0-.5-.5H11zm-1 0V1H6v4z" />
</svg><span class="mx-2 mt-0 text-primary">Crochet</span>
</NavLink>
<NavLink class="nav-link d-flex align-items-center rounded rounded-5" href="pokemon-sleep" ActiveClass="nav-active">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="@_primaryColor" class="bi bi-p-circle-fill" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.5 4.002V12h1.283V9.164h1.668C10.033 9.164 11 8.08 11 6.586c0-1.482-.955-2.584-2.538-2.584zm2.77 4.072c.893 0 1.419-.545 1.419-1.488s-.526-1.482-1.42-1.482H6.778v2.97z" />
</svg> <span class="mx-2 mt-0 text-primary">Pokémon Sleep</span>
</NavLink>
</div>
</div>
</header>
</div>
<div class="mt-2 mb-3 mx-auto w-75 border-top border-primary-subtle"></div>

View File

@ -0,0 +1,7 @@
namespace Portfolio.WebUI.Server.Components.Layout
{
public partial class BaseNavMenu
{
private string _primaryColor = "#593196";
}
}

View File

@ -0,0 +1,13 @@
body {
}
::deep .nav-active {
font-weight: bold;
background-color: #f2eaff;
color: black;
padding-left: 15px;
padding-right: 15px;
padding-top: 5px;
padding-bottom: 5px;
border: 1px dashed #593196;
}

View File

@ -0,0 +1,14 @@

<div class="mt-2 mx-auto w-75 mt-2 border-top border-primary-subtle"></div>
<div class="container mt-2">
<footer class="d-flex flex-column align-content-center">
<p class="text-center fs-6 fw-lighter text-muted">
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,9 @@
@inherits LayoutComponentBase
<div class="page" >
<Sidebar/>
<main class="bg-primary-subtle">
<article class="content px-4">
@Body
</article>
<div class="d-flex flex-column min-vh-100">
<BaseNavMenu />
<main class="container flex-grow-1">
@Body
</main>
<MadeWithLoveFooter/>
</div>

View File

@ -1,8 +1,4 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
@ -12,6 +8,10 @@ main {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.container {
max-width: 1920px;
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
@ -47,9 +47,6 @@ main {
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
@ -76,21 +73,3 @@ main {
}
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}

View File

@ -1,35 +1,35 @@
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">Portfolio.WebUI.Server</a>
<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-3">
<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-2 text-white">Kira Jiroux</span>
</div>
</div>
<ul class="navbar-nav">
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
<li class="nav-item">
<NavLink class="nav-link d-flex align-items-center" href="" Match="NavLinkMatch.All">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFFFFF" class="bi bi-house-heart-fill" 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 text-white">Home</span>
</NavLink>
</div>
<div class="nav-item px-3">
<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>
<li>
<NavLink class="nav-link d-flex align-items-center" href="temperature-blanket">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFFFFF" 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 text-white">Crochet</span>
</NavLink>
</div>
<div class="nav-item px-3">
<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">
</li>
<li>
<NavLink class="nav-link d-flex align-items-center" href="pokemonsleep">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFFFFF" 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 text-white">Pokémon Sleep</span>
</NavLink>
</div>
</nav>
</div>
</li>
</ul>
</div>

View File

@ -1,105 +0,0 @@
.navbar-toggler {
appearance: none;
cursor: pointer;
width: 3.5rem;
height: 2.5rem;
color: white;
position: absolute;
top: 0.5rem;
right: 1rem;
border: 1px solid rgba(255, 255, 255, 0.1);
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
}
.navbar-toggler:checked {
background-color: rgba(255, 255, 255, 0.5);
}
.top-row {
height: 3.5rem;
background-color: rgba(0,0,0,0.4);
}
.navbar-brand {
font-size: 1.1rem;
}
.bi {
display: inline-block;
position: relative;
width: 1.25rem;
height: 1.25rem;
margin-right: 0.75rem;
top: -1px;
background-size: cover;
}
.bi-house-door-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
}
.bi-plus-square-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
}
.bi-list-nested-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep .nav-link {
color: #d7d7d7;
background: none;
border: none;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
width: 100%;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.37);
color: white;
}
.nav-item ::deep .nav-link:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
.nav-scrollable {
display: none;
}
.navbar-toggler:checked ~ .nav-scrollable {
display: block;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.nav-scrollable {
/* Never collapse the sidebar for wide screens */
display: block;
/* Allow sidebar to scroll for tall menus */
height: calc(100vh - 3.5rem);
overflow-y: auto;
}
}

View File

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

View File

@ -1,45 +0,0 @@
<div class="navbar navbar-expand-lg navbar-light bg-info justify-content-md-center" id="navbarsExample08">
<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
</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="articles">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-journal-richtext" viewBox="0 0 16 16">
<path d="M7.5 3.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m-.861 1.542 1.33.886 1.854-1.855a.25.25 0 0 1 .289-.047L11 4.75V7a.5.5 0 0 1-.5.5h-5A.5.5 0 0 1 5 7v-.5s1.54-1.274 1.639-1.208M5 9.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1-.5-.5" />
<path d="M3 0h10a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-1h1v1a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v1H1V2a2 2 0 0 1 2-2" />
<path d="M1 5v-.5a.5.5 0 0 1 1 0V5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm0 3v-.5a.5.5 0 0 1 1 0V8h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm0 3v-.5a.5.5 0 0 1 1 0v.5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z" />
</svg> Articles
</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="pokemonsleep">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-p-circle-fill" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.5 4.002V12h1.283V9.164h1.668C10.033 9.164 11 8.08 11 6.586c0-1.482-.955-2.584-2.538-2.584zm2.77 4.072c.893 0 1.419-.545 1.419-1.488s-.526-1.482-1.42-1.482H6.778v2.97z" />
</svg> Pokemon Sleep
</NavLink>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="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,14 @@
@page "/temperature-blanket"
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Crochet Tools</PageTitle>
@* <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

@ -1,9 +1,841 @@
@page "/"
@inject IHttpClientFactory ClientFactory
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Home</PageTitle>
<!-- <PokemonBackground />-->
<h1 class="test">Hello, world!</h1>
<p>Ensuring that git is connected properly.</p>
<!-- View 1: Desktop View -->
<div class="container d-none d-lg-none d-xl-block mt-3 pt-3 pb-5 ps-5 pe-5 ">
<h1 class="fst-italic fw-light fs-1 font-monospace">Hello, World!</h1>
<!-- Start of Grid -->
<div class="row mb-3">
<!-- Profile Image -->
<div class="col-auto ">
<img class="card-img rounded-3" style="width: 18.1em;" src="https://i.pinimg.com/736x/54/63/ed/5463ed9471053712a76c0dd0b301a7ea.jpg" />
</div>
<div class="col ">
<!-- About Me-->
<div class="d-inline-flex row card rounded fs-5 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">
<!-- School Experience -->
<div class="col ps-0">
<div class="card rounded p-2 ps-3 pe-3 border border-1 border-primary">
<p class="fs-6 fw-lighter card-subtitle p-0">September 2018 March 2022</p>
<p class="fs-5 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>
@**@
<!-- Courses -->
<label class="text-decoration-underline fw-semibold">Courses</label>
<div class="overflow-auto" style="height: auto; max-height: 12rem">
<ul class="list-group list-group-flush">
@if (courses == null)
{
<div>Loading...</div>
}
else
{
<ul class="list-group list-group-flush">
@foreach (var course in courses)
{
<li class="list-group-item">@course</li>
}
</ul>
}
</ul>
</div>
</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>
<!--Skills \ Languages-->
<div class="col-auto p-0 ">
<div class="bg-white rounded p-2 border border-1 border-primary" >
<div class="card-header">
<label class="text-decoration-underline fw-semibold">Skills \ Languages</label>
</div>
<div class="overflow-auto" style="max-height: 20.1rem">
@if (skills == null)
{
<div>Loading...</div>
}
else
{
<ul class="list-group list-group-flush">
@foreach (var skill in skills)
{
<li class="list-group-item">@skill</li>
}
</ul>
}
</div>
</div>
</div>
<!-- Tools -->
<div class="col-auto pe-0">
<div class="bg-white rounded p-2 border border-1 border-primary">
<div class="card-header">
<label class="text-decoration-underline fw-semibold">Tools</label>
</div>
<div class="overflow-auto" style="max-height: 20.1rem">
@if (tools == null)
{
<div>Loading...</div>
}
else
{
<ul class="list-group list-group-flush">
@foreach (var tool in tools)
{
<li class="list-group-item">@tool</li>
}
</ul>
}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Experiences / Projects -->
<div class="row ">
<div class="col card border-0 rounded rounded-4 pt-3 pb-3">
<!-- Button Bay-->
<div class="row mt-2">
<div class="d-grid gap-2 d-md-flex justify-content-around">
<button class="btn rounded-4 w-25 @(isExperience ? "btn-primary" : "btn-outline-primary")" type="button" @onclick="() => ToggleExperience()">Experience</button>
<button class="btn rounded-4 w-25 @(isProjects ? "btn-primary" : "btn-outline-primary")" type="button" @onclick="() => ToggleProjects()">Projects</button>
</div>
</div>
<div class="row">
<div class="col">
@if (isExperience)
{
<div class="row justify-content-md-center fst-italic fs-1 mb-2">
Experience
</div>
@if (experiences == null)
{
<div>Loading...</div>
}
else
{
@foreach (var experience in experiences)
{
<div class="row justify-content-md-center mb-3">
<div class="card w-75 p-3 ps-4 pe-4 rounded">
<p class="fs-6 fw-lighter card-subtitle p-0"> @experience.StartYear @experience.EndYear</p>
<p class="fs-5 card-title m-0">@experience.Title - @experience.Company</p>
<p class="fs-6 fw-lighter card-text">@experience.Details</p>
</div>
</div>
}
}
}
else if (isProjects)
{
<div class="row justify-content-md-center fst-italic fs-1 mb-2">Projects</div>
@if (projects == null)
{
<p>Loading projects...</p>
}
else
{
@foreach (var project in projects)
{
<div class="row justify-content-md-center mb-3">
<div class="card w-75 p-3 ps-4 pe-4 rounded">
<p class="fs-5 card-title m-0">@project.Title</p>
@foreach (var desc in project.Descriptions)
{
<p class="fs-6 fw-lighter card-text">@((MarkupString)FormatDescription(desc))</p>
}
@if (project.Links?.Any() == true)
{
<div class="mt-2 align-self-end">
@foreach (var link in project.Links)
{
@switch (link.Type)
{
case "external":
<a class="me-2" href="@link.Url">@link.Label</a>
break;
case "internal":
<a class="mt-2 " href=""@link.Url">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-file-code" viewBox="0 0 16 16">
<path d="M6.646 5.646a.5.5 0 1 1 .708.708L5.707 8l1.647 1.646a.5.5 0 0 1-.708.708l-2-2a.5.5 0 0 1 0-.708zm2.708 0a.5.5 0 1 0-.708.708L10.293 8 8.646 9.646a.5.5 0 0 0 .708.708l2-2a.5.5 0 0 0 0-.708z"/>
<path d="M2 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zm10-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1"/>
</svg>
</a>
break;
case "github":
<a class="mt-2" href="@link.Url">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-github" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"/>
</svg>
</a>
break;
}
}
</div>
}
</div>
</div>
}
}
}
</div>
</div>
</div>
</div>
</div>
<!-- View 2: Smaller Desktop View -->
<div class="container d-none d-md-none d-xl-none d-lg-block mt-4 pt-3 pb-4 ">
<div class="row">
<h1 class="fst-italic fw-light fs-1 font-monospace ps-1">Hello, World!</h1>
</div>
<div class="row mb-3">
<div class="col-auto ps-0">
<img class="card-img rounded-3" style="width: 16.9em;" src="https://i.pinimg.com/736x/54/63/ed/5463ed9471053712a76c0dd0b301a7ea.jpg" />
</div>
<div class="col ">
<!-- About Me-->
<div class="row card rounded fs-6 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 mt-3">
<!-- School Experience -->
<div class="col me-1">
<div class="row bg-white rounded p-2 ps-3 pe-3 border border-1 border-primary">
<p class="fs-6 fw-lighter card-subtitle p-0">September 2018 March 2022</p>
<p class="fs-5 card-title m-0 p-0">B.S. in Computer Science</p>
<p class="fs-6 fw-lighter card-subtitle p-0">Oregon State University</p>
<p class="fs-6 fw-lighter card-subtitle p-0">Graduated Cum Laude</p>
<p class="fs-6 fw-lighter card-subtitle p-0">Deans List: Fall 2020, Fall 2021</p>
<p class="fs-6 fw-lighter card-subtitle p-0">Final GPA: 3.33</p>
<!-- Courses -->
<label class="text-decoration-underline p-0">Courses</label>
<div class="overflow-auto p-0" style="max-height: 10rem">
<ul class="list-group list-group-flush">
@if (courses == null)
{
<div>Loading...</div>
}
else
{
<ul class="list-group list-group-flush">
@foreach (var course in courses)
{
<li class="list-group-item">@course</li>
}
</ul>
}
</ul>
</div>
</div>
</div>
<div class="col ms-1">
<!--Skills \ Languages-->
<div class="row bg-white rounded p-2 border border-1 border-primary mb-2">
<div class="card-header">
<label class="text-decoration-underline">Skills \ Languages</label>
</div>
<div class="overflow-auto p-0" style="max-height: 8rem">
@if (skills == null)
{
<div>Loading...</div>
}
else
{
<ul class="list-group list-group-flush">
@foreach (var skill in skills)
{
<li class="list-group-item">@skill</li>
}
</ul>
}
</div>
</div>
<!-- Tools -->
<div class="row bg-white rounded p-2 border border-1 border-primary">
<div class="card-header">
<label class="text-decoration-underline">Tools</label>
</div>
<div class="overflow-auto p-0" style="max-height: 8rem">
@if (tools == null)
{
<div>Loading...</div>
}
else
{
<ul class="list-group list-group-flush">
@foreach (var tool in tools)
{
<li class="list-group-item">@tool</li>
}
</ul>
}
</div>
</div>
</div>
</div>
</div>
<!-- Experience / Projects -->
<div>
<div class="row mt-3">
<div class="col card border-0 rounded rounded-4 pt-3 pb-3">
<!-- Button Bay-->
<div class="row mt-2">
<div class="d-grid gap-2 d-md-flex justify-content-around">
<button class="btn rounded-4 w-25 @(isExperience ? "btn-primary" : "btn-outline-primary")" type="button" @onclick="() => ToggleExperience()">Experience</button>
<button class="btn rounded-4 w-25 @(isProjects ? "btn-primary" : "btn-outline-primary")" type="button" @onclick="() => ToggleProjects()">Projects</button>
</div>
</div>
<div class="row">
<div class="col">
@if (isExperience)
{
<div class="row justify-content-md-center fst-italic fs-1 mb-2">
Experience
</div>
@if (experiences == null)
{
<div>Loading...</div>
}
else
{
@foreach (var experience in experiences)
{
<div class="row justify-content-md-center mb-3">
<div class="card w-75 p-3 ps-4 pe-4 rounded">
<p class="fs-6 fw-lighter card-subtitle p-0"> @experience.StartYear @experience.EndYear</p>
<p class="fs-5 card-title m-0">@experience.Title - @experience.Company</p>
<p class="fs-6 fw-lighter card-text">@experience.Details</p>
</div>
</div>
}
}
}
else if (isProjects)
{
<div class="row justify-content-md-center fst-italic fs-1 mb-2">Projects</div>
@if (projects == null)
{
<p>Loading projects...</p>
}
else
{
@foreach (var project in projects)
{
<div class="row justify-content-md-center mb-3">
<div class="card w-75 p-3 ps-4 pe-4 rounded">
<p class="fs-5 card-title m-0">@project.Title</p>
@foreach (var desc in project.Descriptions)
{
<p class="fs-6 fw-lighter card-text">@((MarkupString)FormatDescription(desc))</p>
}
@if (project.Links?.Any() == true)
{
<div class="mt-2 align-self-end">
@foreach (var link in project.Links)
{
@switch (link.Type)
{
case "external":
<a class="me-2" href="@link.Url">@link.Label</a>
break;
case "internal":
<a class="mt-2 " href=""@link.Url">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-file-code" viewBox="0 0 16 16">
<path d="M6.646 5.646a.5.5 0 1 1 .708.708L5.707 8l1.647 1.646a.5.5 0 0 1-.708.708l-2-2a.5.5 0 0 1 0-.708zm2.708 0a.5.5 0 1 0-.708.708L10.293 8 8.646 9.646a.5.5 0 0 0 .708.708l2-2a.5.5 0 0 0 0-.708z"/>
<path d="M2 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zm10-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1"/>
</svg>
</a>
break;
case "github":
<a class="mt-2" href="@link.Url">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-github" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"/>
</svg>
</a>
break;
}
}
</div>
}
</div>
</div>
}
}
}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- View 3: Tablet View -->
<div class="container d-none d-md-block d-lg-none mt-4 pt-3 pb-4 ">
<h1 class="fst-italic fw-light fs-1 font-monospace">Hello, World!</h1>
<div class="row">
<div class="card rounded p-3 border-0">
<div class="clearfix">
<img src="https://i.pinimg.com/736x/54/63/ed/5463ed9471053712a76c0dd0b301a7ea.jpg"
class="mobile-profile float-start me-3 mb-2"
style="width: 10em;
height: 10em;
object-fit: cover;
border-radius: 0.5rem;" />
<p class="fs-6 mb-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.
</p>
</div>
</div>
</div>
<div class="row mt-3">
<!-- School Experience -->
<div class="col me-1">
<div class="row bg-white rounded p-2 ps-3 pe-3 border border-1 border-primary">
<p class="fs-6 fw-lighter card-subtitle p-0">September 2018 March 2022</p>
<p class="fs-5 card-title m-0 p-0">B.S. in Computer Science</p>
<p class="fs-6 fw-lighter card-subtitle p-0">Oregon State University</p>
<p class="fs-6 fw-lighter card-subtitle p-0">Graduated Cum Laude</p>
<p class="fs-6 fw-lighter card-subtitle p-0">Deans List: Fall 2020, Fall 2021</p>
<p class="fs-6 fw-lighter card-subtitle p-0">Final GPA: 3.33</p>
<!-- Courses -->
<label class="text-decoration-underline p-0">Courses</label>
<div class="overflow-auto p-0" style="max-height: 9.8rem">
<ul class="list-group list-group-flush">
@if (courses == null)
{
<div>Loading...</div>
}
else
{
<ul class="list-group list-group-flush">
@foreach (var course in courses)
{
<li class="list-group-item">@course</li>
}
</ul>
}
</ul>
</div>
</div
</div>
<div class="col ms-1">
<!--Skills \ Languages-->
<div class="row bg-white rounded p-2 border border-1 border-primary mb-2">
<div class="card-header">
<label class="text-decoration-underline">Skills \ Languages</label>
</div>
<div class="overflow-auto p-0" style="max-height: 8rem">
@if (skills == null)
{
<div>Loading...</div>
}
else
{
<ul class="list-group list-group-flush">
@foreach (var skill in skills)
{
<li class="list-group-item">@skill</li>
}
</ul>
}
</div>
</div>
<!-- Tools -->
<div class="row bg-white rounded p-2 border border-1 border-primary">
<div class="card-header">
<label class="text-decoration-underline">Tools</label>
</div>
<div class="overflow-auto p-0" style="max-height: 8rem">
@if (tools == null)
{
<div>Loading...</div>
}
else
{
<ul class="list-group list-group-flush">
@foreach (var tool in tools)
{
<li class="list-group-item">@tool</li>
}
</ul>
}
</div>
</div>
</div>
</div>
<div>
<div class="row mt-3">
<div class="col card border-0 rounded rounded-4 pt-3 pb-3">
<!-- Button Bay-->
<div class="row mt-2">
<div class="d-grid gap-2 d-md-flex justify-content-around">
<button class="btn rounded-4 w-25 @(isExperience ? "btn-primary" : "btn-outline-primary")" type="button" @onclick="() => ToggleExperience()">Experience</button>
<button class="btn rounded-4 w-25 @(isProjects ? "btn-primary" : "btn-outline-primary")" type="button" @onclick="() => ToggleProjects()">Projects</button>
</div>
</div>
<div class="row">
<div class="col">
@if (isExperience)
{
<div class="row justify-content-md-center fst-italic fs-1 mb-2">
Experience
</div>
@if (experiences == null)
{
<div>Loading...</div>
}
else
{
@foreach (var experience in experiences)
{
<div class="row justify-content-md-center mb-3">
<div class="card w-75 p-3 ps-4 pe-4 rounded">
<p class="fs-6 fw-lighter card-subtitle p-0"> @experience.StartYear @experience.EndYear</p>
<p class="fs-5 card-title m-0">@experience.Title - @experience.Company</p>
<p class="fs-6 fw-lighter card-text">@experience.Details</p>
</div>
</div>
}
}
}
else if (isProjects)
{
<div class="row justify-content-md-center fst-italic fs-1 mb-2">Projects</div>
@if (projects == null)
{
<p>Loading projects...</p>
}
else
{
@foreach (var project in projects)
{
<div class="row justify-content-md-center mb-3">
<div class="card w-75 p-3 ps-4 pe-4 rounded">
<p class="fs-5 card-title m-0">@project.Title</p>
@foreach (var desc in project.Descriptions)
{
<p class="fs-6 fw-lighter card-text">@((MarkupString)FormatDescription(desc))</p>
}
@if (project.Links?.Any() == true)
{
<div class="mt-2 align-self-end">
@foreach (var link in project.Links)
{
@switch (link.Type)
{
case "external":
<a class="me-2" href="@link.Url">@link.Label</a>
break;
case "internal":
<a class="mt-2 " href=""@link.Url">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-file-code" viewBox="0 0 16 16">
<path d="M6.646 5.646a.5.5 0 1 1 .708.708L5.707 8l1.647 1.646a.5.5 0 0 1-.708.708l-2-2a.5.5 0 0 1 0-.708zm2.708 0a.5.5 0 1 0-.708.708L10.293 8 8.646 9.646a.5.5 0 0 0 .708.708l2-2a.5.5 0 0 0 0-.708z"/>
<path d="M2 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zm10-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1"/>
</svg>
</a>
break;
case "github":
<a class="mt-2" href="@link.Url">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-github" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"/>
</svg>
</a>
break;
}
}
</div>
}
</div>
</div>
}
}
}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- View 4: Mobile View -->
<div class="container card rounded border-0 d-block d-md-none mx-auto mt-4 mb-3">
<h1 class="fst-italic fw-light fs-1 font-monospace pt-2">Hello, World!</h1>
<div class="row">
<div class="card border-0">
<div class="clearfix">
<img src="https://i.pinimg.com/736x/54/63/ed/5463ed9471053712a76c0dd0b301a7ea.jpg"
class="mobile-profile float-start me-3 mb-2" />
<p class="fs-6 mb-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.
</p>
</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" />
<!-- Education -->
<div class="row mb-3 ms-2 me-2 ">
<div class="card p-2 ps-3 pe-3 border border-1 border-primary rounded rounded-1">
<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>
<!-- Skills \ Languages -->
<div class="row mb-3 ms-2 me-2">
<div class="bg-primary-subtle rounded p-2 border-0">
<div class="card-header text-bg-primary text-light fs-3">
Skills \ Languages
</div>
<div class="overflow-auto" style="max-height: 15rem">
@if (skills == null)
{
<div>Loading...</div>
}
else
{
<ul class="list-group list-group-flush">
@foreach (var skill in skills)
{
<li class="list-group-item">@skill</li>
}
</ul>
}
</div>
</div>
</div>
<!-- Technology -->
<div class="row mb-3 ms-2 me-2">
<div class="bg-primary-subtle rounded p-2 border-0">
<div class="card-header text-bg-primary text-light fs-3">
Technology
</div>
<div class="overflow-auto" style="max-height: 15rem">
@if (tools == null)
{
<div>Loading...</div>
}
else
{
<ul class="list-group list-group-flush">
@foreach (var tool in tools)
{
<li class="list-group-item">@tool</li>
}
</ul>
}
</div>
</div>
</div>
<!-- Experience \ Projects -->
<div class="row mb-3 ms-2 me-2">
<div class="col card border-0 rounded rounded-4 pt-3 ">
<!-- Button Bay-->
<div class="row mt-2">
<div class="d-flex justify-content-evenly">
<button class="btn rounded-4 flex-fill @(isExperience ? "btn-primary" : "btn-outline-primary")" type="button" @onclick="() => ToggleExperience()">Experience</button>
<button class="btn rounded-4 flex-fill @(isProjects ? "btn-primary" : "btn-outline-primary")" type="button" @onclick="() => ToggleProjects()">Projects</button>
</div>
</div>
@if (isExperience)
{
<div class="row justify-content-center fst-italic fs-1 mt-2">
Experience
</div>
@if (experiences == null)
{
<div>Loading...</div>
}
else
{
<div class="overflow-auto" style="max-height: 16rem">
<ul class="list-group list-group-flush">
@foreach (var experience in experiences)
{
<div class=" justify-content-md-center">
<div class="card p-3 ps-4 pe-4 rounded">
<p class="fs-6 fw-lighter card-subtitle p-0"> @experience.StartYear @experience.EndYear</p>
<p class="fs-5 card-title m-0">@experience.Title - @experience.Company</p>
<p class="fs-6 fw-lighter card-text">@experience.Details</p>
</div>
</div>
}
</ul>
</div>
}
}
else if (isProjects)
{
<div class="row justify-content-center fst-italic fs-1 mt-2">Projects</div>
<div class="overflow-auto" style="max-height: 16rem">
<ul class="list-group list-group-flush">
@foreach (var project in projects)
{
<div class="justify-content-center">
<div class="card p-3 ps-4 pe-4 rounded">
<p class="fs-5 card-title m-0">@project.Title</p>
@foreach (var desc in project.Descriptions)
{
<p class="fs-6 fw-lighter card-text">@((MarkupString)FormatDescription(desc))</p>
}
@if (project.Links?.Any() == true)
{
<div class="mt-2 align-self-end">
@foreach (var link in project.Links)
{
@switch (link.Type)
{
case "external":
<a class="me-2" href="@link.Url">@link.Label</a>
break;
case "internal":
<a class="mt-2 " href=""@link.Url">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-file-code" viewBox="0 0 16 16">
<path d="M6.646 5.646a.5.5 0 1 1 .708.708L5.707 8l1.647 1.646a.5.5 0 0 1-.708.708l-2-2a.5.5 0 0 1 0-.708zm2.708 0a.5.5 0 1 0-.708.708L10.293 8 8.646 9.646a.5.5 0 0 0 .708.708l2-2a.5.5 0 0 0 0-.708z"/>
<path d="M2 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zm10-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1"/>
</svg>
</a>
break;
case "github":
<a class="mt-2" href="@link.Url">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-github" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"/>
</svg>
</a>
break;
}
}
</div>
}
</div>
</div>
}
</ul>
</div>
}
</div>
</div>
</div>
Welcome to your new app.

View File

@ -0,0 +1,53 @@
using Portfolio.Domain.Features.Portfolio;
using static System.Net.WebRequestMethods;
namespace Portfolio.WebUI.Server.Components.Pages
{
partial class Home
{
private List<string> skills;
private List<string> tools;
private List<string> courses;
private List<WorkExperience> experiences;
private List<ProjectEntry> projects;
private bool isExperience = true;
private bool isProjects = false;
protected override async Task OnInitializedAsync()
{
var http = ClientFactory.CreateClient("LocalClient");
skills = await http.GetFromJsonAsync<List<string>>("data/skills.json");
tools = await http.GetFromJsonAsync<List<string>>("data/tools.json");
courses = await http.GetFromJsonAsync<List<string>>("data/courses.json");
experiences = await http.GetFromJsonAsync<List<WorkExperience>>("data/workexperiences.json");
projects = await http.GetFromJsonAsync<List<ProjectEntry>>("data/projects.json");
}
private void ToggleExperience()
{
if (!isExperience)
{
isExperience = true;
isProjects = false;
StateHasChanged();
}
}
private void ToggleProjects()
{
if (!isProjects)
{
isProjects = true;
isExperience = false;
StateHasChanged();
}
}
private string FormatDescription(string desc)
{
// Simple replacement for [text](url) markdown-style links
return System.Text.RegularExpressions.Regex.Replace(desc, @"\[(.+?)\]\((.+?)\)", "<a href=\"$2\">$1</a>");
}
}
}

View File

@ -0,0 +1,6 @@
.mobile-profile {
width: 10em;
height: 10em;
object-fit: cover;
border-radius: 0.5rem;
}

View File

@ -0,0 +1,23 @@
@page "/pokemon-sleep/admincontrol"
<div class="w-100">
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
<PokemonNavMenu />
<hr class="mt-5" />
<!-- Add Pokemon (Wrap in Auth) -->
<button class="btn btn-warning mx-1" style="border-radius: 50px 15px; max-width: 60px;">
<NavLink href="/pokemon-sleep/addmincontrol/add-new-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFF" class="bi bi-plus-circle-fill mb-1" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3z" />
</svg>
</NavLink>
</button>
</div>

View File

@ -0,0 +1,93 @@
@page "/pokemon-sleep/add-new-pokemon"
@inject IPokemonService PokemonService
@inject NavigationManager Navigation
@inject IJSRuntime JS
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Add New Pokémon</PageTitle>
<PokemonNavMenu />
@if (isSubmitting)
{
<p><em>Submitting...</em></p>
}
else
{
<div class="container">
<div class="row mt-3">
<div class="d-flex justify-content-evenly p-0">
<div class="mx-1 align-content-center">
<div class="addcard">
<PokemonForm
formUse="ADD"
OnPokemonReady="ReceivePokemon1"
mostRecentForm=false
/>
</div>
</div>
@if(!pokemon2FormView && !pokemon3FormView)
{
<div class="mx-1 align-content-center">
<PokemonAddButton OnAdd="TogglePokemon2FormView" />
</div>
}
else if (pokemon2FormView && !pokemon3FormView)
{
<div class="mx-1 align-content-center">
<div class="addcard">
<PokemonForm OnPokemonReady="ReceivePokemon2"
formUse="ADD"
RemoveForm="TogglePokemon2FormView"
mostRecentForm="@pokemon2FormView" />
</div>
</div>
<div class="mx-1 align-content-center">
<PokemonAddButton OnAdd="TogglePokemon3FormView" />
</div>
}
else if (!pokemon2FormView && pokemon3FormView)
{
<div class="mx-1 align-content-center">
<div class="addcard">
<PokemonForm OnPokemonReady="ReceivePokemon2"
formUse="ADD"
RemoveForm="TogglePokemon2FormView"
mostRecentForm="@pokemon2FormView" />
</div>
</div>
<div class="mx-1 align-content-center">
<div class="addcard">
<PokemonForm OnPokemonReady="ReceivePokemon3"
formUse="ADD"
RemoveForm="TogglePokemon3FormView"
mostRecentForm="@pokemon3FormView" />
</div>
</div>
}
</div>
</div>
</div>
<div class="d-flex justify-content-center mt-5">
<div class="btn-group">
<button @onclick="@HandleAdd" class="btn btn-primary rounded">Add Pokemon</button>
</div>
</div>
}

View File

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

View File

@ -0,0 +1,5 @@
.addcard {
width: 100%;
max-width: 25rem;
}

View File

@ -0,0 +1,41 @@
@page "/pokemon-sleep/edit-pokemon/{id:int}"
@inject IPokemonService PokemonService
@inject NavigationManager Navigation
@inject IJSRuntime JS
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Edit Pokémon</PageTitle>
<PokemonNavMenu />
@if (pokemon == null)
{
<Loading />
}
else
{
<div class="container mt-5 w-50 mb-5">
<div class="row d-flex flex-row mb-0 justify-content-center">
<div class="w-50">
<div class="editcard">
<PokemonForm
formUse="EDIT"
OnPokemonReady="ReceivePokemon"
RemoveForm="Cancel"
mostRecentForm=true
PokemonToEdit="pokemon"
/>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-center">
<div class="btn-group">
<button @onclick="@HandleSubmit" class="btn btn-primary rounded">Edit Pokemon</button>
</div>
</div>
}

View File

@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{
public partial class PokemonEdit
{
[Parameter] public int Id { get; set; }
private Pokemon? pokemon;
protected override async Task OnInitializedAsync()
{
pokemon = await PokemonService.GetPokemonByIdAsync(Id);
}
private async Task ReceivePokemon(Pokemon p)
{
// Save received pokemon as Pokemon 1
pokemon = p;
// Server console (Blazor Server)
Console.WriteLine($"[Pokemon 1] #{pokemon.PokemonId} {pokemon.PokemonName} | {pokemon.PokemonType} | {pokemon.SleepType} | {pokemon.Speciality} | Var:{pokemon.IsVariation} {pokemon.VariationName}");
// Browser console
await JS.InvokeVoidAsync("console.log", "Pokemon 1:", pokemon);
}
private async Task HandleSubmit()
{
await PokemonService.UpdatePokemonAsync(pokemon);
//Navigation.NavigateTo("/pokemonsleep");
await JS.InvokeVoidAsync("history.back");
}
private async void Cancel()
{
await JS.InvokeVoidAsync("history.back");
}
private bool HideLabel { get; set; } = true;
private void Toggle()
{
HideLabel = !HideLabel;
}
}
}

View File

@ -5,3 +5,8 @@
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;
}
.editcard {
width: 100%;
max-width: 30rem;
}

View File

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

View File

@ -0,0 +1,52 @@
using Microsoft.AspNetCore.Components;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{
public partial class PokemonIndividualView
{
[Parameter] public int Id { get; set; }
private Pokemon? _pokemon;
private Pokemon? _pokemonVariant;
private List<int> _pokemonIds;
private int? _nextPokemonId;
private int? _previousPokemonId;
private int? _variationPokemonId;
private int _currentIndex;
protected override async Task OnParametersSetAsync()
{
_pokemon = await PokemonService.GetPokemonByPokemonIdAsync(Id);
// These can be smart queries if your data is sorted by ID or by another property
_pokemonIds = await PokemonService.GetAllPokemonIdsAsync();
_currentIndex = _pokemonIds.IndexOf(_pokemon.PokemonId);
//Console.WriteLine(_currentIndex);
_nextPokemonId = await PokemonService.GetNextPokemonIdAsync(Id);
_previousPokemonId = await PokemonService.GetPreviousPokemonIdAsync(Id);
_variationPokemonId = await PokemonService.GetVariationPokemonIdAsync(Id);
if (_variationPokemonId != null)
{
Console.WriteLine(_variationPokemonId);
_pokemonVariant = await PokemonService.GetPokemonByIdAsync((int)_variationPokemonId);
Console.WriteLine(_pokemonVariant.VariationName);
}
}
private void NavigateToNext()
{
if (_nextPokemonId.HasValue)
Navigation.NavigateTo($"/pokemon-sleep/pokemon/{_nextPokemonId.Value}");
}
private void NavigateToPrevious()
{
if (_previousPokemonId.HasValue)
Navigation.NavigateTo($"/pokemon-sleep/pokemon/{_previousPokemonId.Value}");
}
}
}

View File

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

View File

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

View File

@ -3,7 +3,7 @@ using Portfolio.Domain.Features.Pokemon;
using Portfolio.Domain.Features.Pokemon_Natures;
using Portfolio.Domain.Features.Pokemon_Subskills;
namespace Portfolio.WebUI.Server.Components.Pages
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{
public partial class PokemonRate
{
@ -58,7 +58,8 @@ namespace Portfolio.WebUI.Server.Components.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

@ -0,0 +1,185 @@
@page "/pokemon-sleep"
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Pokémon Sleep</PageTitle>
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
<PokemonNavMenu />
<!-- Large -->
<div class="container d-none d-lg-block d-md-none card shadow-sm border-0 p-3 pb-4 mt-5 w-50 mb-5 bg-white rounded ">
<!-- Top Row-->
<div class="row d-flex flex-row px-3 mb-0 pb-0">
<!-- Image side -->
<div class="col-4 p-0">
<img class="shadow-sm m-auto img-fluid rounded" src="images/Pokemon_Sleep_ID.jpg" />
</div>
<!-- Text side -->
<div class="col w-50 p-3 ps-4 pb-0">
<div class="row">
<p class="fw-bold fst-italic fs-3 mb-0">Pokémon Sleep? Really?</p>
<p class="fw-normal fs-6 font-monospace">
Yes, really! Pokémon Sleep has become a fun addition to my day since it's release. Scan the QR code in the Pokémon Sleep app to send me a friend request!
</p>
</div>
<div class="row">
<p class="fw-bold fst-italic fs-3 mb-0">But what do you even do?</p>
<p class="fw-normal fs-6 font-monospace">
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>
</div>
<!-- Second Row -->
<div class="row mt-0 pt-0">
<p class="fw-bold fst-italic fs-3 mb-0">Okay, but why a whole section dedicated to Pokémon Sleep?</p>
<p class="fw-normal fs-6 font-monospace">
Well, as it is in any Pokémon game, Natures (and in this case Subskills) matter, amongst other things. This was designed as a helpful tool in assessing new Pokémon acquired from Sleep Research.
</p>
</div>
<!-- Button Row-->
<div class="row d-flex justify-content-evenly mt-3 ">
<button class="btn btn-info rounded-4 d-flex justify-content-center" style="width:12rem;">
<NavLink class="nav-link d-flex align-items-center" href="/pokemon-sleep/pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-table" viewBox="0 0 16 16">
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 2h-4v3h4zm0 4h-4v3h4zm0 4h-4v3h3a1 1 0 0 0 1-1zm-5 3v-3H6v3zm-5 0v-3H1v2a1 1 0 0 0 1 1zm-4-4h4V8H1zm0-4h4V4H1zm5-3v3h4V4zm4 4H6v3h4z" />
</svg>
<span class="ps-1 text-white">Available Pokémon</span>
</NavLink>
</button>
<button class="btn btn-success rounded-4 d-flex justify-content-center" style="width:12rem;">
<NavLink class="nav-link d-flex align-items-center" href="/pokemon-sleep/rate-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFF" class="bi bi-award-fill" viewBox="0 0 16 16">
<path d="m8 0 1.669.864 1.858.282.842 1.68 1.337 1.32L13.4 6l.306 1.854-1.337 1.32-.842 1.68-1.858.282L8 12l-1.669-.864-1.858-.282-.842-1.68-1.337-1.32L2.6 6l-.306-1.854 1.337-1.32.842-1.68L6.331.864z" />
<path d="M4 11.794V16l4-1 4 1v-4.206l-2.018.306L8 13.126 6.018 12.1z" />
</svg>
<span class="ps-1 text-white">Rate Pokémon</span>
</NavLink>
</button>
</div>
</div>
<!-- Medium -->
<div class="container d-none d-lg-none d-md-block mt-3">
<!-- Top Row-->
<div class="row d-flex flex-row justify-content-center">
<!-- Top side -->
<div class="col p-0" >
<img class="shadow-sm m-auto img-fluid rounded" src="images/Pokemon_Sleep_ID.jpg" />
</div>
<div class="col text-wrap p-1 ps-3" >
<div class="row d-flex flex-row justify-content-center">
<!-- Bottom side -->
<p class="fw-bold fst-italic fs-3 mb-1">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. Scan the QR code in the Pokémon Sleep app to send me a friend request!
</p>
</div>
<p class="fw-bold fst-italic fs-3 mb-0">But what do you even do?</p>
<div class="row d-flex flex-row justify-content-center">
<!-- Bottom side -->
<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>
</div>
<!-- Second Row -->
<div class="row d-flex flex-row justify-content-center p-1">
<p class="fw-bold fst-italic fs-3 mb-0">Okay, but why a whole section dedicated to Pokémon Sleep?</p>
<p class="fw-normal fs-6 text-start">
Well, as it is in any Pokémon game, Natures (and in this case Subskills) matter, amongst other things. This was designed as a helpful tool in assessing new Pokémon acquired from Sleep Research.
</p>
</div>
<!-- Button Row-->
<div class="row d-flex justify-content-evenly mt-3 ">
<button class="btn btn-info rounded-4 d-flex justify-content-center" style="width:12rem;">
<NavLink class="nav-link d-flex align-items-center" href="/pokemon-sleep/pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-table" viewBox="0 0 16 16">
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 2h-4v3h4zm0 4h-4v3h4zm0 4h-4v3h3a1 1 0 0 0 1-1zm-5 3v-3H6v3zm-5 0v-3H1v2a1 1 0 0 0 1 1zm-4-4h4V8H1zm0-4h4V4H1zm5-3v3h4V4zm4 4H6v3h4z" />
</svg>
<span class="ps-1 text-white">Available Pokémon</span>
</NavLink>
</button>
<button class="btn btn-success rounded-4 d-flex justify-content-center" style="width:12rem;">
<NavLink class="nav-link d-flex align-items-center" href="/pokemon-sleep/rate-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFF" class="bi bi-award-fill" viewBox="0 0 16 16">
<path d="m8 0 1.669.864 1.858.282.842 1.68 1.337 1.32L13.4 6l.306 1.854-1.337 1.32-.842 1.68-1.858.282L8 12l-1.669-.864-1.858-.282-.842-1.68-1.337-1.32L2.6 6l-.306-1.854 1.337-1.32.842-1.68L6.331.864z" />
<path d="M4 11.794V16l4-1 4 1v-4.206l-2.018.306L8 13.126 6.018 12.1z" />
</svg>
<span class="ps-1 text-white">Rate Pokémon</span>
</NavLink>
</button>
</div>
</div>
<!-- Small -->
<div class="container d-inline d-lg-none d-md-none mt-3 mx-auto">
<!-- Image-->
<div class="row d-flex flex-row justify-content-center">
<!-- Top side -->
<div class="col-6">
<img class="img-fluid rounded p-0" src="images/Pokemon_Sleep_ID.jpg" />
</div>
</div>
<div class="row mt-2 w-75 mx-auto p-1 ps-3">
<p class="fw-bold fst-italic fs-4 mb-1">Pokémon Sleep? Really?</p>
<p class="fw-normal fs-6 font-monospace">
Yes, really! Pokémon Sleep has become a fun addition to my day since it's release. Scan the QR code in the Pokémon Sleep app to send me a friend request!
</p>
<p class="fw-bold fst-italic fs-4 mb-0">But what do you even do?</p>
<p class="fw-normal fs-6 font-monospace">
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>
<p class="fw-bold fst-italic fs-4 mb-0">Okay, but why a whole section dedicated to Pokémon Sleep?</p>
<p class="fw-normal fs-6 font-monospace">
Well, as it is in any Pokémon game, Natures (and in this case Subskills) matter, amongst other things. This was designed as a helpful tool in assessing new Pokémon acquired from Sleep Research.
</p>
</div>
<!-- Button Row-->
<div class="row d-flex justify-content-evenly mt-2">
<button class="btn btn-info rounded-4 d-flex justify-content-center" style="width:12rem;">
<NavLink class="nav-link d-flex align-items-center" href="/pokemon-sleep/pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-table" viewBox="0 0 16 16">
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 2h-4v3h4zm0 4h-4v3h4zm0 4h-4v3h3a1 1 0 0 0 1-1zm-5 3v-3H6v3zm-5 0v-3H1v2a1 1 0 0 0 1 1zm-4-4h4V8H1zm0-4h4V4H1zm5-3v3h4V4zm4 4H6v3h4z" />
</svg>
<span class="ps-1 text-white">Available Pokémon</span>
</NavLink>
</button>
<button class="btn btn-success rounded-4 d-flex justify-content-center" style="width:12rem;">
<NavLink class="nav-link d-flex align-items-center" href="/pokemon-sleep/rate-pokemon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#FFF" class="bi bi-award-fill" viewBox="0 0 16 16">
<path d="m8 0 1.669.864 1.858.282.842 1.68 1.337 1.32L13.4 6l.306 1.854-1.337 1.32-.842 1.68-1.858.282L8 12l-1.669-.864-1.858-.282-.842-1.68-1.337-1.32L2.6 6l-.306-1.854 1.337-1.32.842-1.68L6.331.864z" />
<path d="M4 11.794V16l4-1 4 1v-4.206l-2.018.306L8 13.126 6.018 12.1z" />
</svg>
<span class="ps-1 text-white">Rate Pokémon</span>
</NavLink>
</button>
</div>
</div>

View File

@ -0,0 +1,22 @@
@page "/pokemon-sleep/pokemon"
@inject IPokemonService PokemonService
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Pokémon Sleep</PageTitle>
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
<PokemonNavMenu />
<PokemonTable AllPokemon="pokemons"/>

View File

@ -1,9 +1,9 @@
using Portfolio.Application.Services.PokemonService;
using Portfolio.Domain.Features.Pokemon;
namespace Portfolio.WebUI.Server.Components.Pages
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
{
public partial class PokemonSleep
public partial class PokemonView
{
public List<Pokemon> pokemons = new List<Pokemon>();
public List<string> pokemonImageUrls = new List<string>();

View File

@ -1,102 +0,0 @@
@page "/pokemonsleep/add-new-pokemon"
@inject IPokemonService PokemonService
@inject NavigationManager Navigation
@attribute [StreamRendering]
@rendermode InteractiveServer
<PageTitle>Add New Pokémon</PageTitle>
<PokemonHeader />
@if (isSubmitting)
{
<p><em>Submitting...</em></p>
}
else
{
<div class="w-50 mt-5 m-auto create-container">
<div class="card-header bg-secondary bg-gradient ml-0 py-3">
<div class="row">
<div class="col-12 text-center">
<h2 class="text-info">Add New Pokémon</h2>
</div>
</div>
</div>
<div class="container m-lg-1" >
<EditForm class=" col mb-3" Model="NewPokemon" OnValidSubmit="HandleSubmit">
<DataAnnotationsValidator />
<!-- Section 1 -->
<div class="row ">
<div class="col-sm-3 input-group mb-3 ">
<!-- Pokemon #-->
<span for="PokemonId" class="input-group-text">#</span>
<InputNumber min="0" placeholder="Pokedex #" id="PokemonId" @bind-Value="NewPokemon.PokemonId" class="form-control " required />
<!-- Pokemon Name-->
<InputText placeholder="Pokemon Name" id="PokemonName" @bind-Value="NewPokemon.PokemonName" class="form-control w-75" required />
</div>
</div>
<!-- Section 2 -->
<div class="row">
<div class="input-group mb-3">
<!-- Variation Check -->
<div class=" d-inline-flex">
<InputCheckbox id="IsVariation" @bind-Value="NewPokemon.IsVariation" @onclick="@Toggle" class="form-check-input checkbox-styling p-3 mt-1" />
<span for="IsVariation" class="input-group-text m-1">Variation?</span>
</div>
<!-- Variation Region Input -->
<div class="w-75 mx-2 mt-1">
<InputText placeholder="What Variant? (Alolan, Paldean)" hidden="@HideLabel" id="VariationName" @bind-Value="NewPokemon.VariationName" class="form-control" />
</div>
</div>
</div>
<!-- Section 3 -->
<div class="row mb-3 m-auto" >
<label for="SleepType" class="form-label">Sleep Type</label>
<InputSelect id="SleepType" @bind-Value="NewPokemon.SleepType" class="form-select">
<option hidden value="">Dozing / Snoozing / Slumbering</option>
<option value="Dozing">Dozing</option>
<option value="Snoozing">Snoozing</option>
<option value="Slumbering">Slumbering</option>
</InputSelect>
</div>
<!-- Section 4 -->
<div class="row mb-3 m-auto">
<label for="Speciality" class="form-label">Specialty</label>
<InputSelect id="SleepType" @bind-Value="NewPokemon.Speciality" class="form-select">
<option hidden value="">Berries / Ingredients / Skills</option>
<option value="Berries">Berries</option>
<option value="Ingredients">Ingredients</option>
<option value="Skills">Skills</option>
<option value="Other">Other</option>
</InputSelect>
</div>
<!-- New Image URL Field -->
<div class="row mb-3 m-auto">
<label for="ImageUrl" class="form-label">Base Image URL</label>
<InputText id="ImageUrl" @bind-Value="NewPokemon.PokemonImageUrl" class="form-control" />
</div>
<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>
<button type="submit" class="btn btn-primary mb-3">Add Pokémon</button>
<button type="button" class="btn btn-secondary mb-3" @onclick="@Cancel">Cancel</button>
</EditForm>
</div>
</div>
}

Some files were not shown because too many files have changed in this diff Show More