Add frontend creation game (#13)
Reviewed-on: #13
This commit was merged in pull request #13.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
|
||||
<div class="account-setting-container" tabindex="1000">
|
||||
<div class="account-setting-content @(ContentVisile ? string.Empty : "invisible")">
|
||||
@if (!AuthentificationService.IsLogin)
|
||||
@if (!IsLogin)
|
||||
{
|
||||
<EditForm EditContext="EditContext" OnSubmit="HandleLoginSubmit">
|
||||
<FluentValidationValidator />
|
||||
|
||||
@@ -1,28 +1,21 @@
|
||||
using GameIdeas.BlazorApp.Services;
|
||||
using GameIdeas.Shared.Dto;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Shared.Components.Account;
|
||||
|
||||
public partial class AccountSettings (
|
||||
AuthentificationService AuthentificationService)
|
||||
public partial class AccountSettings
|
||||
{
|
||||
private bool ContentVisile = false;
|
||||
private EditContext? EditContext;
|
||||
private LoginDto LoginDto = new();
|
||||
private bool IsLoading = false;
|
||||
private bool IsLogin = true;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
EditContext = new EditContext(LoginDto);
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
ContentVisile = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
ContentVisile = false;
|
||||
@@ -45,15 +38,10 @@ public partial class AccountSettings (
|
||||
IsLoading = true;
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
Close();
|
||||
AuthentificationService.Login();
|
||||
IsLoading = false;
|
||||
|
||||
LoginDto = new();
|
||||
EditContext = new EditContext(LoginDto);
|
||||
}
|
||||
private void HandleLogoutClicked()
|
||||
{
|
||||
Close();
|
||||
AuthentificationService.Logout();
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
}
|
||||
|
||||
.backdrop-filter.overlay {
|
||||
background-color: var(--grey-filter);
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.backdrop-filter.transparent {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
window.setBodyOverflow = (overflow) => {
|
||||
document.getElementsByTagName('html')[0].style.overflow = overflow;
|
||||
document.getElementsByClassName('page')[0].style.overflow = overflow;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
@using GameIdeas.BlazorApp.Shared.Components.BackdropFilter
|
||||
@using GameIdeas.BlazorApp.Shared.Constants
|
||||
|
||||
<CascadingValue Value="this">
|
||||
<div class="popup-wrapper" style="@GetDisplayStyle()">
|
||||
<div class="popup-content">
|
||||
@if (Closable)
|
||||
{
|
||||
<button @onclick="HandleBackdropFilterClicked">@Icons.Shared.Close</button>
|
||||
}
|
||||
@ChildContent
|
||||
</div>
|
||||
</div>
|
||||
</CascadingValue>
|
||||
|
||||
|
||||
<BackdropFilter @ref="BackdropFilter" OnClick="HandleBackdropFilterClicked" CloseOnClick="@Closable"
|
||||
AllowBodyScroll="false" Color="BackdropFilterColor.Overlay" />
|
||||
@@ -0,0 +1,52 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Shared.Components.Popup;
|
||||
|
||||
public partial class Popup
|
||||
{
|
||||
[Parameter] public RenderFragment? ChildContent { get; set; }
|
||||
[Parameter] public bool IsDrawerOpen { get; set; }
|
||||
[Parameter] public EventCallback BackdropFilterClicked { get; set; }
|
||||
[Parameter] public bool Closable { get; set; } = true;
|
||||
public bool IsOpen { get; set; }
|
||||
public EventHandler<bool>? StateChanged { get; set; }
|
||||
|
||||
private BackdropFilter.BackdropFilter? BackdropFilter;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await base.OnParametersSetAsync();
|
||||
if (BackdropFilter?.IsVisible == true && IsOpen)
|
||||
{
|
||||
await BackdropFilter.Hide();
|
||||
}
|
||||
|
||||
if (BackdropFilter?.IsVisible == false && IsOpen)
|
||||
{
|
||||
await BackdropFilter.Show();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Open()
|
||||
{
|
||||
IsOpen = true;
|
||||
await BackdropFilter?.Show()!;
|
||||
StateChanged?.Invoke(null, IsOpen);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task Close()
|
||||
{
|
||||
IsOpen = false;
|
||||
await BackdropFilter?.Hide()!;
|
||||
StateChanged?.Invoke(null, IsOpen);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task HandleBackdropFilterClicked()
|
||||
{
|
||||
await BackdropFilterClicked.InvokeAsync();
|
||||
}
|
||||
|
||||
private string GetDisplayStyle() => IsOpen ? "display: flex;" : "display: none;";
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
.popup-wrapper {
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: var(--index-popup);
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
position: relative;
|
||||
background-color: var(--dropdown-content);
|
||||
padding: 10px;
|
||||
border-radius: var(--big-radius);
|
||||
box-shadow: var(--drop-shadow);
|
||||
}
|
||||
|
||||
.popup-content button {
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
::deep .popup-content button svg {
|
||||
height: 18px;
|
||||
fill: var(--white);
|
||||
}
|
||||
|
||||
.popup-content button:hover {
|
||||
cursor: pointer;
|
||||
background: var(--input-selected);
|
||||
border-radius: var(--small-radius);
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
@using GameIdeas.Shared.Constants
|
||||
@using GameIdeas.BlazorApp.Shared.Constants
|
||||
@using GameIdeas.Shared.Constants
|
||||
|
||||
<div class="search-container">
|
||||
<input @ref=InputText
|
||||
type="text"
|
||||
@@ -13,7 +15,7 @@
|
||||
@if (!string.IsNullOrEmpty(Text))
|
||||
{
|
||||
<div class="clear-icon" @onclick=HandleClearClicked>
|
||||
@ClearIcon
|
||||
@Icons.Shared.Close;
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using GameIdeas.BlazorApp.Shared.Constants;
|
||||
using GameIdeas.Shared.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
@@ -14,7 +15,6 @@ public partial class SearchInput
|
||||
[Parameter] public SearchInputIcon Icon { get; set; }
|
||||
|
||||
private ElementReference InputText;
|
||||
private readonly MarkupString ClearIcon = new(Icons.Search.Clear);
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
@@ -52,8 +52,8 @@ public partial class SearchInput
|
||||
{
|
||||
return Icon switch
|
||||
{
|
||||
SearchInputIcon.Dropdown => new MarkupString(Icons.Search.Triangle),
|
||||
SearchInputIcon.Search => new MarkupString(Icons.Search.Glass),
|
||||
SearchInputIcon.Dropdown => Icons.Search.Triangle,
|
||||
SearchInputIcon.Search => Icons.Search.Glass,
|
||||
_ => new MarkupString()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
background: none !important;
|
||||
color: var(--white);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
namespace GameIdeas.BlazorApp.Shared.Components.Select.Models;
|
||||
|
||||
public class SelectElement<TItem>
|
||||
public class SelectElement<TItem>(TItem item, string? label)
|
||||
{
|
||||
public TItem? Item { get; set; }
|
||||
public string? Label { get; set; }
|
||||
public TItem Item { get; set; } = item;
|
||||
public string? Label { get; set; } = label;
|
||||
public bool IsSelected { get; set; } = false;
|
||||
public bool IsNew { get; set; } = false;
|
||||
}
|
||||
|
||||
@@ -5,5 +5,6 @@ public enum SelectListTheme
|
||||
Navigation,
|
||||
Sort,
|
||||
Filter,
|
||||
AdvancedFilter
|
||||
AdvancedFilter,
|
||||
Creation
|
||||
}
|
||||
|
||||
@@ -30,14 +30,22 @@
|
||||
margin: 2px 6px;
|
||||
border-bottom: 2px solid var(--input-selected);
|
||||
}
|
||||
|
||||
/* Advanced filter */
|
||||
::deep .select-button.advancedfilter .search-container {
|
||||
height: 24px;
|
||||
background: var(--input-secondary);
|
||||
}
|
||||
|
||||
::deep .select-button.advancedfilter .search-container input::placeholder {
|
||||
color: #bbb;
|
||||
::deep .select-button.advancedfilter .search-container input::placeholder {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
/* Creation */
|
||||
::deep .select-button.creation .search-container {
|
||||
height: 24px;
|
||||
background: var(--input-secondary);
|
||||
border: solid 1px var(--input-selected);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* width */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@using GameIdeas.BlazorApp.Shared.Components.BackdropFilter
|
||||
@using GameIdeas.BlazorApp.Shared.Components.Select.Components
|
||||
@typeparam TItem
|
||||
@typeparam TItem
|
||||
@typeparam THeader
|
||||
|
||||
<div class="select-list">
|
||||
<div class="select-button" @onclick=HandleButtonClicked>
|
||||
@@ -11,10 +12,10 @@
|
||||
@if (IsContentOpen)
|
||||
{
|
||||
<div class="select-content @(Enum.GetName(Theme)?.ToLower())">
|
||||
@foreach (var item in Headers)
|
||||
@foreach (var header in Headers)
|
||||
{
|
||||
<SelectListElement TItem="TItem"
|
||||
Value="item"
|
||||
<SelectListElement TItem="THeader"
|
||||
Value="header"
|
||||
ValueChanged="HandleHeaderClicked"
|
||||
Theme="Theme" />
|
||||
}
|
||||
|
||||
@@ -3,24 +3,25 @@ using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Shared.Components.Select;
|
||||
|
||||
public partial class SelectList<TItem>
|
||||
public partial class SelectList<TItem, THeader>
|
||||
{
|
||||
[Parameter] public RenderFragment? ChildContent { get; set; }
|
||||
[Parameter] public TItem? Value { get; set; }
|
||||
[Parameter] public EventCallback<TItem?> ValueChanged { get; set; }
|
||||
[Parameter] public TItem? Header { get; set; }
|
||||
[Parameter] public EventCallback<TItem?> HeaderChanged { get; set; }
|
||||
[Parameter] public THeader? Header { get; set; }
|
||||
[Parameter] public EventCallback<THeader?> HeaderChanged { get; set; }
|
||||
[Parameter] public IEnumerable<SelectElement<TItem>> Items { get; set; } = [];
|
||||
[Parameter] public IEnumerable<SelectElement<TItem>> Headers { get; set; } = [];
|
||||
[Parameter] public IEnumerable<SelectElement<THeader>> Headers { get; set; } = [];
|
||||
[Parameter] public SelectListTheme Theme { get; set; }
|
||||
[Parameter] public bool AlignRight { get; set; }
|
||||
|
||||
private bool IsContentOpen = false;
|
||||
|
||||
private void HandleButtonClicked()
|
||||
{
|
||||
public void Close() =>
|
||||
IsContentOpen = false;
|
||||
|
||||
private void HandleButtonClicked() =>
|
||||
IsContentOpen = !IsContentOpen;
|
||||
}
|
||||
|
||||
private void HandleContentClosed()
|
||||
{
|
||||
@@ -40,7 +41,7 @@ public partial class SelectList<TItem>
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
|
||||
private async Task HandleHeaderClicked(SelectElement<TItem> selectedValue)
|
||||
private async Task HandleHeaderClicked(SelectElement<THeader> selectedValue)
|
||||
{
|
||||
foreach (var header in Headers)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<div class="container">
|
||||
<div class="slider-track"></div>
|
||||
|
||||
<input type="range" id="min-range" style="@StatusColor(Value)" min="@Params.Min" max="@Params.Max"
|
||||
@bind="@Value" @bind:event="oninput" @bind:after=HandleSlideOnInput />
|
||||
|
||||
<div class="values">
|
||||
<span class="value">@Params.Min</span>
|
||||
<span class="value">@Params.Max</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Shared.Components.Slider;
|
||||
|
||||
public partial class Slider
|
||||
{
|
||||
[Parameter] public SliderParams Params { get; set; } = new();
|
||||
[Parameter] public int Value { get; set; }
|
||||
[Parameter] public EventCallback<int> ValueChanged { get; set; }
|
||||
|
||||
private async Task HandleSlideOnInput()
|
||||
{
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
|
||||
private string StatusColor(int value)
|
||||
{
|
||||
string str = "--thumb-color: var({0});";
|
||||
|
||||
int firstTier = (int)Math.Floor(0.33 * Params.Max);
|
||||
int secondTier = (int)Math.Ceiling(0.66 * Params.Max);
|
||||
|
||||
return value switch
|
||||
{
|
||||
int x when x <= firstTier => string.Format(str, "--red"),
|
||||
int x when x >= secondTier => string.Format(str, "--green"),
|
||||
_ => string.Format(str, "--yellow"),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
.container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: var(--index-component)
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.slider-track {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
margin: auto;
|
||||
border-radius: 2px;
|
||||
background: var(--input-primary);
|
||||
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-runnable-track {
|
||||
-webkit-appearance: none;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
input[type="range"]::-moz-range-track {
|
||||
-moz-appearance: none;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
input[type="range"]::-ms-track {
|
||||
appearance: none;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
background-color: var(--thumb-color);
|
||||
cursor: pointer;
|
||||
margin-top: -6px;
|
||||
pointer-events: auto;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input[type="range"]::-moz-range-thumb {
|
||||
-webkit-appearance: none;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
background-color: var(--thumb-color);
|
||||
pointer-events: auto;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input[type="range"]::-ms-thumb {
|
||||
appearance: none;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
background-color: var(--thumb-color);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.values {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
margin-top: 2px;
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.value {
|
||||
width: 1em;
|
||||
text-align:center;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace GameIdeas.BlazorApp.Shared.Components.Slider;
|
||||
|
||||
public class SliderParams
|
||||
{
|
||||
public int Min{ get; set; }
|
||||
public int Max { get; set; }
|
||||
public int Gap { get; set; } = 0;
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
<div class="container">
|
||||
<div class="slider-track" style="@FillColor()"></div>
|
||||
|
||||
<input type="range" id="min-range" style="@StatusColor(Params.ValueMin)" min="@Params.Min" max="@Params.Max"
|
||||
@bind="@Params.ValueMin" @bind:event="oninput" @bind:after=HandleSlideOnInput />
|
||||
<input type="range" id="max-range" style="@StatusColor(Params.ValueMax)" min="@Params.Min" max="@Params.Max"
|
||||
@bind="@Params.ValueMax" @bind:event="oninput" @bind:after=HandleSlideTwoInput />
|
||||
<input type="range" id="min-range" style="@StatusColor(Min)" min="@Params.Min" max="@Params.Max"
|
||||
@bind="@Min" @bind:event="oninput" @bind:after=HandleSlideOnInput />
|
||||
<input type="range" id="max-range" style="@StatusColor(Max)" min="@Params.Min" max="@Params.Max"
|
||||
@bind="@Max" @bind:event="oninput" @bind:after=HandleSlideTwoInput />
|
||||
|
||||
<div class="values">
|
||||
<span class="value">@Params.ValueMin</span>
|
||||
<span class="value">@Params.ValueMax</span>
|
||||
<span class="value">@Min</span>
|
||||
<span class="value">@Max</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,30 +13,28 @@ public partial class SliderRange
|
||||
|
||||
private async Task HandleSlideTwoInput()
|
||||
{
|
||||
if (Params.ValueMax - Params.ValueMin <= Params.Gap)
|
||||
if (Max - Min <= Params.Gap)
|
||||
{
|
||||
Params.ValueMin = Params.ValueMax - Params.Gap;
|
||||
Min = Max - Params.Gap;
|
||||
}
|
||||
|
||||
Max = Params.Max;
|
||||
await MaxChanged.InvokeAsync(Params.Max);
|
||||
await MaxChanged.InvokeAsync(Max);
|
||||
}
|
||||
|
||||
private async Task HandleSlideOnInput()
|
||||
{
|
||||
if (Params.ValueMax - Params.ValueMin <= Params.Gap)
|
||||
if (Max - Min <= Params.Gap)
|
||||
{
|
||||
Params.ValueMax = Params.ValueMin + Params.Gap;
|
||||
Max = Min + Params.Gap;
|
||||
}
|
||||
|
||||
Min = Params.Min;
|
||||
await MinChanged.InvokeAsync(Params.Min);
|
||||
await MinChanged.InvokeAsync(Min);
|
||||
}
|
||||
|
||||
private string FillColor()
|
||||
{
|
||||
var percent1 = (double)(Params.ValueMin - Params.Min) / (Params.Max - Params.Min) * 100;
|
||||
var percent2 = (double)(Params.ValueMax - Params.Min) / (Params.Max - Params.Min) * 100;
|
||||
var percent1 = (double)(Min - Params.Min) / (Params.Max - Params.Min) * 100;
|
||||
var percent2 = (double)(Max - Params.Min) / (Params.Max - Params.Min) * 100;
|
||||
return $"background: linear-gradient(to right, var(--line) {percent1}% , var(--violet) {percent1}% , var(--violet) {percent2}%, var(--line) {percent2}%)";
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,5 @@ public class SliderRangeParams
|
||||
{
|
||||
public int Min{ get; set; }
|
||||
public int Max { get; set; }
|
||||
public int ValueMin { get; set; }
|
||||
public int ValueMax { get; set; }
|
||||
public int Gap { get; set; } = 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace GameIdeas.BlazorApp.Shared.Constants;
|
||||
|
||||
public static class Endpoints
|
||||
{
|
||||
public static class Game
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static class Category
|
||||
{
|
||||
public static readonly string AllCategories = "api/Category/All";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Shared.Constants;
|
||||
|
||||
public static class Icons
|
||||
{
|
||||
private const string OpenBraket = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">";
|
||||
private const string CloseBraket = "</svg>";
|
||||
|
||||
|
||||
public static class Search
|
||||
{
|
||||
public readonly static MarkupString Glass = new(OpenBraket +
|
||||
"<path d=\"M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z\" />" +
|
||||
CloseBraket);
|
||||
|
||||
public readonly static MarkupString Triangle = new(OpenBraket +
|
||||
"<path d=\"M1 3H23L12 22\" />" +
|
||||
CloseBraket);
|
||||
}
|
||||
|
||||
public static class Shared
|
||||
{
|
||||
public readonly static MarkupString Close = new(OpenBraket +
|
||||
"<path d=\"M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z\" />" +
|
||||
CloseBraket);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace GameIdeas.BlazorApp.Shared.Exceptions;
|
||||
|
||||
public class CategoryNotFoundException(string message) : Exception(message);
|
||||
Reference in New Issue
Block a user