Added multi-pokemon adding functionality including proper structuring and UI management; renamed some pages for clarity
This commit is contained in:
parent
bc07f56c97
commit
d977e08650
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
@inject IPokemonService PokemonService
|
||||||
|
|
||||||
|
|
||||||
|
<div class="create-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"
|
||||||
|
class="form-control "
|
||||||
|
type="number" />
|
||||||
|
<InputText placeholder="Pokemon Name"
|
||||||
|
id="PokemonName"
|
||||||
|
@bind-Value="NewPokemon.PokemonName"
|
||||||
|
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"
|
||||||
|
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"
|
||||||
|
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" 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" 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" 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" 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" 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" 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-primary rounded p-1 px-0"
|
||||||
|
@onclick="@SendPokemon"
|
||||||
|
disabled="@( !IsComplete )">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-file-arrow-up" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 11a.5.5 0 0 0 .5-.5V6.707l1.146 1.147a.5.5 0 0 0 .708-.708l-2-2a.5.5 0 0 0-.708 0l-2 2a.5.5 0 1 0 .708.708L7.5 6.707V10.5a.5.5 0 0 0 .5.5" />
|
||||||
|
<path d="M4 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 1h8a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
@if(mostRecentForm)
|
||||||
|
{
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-danger rounded p-1 px-0"
|
||||||
|
@onclick="@HandleRemove">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-file-minus" viewBox="0 0 16 16">
|
||||||
|
<path d="M5.5 8a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1H6a.5.5 0 0 1-.5-.5" />
|
||||||
|
<path d="M4 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 1h8a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</EditForm>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
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 PokemonAddForm
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<Pokemon> OnPokemonReady { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback RemoveForm { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public bool mostRecentForm { get; set; }
|
||||||
|
|
||||||
|
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; } = true;
|
||||||
|
private bool showErrors { get; set; } = false;
|
||||||
|
|
||||||
|
private Pokemon NewPokemon = new Pokemon
|
||||||
|
{
|
||||||
|
PokemonId = 0, // Or any default ID logic
|
||||||
|
PokemonName = string.Empty, // Required fields cannot be null
|
||||||
|
SleepType = string.Empty,
|
||||||
|
Speciality = string.Empty,
|
||||||
|
IsVariation = false
|
||||||
|
};
|
||||||
|
|
||||||
|
private void Toggle() => HideLabel = !HideLabel;
|
||||||
|
|
||||||
|
|
||||||
|
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 (!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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
$display-font-sizes: (
|
||||||
|
1: 5rem,
|
||||||
|
2: 4.5rem,
|
||||||
|
3: 4rem,
|
||||||
|
4: 3.5rem,
|
||||||
|
5: 3rem,
|
||||||
|
6: 2.5rem
|
||||||
|
);
|
||||||
|
|
||||||
|
.create-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 390px; /* Prevent it from getting too huge */
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -3,11 +3,9 @@
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<main class="">
|
<main class="">
|
||||||
<NavMenu3 />
|
<NavMenu3 />
|
||||||
<article class="container ">
|
<div class="container">
|
||||||
<div class="">
|
|
||||||
@Body
|
@Body
|
||||||
</div>
|
</div>
|
||||||
</article>
|
|
||||||
</main>
|
</main>
|
||||||
@* <MadeWithLoveFooter/> *@
|
@* <MadeWithLoveFooter/> *@
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
@inject IPokemonService PokemonService
|
@inject IPokemonService PokemonService
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
|
|
||||||
|
@inject IJSRuntime JS
|
||||||
|
|
||||||
@attribute [StreamRendering]
|
@attribute [StreamRendering]
|
||||||
@rendermode InteractiveServer
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
|
@ -17,119 +19,68 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="w-50 mt-5 m-auto create-container bg-info border border-5 border-info-subtle">
|
<div class="container mx-0 px-0">
|
||||||
|
<div class="row mt-5">
|
||||||
|
<div class="d-flex justify-content-evenly h-100 p-0">
|
||||||
|
|
||||||
<div class="card-header rounded-top-3 bg-info py-3">
|
<div class="mx-1 align-content-center">
|
||||||
<div class="row text-center">
|
<div class="addcard">
|
||||||
<h2 class="text-white text-decoration-underline">Add New Pokémon</h2>
|
<PokemonAddForm
|
||||||
|
OnPokemonReady="ReceivePokemon1"
|
||||||
|
mostRecentForm=false
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="p-3" >
|
@if(!pokemon2FormView && !pokemon3FormView)
|
||||||
<EditForm class=" col" Model="NewPokemon" OnValidSubmit="HandleSubmit">
|
{
|
||||||
<DataAnnotationsValidator />
|
<div class="mx-1 align-content-center">
|
||||||
|
<PokemonAddButton OnAdd="TogglePokemon2FormView" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Pokemon Number and Name -->
|
else if (pokemon2FormView && !pokemon3FormView)
|
||||||
<div class="row mt-3">
|
{
|
||||||
<div class="col-sm-3 input-group mb-3">
|
<div class="mx-1 align-content-center">
|
||||||
<!-- Pokemon #-->
|
<div class="addcard">
|
||||||
<span for="PokemonId" class="input-group-text">#</span>
|
<PokemonAddForm OnPokemonReady="ReceivePokemon2"
|
||||||
<InputNumber min="0" placeholder="Pokedex #" id="PokemonId" @bind-Value="NewPokemon.PokemonId" class="form-control " required />
|
RemoveForm="TogglePokemon2FormView"
|
||||||
<!-- Pokemon Name-->
|
mostRecentForm="@pokemon2FormView" />
|
||||||
<InputText placeholder="Pokemon Name" id="PokemonName" @bind-Value="NewPokemon.PokemonName" class="form-control w-75" required />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mx-1 align-content-center">
|
||||||
|
<PokemonAddButton OnAdd="TogglePokemon3FormView" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Pokemon Type, Sleep Type, and Speciality -->
|
else if (!pokemon2FormView && pokemon3FormView)
|
||||||
<div class="row mb-3">
|
{
|
||||||
<!-- Pokemon Type -->
|
<div class="mx-1 align-content-center">
|
||||||
<div class="col m-auto">
|
<div class="addcard">
|
||||||
<label for="PokemonType" class="form-label">Pokemon Type</label>
|
<PokemonAddForm OnPokemonReady="ReceivePokemon2"
|
||||||
<InputSelect id="PokemonType" @bind-Value="NewPokemon.PokemonType" class="form-select">
|
RemoveForm="TogglePokemon2FormView"
|
||||||
<option dsabled value="">Select Type</option>
|
mostRecentForm="@pokemon2FormView" />
|
||||||
<option value="Grass">Grass</option>
|
|
||||||
<option value="Fire">Fire</option>
|
|
||||||
<option value="Water">Water</option>
|
|
||||||
<option value="Normal">Normal</option>
|
|
||||||
<option value="Flying">Flying</option>
|
|
||||||
<option value="Bug">Bug</option>
|
|
||||||
<option value="Poison">Poison</option>
|
|
||||||
<option value="Electric">Electric</option>
|
|
||||||
<option value="Ground">Ground</option>
|
|
||||||
<option value="Rock">Rock</option>
|
|
||||||
<option value="Ice">Ice</option>
|
|
||||||
<option value="Steel">Steel</option>
|
|
||||||
<option value="Fighting">Fighting</option>
|
|
||||||
<option value="Psychic">Psychic</option>
|
|
||||||
<option value="Dark">Dark</option>
|
|
||||||
<option value="Fairy">Fairy</option>
|
|
||||||
<option value="Ghost">Ghost</option>
|
|
||||||
<option value="Dragon">Dragon</option>
|
|
||||||
</InputSelect>
|
|
||||||
</div>
|
|
||||||
<!-- Sleep Type -->
|
|
||||||
<div class="col m-auto">
|
|
||||||
<label for="SleepType" class="form-label">Sleep Type</label>
|
|
||||||
<InputSelect id="SleepType" @bind-Value="NewPokemon.SleepType" class="form-select">
|
|
||||||
<option value="Dozing">Dozing</option>
|
|
||||||
<option value="Snoozing">Snoozing</option>
|
|
||||||
<option value="Slumbering">Slumbering</option>
|
|
||||||
</InputSelect>
|
|
||||||
</div>
|
|
||||||
<!-- Speciality-->
|
|
||||||
<div class="col m-auto">
|
|
||||||
<label for="Speciality" class="form-label">Specialty</label>
|
|
||||||
<InputSelect id="Speciality" @bind-Value="NewPokemon.Speciality" class="form-select">
|
|
||||||
<option value="Berries">Berries</option>
|
|
||||||
<option value="Ingredients">Ingredients</option>
|
|
||||||
<option value="Skills">Skills</option>
|
|
||||||
<option value="All">All</option>
|
|
||||||
</InputSelect>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mx-1 align-content-center">
|
||||||
<!-- If New Pokemon is Variant -->
|
<div class="addcard">
|
||||||
<div class="row mb-3">
|
<PokemonAddForm OnPokemonReady="ReceivePokemon3"
|
||||||
<div class="input-group align-items-center">
|
RemoveForm="TogglePokemon3FormView"
|
||||||
<!-- Variation Check -->
|
mostRecentForm="@pokemon3FormView" />
|
||||||
<div class="d-flex">
|
|
||||||
<InputCheckbox id="IsVariation" @bind-Value="NewPokemon.IsVariation" @onclick="@Toggle" class="form-check-input checkbox-styling p-3 rounded" />
|
|
||||||
<span for="IsVariation" class="input-group-text mx-2">Variation?</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Variation Region Input -->
|
|
||||||
<div class="flex-fill">
|
|
||||||
|
|
||||||
<InputText placeholder="What Variant? (Alolan, Paldean)" hidden="@HideLabel" id="VariationName" @bind-Value="NewPokemon.VariationName" class="form-control" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- New Image URL Field -->
|
|
||||||
<div class="row mb-3 m-auto">
|
|
||||||
<label for="ImageUrl" class="form-label">Base Image URL</label>
|
|
||||||
<InputText id="ImageUrl" @bind-Value="NewPokemon.PokemonImageUrl" class="form-control" />
|
|
||||||
</div>
|
|
||||||
<!-- Shiny Image URL Input -->
|
|
||||||
<div class="row mb-3 m-auto">
|
|
||||||
<label for="ImageUrl" class="form-label">Shiny Image URL</label>
|
|
||||||
<InputText id="ImageUrl" @bind-Value="NewPokemon.PokemonShinyImageUrl" class="form-control" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Flavor Text Input -->
|
|
||||||
<div class="row mb-3 m-auto">
|
|
||||||
<label for="FlavorText" class="form-label">Flavor Text</label>
|
|
||||||
<InputText id="FlavorText" @bind-Value="NewPokemon.FlavorText" class="form-control" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Form Buttons -->
|
|
||||||
<div class="d-flex mt-5 justify-content-between">
|
|
||||||
<button type="button" class="btn btn-danger rounded rouneded-4 mb-3 " @onclick="Cancel">Cancel</button>
|
|
||||||
<button type="submit" class="btn btn-success rounded rouneded-4 mb-3 ">Add Pokemon</button>
|
|
||||||
</div>
|
|
||||||
</EditForm>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button @onclick="@HandleAdd" class="btn btn-primary rounded">Add Pokemon</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,32 @@
|
||||||
using Microsoft.AspNetCore.Components.Web;
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
|
using Microsoft.JSInterop;
|
||||||
using Portfolio.Domain.Features.Pokemon;
|
using Portfolio.Domain.Features.Pokemon;
|
||||||
|
|
||||||
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
|
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
|
||||||
{
|
{
|
||||||
public partial class PokemonCreate
|
public partial class PokemonCreate
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private bool pokemon2FormView { get; set; } = false;
|
||||||
|
private bool pokemon3FormView { get; set; } = false;
|
||||||
|
|
||||||
private bool HideLabel { get; set; } = true;
|
private bool HideLabel { get; set; } = true;
|
||||||
private void Toggle()
|
private void Toggle()
|
||||||
{
|
{
|
||||||
HideLabel = !HideLabel;
|
HideLabel = !HideLabel;
|
||||||
}
|
}
|
||||||
|
private void TogglePokemon2FormView()
|
||||||
|
{
|
||||||
|
pokemon2FormView = !pokemon2FormView;
|
||||||
|
}
|
||||||
|
private void TogglePokemon3FormView()
|
||||||
|
{
|
||||||
|
pokemon3FormView = !pokemon3FormView;
|
||||||
|
TogglePokemon2FormView();
|
||||||
|
|
||||||
private Pokemon NewPokemon = new Pokemon
|
}
|
||||||
|
|
||||||
|
private Pokemon pokemon1, pokemon2, pokemon3 = new Pokemon
|
||||||
{
|
{
|
||||||
PokemonId = 0, // Or any default ID logic
|
PokemonId = 0, // Or any default ID logic
|
||||||
PokemonName = string.Empty, // Required fields cannot be null
|
PokemonName = string.Empty, // Required fields cannot be null
|
||||||
|
|
@ -27,12 +42,61 @@ namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
|
||||||
{
|
{
|
||||||
this.ToggleVariationName = true;
|
this.ToggleVariationName = true;
|
||||||
}
|
}
|
||||||
private async Task HandleSubmit()
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pokemon2FormView)
|
||||||
|
{
|
||||||
|
if (IsComplete(pokemon1) && IsComplete(pokemon2))
|
||||||
|
{
|
||||||
|
await HandleSubmit(pokemon1);
|
||||||
|
await HandleSubmit(pokemon2);
|
||||||
|
Navigation.NavigateTo("/pokemon");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pokemon3FormView)
|
||||||
|
{
|
||||||
|
if (IsComplete(pokemon1) && IsComplete(pokemon2) && IsComplete(pokemon3))
|
||||||
|
{
|
||||||
|
await HandleSubmit(pokemon1);
|
||||||
|
await HandleSubmit(pokemon2);
|
||||||
|
await HandleSubmit(pokemon3);
|
||||||
|
Navigation.NavigateTo("/pokemon");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleSubmit(Pokemon NewPokemon)
|
||||||
{
|
{
|
||||||
isSubmitting = true;
|
isSubmitting = true;
|
||||||
|
|
||||||
await PokemonService.AddPokemonAsync(NewPokemon);
|
await PokemonService.AddPokemonAsync(NewPokemon);
|
||||||
isSubmitting = false;
|
isSubmitting = false;
|
||||||
Navigation.NavigateTo("/pokemon");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Cancel(MouseEventArgs e)
|
protected void Cancel(MouseEventArgs e)
|
||||||
|
|
@ -41,5 +105,41 @@ namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
|
||||||
Navigation.NavigateTo("/pokemon");
|
Navigation.NavigateTo("/pokemon");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
|
.addcard {
|
||||||
.create-container {
|
width: 100%;
|
||||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(255,255, 255, 0.19);
|
max-width: 30rem;
|
||||||
border-radius: 15px;
|
flex: 0 0 auto; /* prevent flex shrink/grow */
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
@page "/pokemon/{id:int}"
|
||||||
|
@inject IPokemonService PokemonService
|
||||||
|
@inject NavigationManager Navigation
|
||||||
|
|
||||||
|
@attribute [StreamRendering]
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
|
||||||
|
<PokemonHeader />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@if (_pokemon == null)
|
||||||
|
{
|
||||||
|
<Loading />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<PageTitle>@_pokemon.PokemonName</PageTitle>
|
||||||
|
<!-- Total Componenet-->
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="w-75 row mt-4 m-auto">
|
||||||
|
|
||||||
|
<!-- Previous Pokemon Button -->
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="mt-1 p-2 btn btn-danger rounded-5 align-self-start text-white" disabled="@(!_previousPokemonId.HasValue)" @onclick="NavigateToPrevious">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-arrow-left-circle-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0m3.5 7.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pokemon Presentation -->
|
||||||
|
<div class="mt-5 mx-5 col justify-content-center">
|
||||||
|
@if (_variationPokemonId != null)
|
||||||
|
{
|
||||||
|
@if (_variationPokemonId != null && _pokemonVariant == null){
|
||||||
|
<Loading />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<!-- If Variation Pokemon have same PokemonId -->
|
||||||
|
@if(_pokemon.Id != _pokemonVariant.Id)
|
||||||
|
{
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<div class="pokemoncard m-1">
|
||||||
|
<PokemonCard Pokemon="_pokemon" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pokemoncard m-1">
|
||||||
|
<PokemonCard Pokemon="_pokemonVariant" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
<!-- If Variation Pokemon has diff PokemonId -->
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="d-flex justify-content-center align-items-center h-100">
|
||||||
|
<div class="pokemoncard">
|
||||||
|
<PokemonCard Pokemon="_pokemonVariant" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
<!-- Standard Pokemon Display -->
|
||||||
|
<div class="d-flex justify-content-center align-items-center h-100">
|
||||||
|
<div class="pokemoncard m-1">
|
||||||
|
<PokemonCard Pokemon="_pokemon" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Next Pokemon Button -->
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="mt-1 p-2 btn btn-danger rounded rounded-5 align-self-start text-white" disabled="@(!_nextPokemonId.HasValue)" @onclick="NavigateToNext">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-arrow-right-circle-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0M4.5 7.5a.5.5 0 0 0 0 1h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
@ -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/{_nextPokemonId.Value}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NavigateToPrevious()
|
||||||
|
{
|
||||||
|
if (_previousPokemonId.HasValue)
|
||||||
|
Navigation.NavigateTo($"/pokemon/{_previousPokemonId.Value}");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -1,23 +1,67 @@
|
||||||
@page "/pokemon"
|
@page "/pokemonsleep"
|
||||||
|
|
||||||
|
|
||||||
@inject IPokemonService PokemonService
|
|
||||||
|
|
||||||
@attribute [StreamRendering]
|
@attribute [StreamRendering]
|
||||||
@rendermode InteractiveServer
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
|
||||||
<PageTitle>Pokémon Sleep</PageTitle>
|
<PageTitle>Pokémon Sleep</PageTitle>
|
||||||
|
|
||||||
|
|
||||||
<div class="w-100">
|
|
||||||
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
|
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
|
||||||
|
|
||||||
<PokemonHeader />
|
<PokemonHeader />
|
||||||
|
|
||||||
<PokemonTable AllPokemon="pokemons"/>
|
<!-- Card -->
|
||||||
|
<div class="container card shadow-sm border-0 p-3 pb-4 mt-4 m-auto w-75 mb-5 bg-white">
|
||||||
|
|
||||||
<hr class="mt-5" />
|
<!-- Top Row-->
|
||||||
|
<div class="row d-flex flex-row">
|
||||||
|
<!-- Image side -->
|
||||||
|
<div class="col-5 border-0 ">
|
||||||
|
<img class="shadow-sm w-100 m-auto img-fluid" src="images/Pokemon_Sleep_ID.jpg" />
|
||||||
|
</div>
|
||||||
|
<!-- Text side -->
|
||||||
|
<div class="col w-50 p-3">
|
||||||
|
<p class="fw-bold fst-italic fs-3">Pokémon Sleep? Really?</p>
|
||||||
|
<p class="fw-normal fs-6 text-start">
|
||||||
|
Yes, really! Pokémon Sleep has become a fun addition to my day since it's release.
|
||||||
|
</p>
|
||||||
|
<p class="fw-bold fst-italic fs-3">But what do you even do?</p>
|
||||||
|
<p class="fw-normal fs-6 text-start">
|
||||||
|
That's harder to explain. See, it isn't as much a game, as it is a gamified sleep tracker. But it's fun to collect the Pokémon and gather their berries and ingredients and create silly little Pokémon-themed foods. Plus, it encourages me to try to get to bed in a timely manner; though, if I'm honest, I definitely put my Pokémon to bed ahead of when I do.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Second Row -->
|
||||||
|
<div class="row mt-3">
|
||||||
|
<p class="fw-bold fst-italic fs-3">Okay, but why a whole section dedicated to Pokémon Sleep?</p>
|
||||||
|
<p class="fw-normal fs-6 text-start">
|
||||||
|
Well, as it is in any Pokémon game, Natures (and in this case Subskills) matter, amongst other things. This was designed as a helpful tool in assessing new Pokémon acquired from Sleep Research.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Button Row-->
|
||||||
|
<div class="d-flex justify-content-evenly mt-4 ">
|
||||||
|
<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">
|
||||||
|
<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="/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>
|
</div>
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
using Portfolio.Application.Services.PokemonService;
|
|
||||||
using Portfolio.Domain.Features.Pokemon;
|
|
||||||
|
|
||||||
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
|
|
||||||
{
|
|
||||||
public partial class PokemonSleep
|
|
||||||
{
|
|
||||||
public List<Pokemon> pokemons = new List<Pokemon>();
|
|
||||||
public List<string> pokemonImageUrls = new List<string>();
|
|
||||||
public List<string> pokemonShinyImageUrls = new List<string>();
|
|
||||||
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
var result = await PokemonService.GetAllPokemonAsync();
|
|
||||||
if (result is not null)
|
|
||||||
{
|
|
||||||
pokemons = result;
|
|
||||||
pokemons.Sort((x, y) => x.PokemonId.CompareTo(y.PokemonId));
|
|
||||||
|
|
||||||
foreach (var pokemon in pokemons)
|
|
||||||
{
|
|
||||||
pokemonImageUrls.Add(pokemon.PokemonImageUrl);
|
|
||||||
pokemonShinyImageUrls.Add(pokemon.PokemonShinyImageUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,93 +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);
|
|
||||||
}
|
|
||||||
|
|
||||||
.front, .back {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
backface-visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back {
|
|
||||||
transform: rotateY(180deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
width: 100px;
|
|
||||||
height: 30px;
|
|
||||||
color: white;
|
|
||||||
padding: 4px 8px;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
border-radius: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statText {
|
|
||||||
position: relative;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dozing {
|
|
||||||
background-color: #fcdc5e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snoozing {
|
|
||||||
background-color: #4ce8ed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slumbering {
|
|
||||||
background-color: #4588fb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.berries {
|
|
||||||
background-color: #24d86b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ingredients {
|
|
||||||
background-color: #fdbe4d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skills {
|
|
||||||
background-color: #47a0fc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-content {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1; /* Higher than stickers */
|
|
||||||
background-color: rgba(255, 255, 255, 0.8); /* Optional translucent bg */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
@page "/pokemonsleep"
|
|
||||||
|
|
||||||
|
|
||||||
@attribute [StreamRendering]
|
|
||||||
@rendermode InteractiveServer
|
|
||||||
|
|
||||||
|
|
||||||
<PageTitle>Pokémon Sleep</PageTitle>
|
|
||||||
|
|
||||||
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
|
|
||||||
<PokemonHeader />
|
|
||||||
|
|
||||||
<!-- Card -->
|
|
||||||
<div class="container card shadow-sm border-0 p-3 pb-4 mt-4 m-auto w-75 mb-5 bg-white">
|
|
||||||
|
|
||||||
<!-- Top Row-->
|
|
||||||
<div class="row d-flex flex-row">
|
|
||||||
<!-- Image side -->
|
|
||||||
<div class="col-5 border-0 ">
|
|
||||||
<img class="shadow-sm w-100 m-auto img-fluid" src="images/Pokemon_Sleep_ID.jpg" />
|
|
||||||
</div>
|
|
||||||
<!-- Text side -->
|
|
||||||
<div class="col w-50 p-3">
|
|
||||||
<p class="fw-bold fst-italic fs-3">Pokémon Sleep? Really?</p>
|
|
||||||
<p class="fw-normal fs-6 text-start">
|
|
||||||
Yes, really! Pokémon Sleep has become a fun addition to my day since it's release.
|
|
||||||
</p>
|
|
||||||
<p class="fw-bold fst-italic fs-3">But what do you even do?</p>
|
|
||||||
<p class="fw-normal fs-6 text-start">
|
|
||||||
That's harder to explain. See, it isn't as much a game, as it is a gamified sleep tracker. But it's fun to collect the Pokémon and gather their berries and ingredients and create silly little Pokémon-themed foods. Plus, it encourages me to try to get to bed in a timely manner; though, if I'm honest, I definitely put my Pokémon to bed ahead of when I do.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Second Row -->
|
|
||||||
<div class="row mt-3">
|
|
||||||
<p class="fw-bold fst-italic fs-3">Okay, but why a whole section dedicated to Pokémon Sleep?</p>
|
|
||||||
<p class="fw-normal fs-6 text-start">
|
|
||||||
Well, as it is in any Pokémon game, Natures (and in this case Subskills) matter, amongst other things. This was designed as a helpful tool in assessing new Pokémon acquired from Sleep Research.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Button Row-->
|
|
||||||
<div class="d-flex justify-content-evenly mt-4 ">
|
|
||||||
<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">
|
|
||||||
<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="/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>
|
|
||||||
|
|
@ -1,93 +1,23 @@
|
||||||
@page "/pokemon/{id:int}"
|
@page "/pokemon"
|
||||||
|
|
||||||
|
|
||||||
@inject IPokemonService PokemonService
|
@inject IPokemonService PokemonService
|
||||||
@inject NavigationManager Navigation
|
|
||||||
|
|
||||||
@attribute [StreamRendering]
|
@attribute [StreamRendering]
|
||||||
@rendermode InteractiveServer
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
|
||||||
|
<PageTitle>Pokémon Sleep</PageTitle>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="w-100">
|
||||||
|
<!-- <PokemonBackground PokemonImages="pokemonImageUrls" ShinyPokemonImages="pokemonShinyImageUrls" /> -->
|
||||||
|
|
||||||
<PokemonHeader />
|
<PokemonHeader />
|
||||||
|
|
||||||
|
<PokemonTable AllPokemon="pokemons"/>
|
||||||
|
|
||||||
|
<hr class="mt-5" />
|
||||||
|
|
||||||
@if (_pokemon == null)
|
|
||||||
{
|
|
||||||
<Loading />
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<PageTitle>@_pokemon.PokemonName</PageTitle>
|
|
||||||
<!-- Total Componenet-->
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="w-75 row mt-4 m-auto">
|
|
||||||
|
|
||||||
<!-- Previous Pokemon Button -->
|
|
||||||
<div class="col-auto">
|
|
||||||
<button class="mt-1 p-2 btn btn-danger rounded-5 align-self-start text-white" disabled="@(!_previousPokemonId.HasValue)" @onclick="NavigateToPrevious">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-arrow-left-circle-fill" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0m3.5 7.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Pokemon Presentation -->
|
|
||||||
<div class="mt-5 mx-5 col justify-content-center">
|
|
||||||
@if (_variationPokemonId != null)
|
|
||||||
{
|
|
||||||
@if (_variationPokemonId != null && _pokemonVariant == null){
|
|
||||||
<Loading />
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<!-- If Variation Pokemon have same PokemonId -->
|
|
||||||
@if(_pokemon.Id != _pokemonVariant.Id)
|
|
||||||
{
|
|
||||||
<div class="d-flex justify-content-center">
|
|
||||||
<div class="pokemoncard m-1">
|
|
||||||
<PokemonCard Pokemon="_pokemon" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="pokemoncard m-1">
|
|
||||||
<PokemonCard Pokemon="_pokemonVariant" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</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>
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,30 @@
|
||||||
using Microsoft.AspNetCore.Components;
|
using Portfolio.Application.Services.PokemonService;
|
||||||
using Portfolio.Domain.Features.Pokemon;
|
using Portfolio.Domain.Features.Pokemon;
|
||||||
|
|
||||||
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
|
namespace Portfolio.WebUI.Server.Components.Pages.Pokemon_Pages
|
||||||
{
|
{
|
||||||
public partial class PokemonView
|
public partial class PokemonView
|
||||||
{
|
{
|
||||||
[Parameter] public int Id { get; set; }
|
public List<Pokemon> pokemons = new List<Pokemon>();
|
||||||
private Pokemon? _pokemon;
|
public List<string> pokemonImageUrls = new List<string>();
|
||||||
private Pokemon? _pokemonVariant;
|
public List<string> pokemonShinyImageUrls = new List<string>();
|
||||||
private List<int> _pokemonIds;
|
|
||||||
private int? _nextPokemonId;
|
|
||||||
private int? _previousPokemonId;
|
|
||||||
private int? _variationPokemonId;
|
|
||||||
private int _currentIndex;
|
|
||||||
|
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
_pokemon = await PokemonService.GetPokemonByPokemonIdAsync(Id);
|
var result = await PokemonService.GetAllPokemonAsync();
|
||||||
|
if (result is not null)
|
||||||
// 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);
|
pokemons = result;
|
||||||
_pokemonVariant = await PokemonService.GetPokemonByIdAsync((int)_variationPokemonId);
|
pokemons.Sort((x, y) => x.PokemonId.CompareTo(y.PokemonId));
|
||||||
Console.WriteLine(_pokemonVariant.VariationName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void NavigateToNext()
|
foreach (var pokemon in pokemons)
|
||||||
{
|
{
|
||||||
if (_nextPokemonId.HasValue)
|
pokemonImageUrls.Add(pokemon.PokemonImageUrl);
|
||||||
Navigation.NavigateTo($"/pokemon/{_nextPokemonId.Value}");
|
pokemonShinyImageUrls.Add(pokemon.PokemonShinyImageUrl);
|
||||||
}
|
|
||||||
|
|
||||||
private void NavigateToPrevious()
|
|
||||||
{
|
|
||||||
if (_previousPokemonId.HasValue)
|
|
||||||
Navigation.NavigateTo($"/pokemon/{_previousPokemonId.Value}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,93 @@
|
||||||
.pokemoncard {
|
|
||||||
width: 100%;
|
.tableFixHead {
|
||||||
max-width: 350px;
|
overflow: auto;
|
||||||
flex: 0 0 auto; /* prevent flex shrink/grow */
|
height: 600px;
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tableFixHead thead th {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flip-container {
|
||||||
|
perspective: 1000px;
|
||||||
|
display: inline-block;
|
||||||
|
width: 90px;
|
||||||
|
height: 90px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flipper {
|
||||||
|
transition: transform 0.6s;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flipped {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.front, .back {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
width: 100px;
|
||||||
|
height: 30px;
|
||||||
|
color: white;
|
||||||
|
padding: 4px 8px;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statText {
|
||||||
|
position: relative;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dozing {
|
||||||
|
background-color: #fcdc5e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snoozing {
|
||||||
|
background-color: #4ce8ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slumbering {
|
||||||
|
background-color: #4588fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.berries {
|
||||||
|
background-color: #24d86b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ingredients {
|
||||||
|
background-color: #fdbe4d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skills {
|
||||||
|
background-color: #47a0fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1; /* Higher than stickers */
|
||||||
|
background-color: rgba(255, 255, 255, 0.8); /* Optional translucent bg */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue