Shady nagy/net6 (#614)
* udated to .net6 * used the .net6 version RC2 * added editconfig. * App core new Scoped Namespaces style. * BlazorAdmin new Scoped Namespaces style. * Blazor Shared new Scoped Namespaces style. * Infra new Scoped Namespaces style. * public api new Scoped Namespaces style. * web new Scoped Namespaces style. * FunctionalTests new Scoped Namespaces style. * Integrational tests new Scoped Namespaces style. * unit tests new Scoped Namespaces style. * update github action. * update github action. * change the global.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,87 +1,86 @@
|
||||
using BlazorShared.Authorization;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorShared.Authorization;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BlazorAdmin
|
||||
namespace BlazorAdmin;
|
||||
|
||||
public class CustomAuthStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
public class CustomAuthStateProvider : AuthenticationStateProvider
|
||||
// TODO: Get Default Cache Duration from Config
|
||||
private static readonly TimeSpan UserCacheRefreshInterval = TimeSpan.FromSeconds(60);
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<CustomAuthStateProvider> _logger;
|
||||
|
||||
private DateTimeOffset _userLastCheck = DateTimeOffset.FromUnixTimeSeconds(0);
|
||||
private ClaimsPrincipal _cachedUser = new ClaimsPrincipal(new ClaimsIdentity());
|
||||
|
||||
public CustomAuthStateProvider(HttpClient httpClient,
|
||||
ILogger<CustomAuthStateProvider> logger)
|
||||
{
|
||||
// TODO: Get Default Cache Duration from Config
|
||||
private static readonly TimeSpan UserCacheRefreshInterval = TimeSpan.FromSeconds(60);
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<CustomAuthStateProvider> _logger;
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
return new AuthenticationState(await GetUser(useCache: true));
|
||||
}
|
||||
|
||||
private DateTimeOffset _userLastCheck = DateTimeOffset.FromUnixTimeSeconds(0);
|
||||
private ClaimsPrincipal _cachedUser = new ClaimsPrincipal(new ClaimsIdentity());
|
||||
|
||||
public CustomAuthStateProvider(HttpClient httpClient,
|
||||
ILogger<CustomAuthStateProvider> logger)
|
||||
private async ValueTask<ClaimsPrincipal> GetUser(bool useCache = false)
|
||||
{
|
||||
var now = DateTimeOffset.Now;
|
||||
if (useCache && now < _userLastCheck + UserCacheRefreshInterval)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
return new AuthenticationState(await GetUser(useCache: true));
|
||||
}
|
||||
|
||||
private async ValueTask<ClaimsPrincipal> GetUser(bool useCache = false)
|
||||
{
|
||||
var now = DateTimeOffset.Now;
|
||||
if (useCache && now < _userLastCheck + UserCacheRefreshInterval)
|
||||
{
|
||||
return _cachedUser;
|
||||
}
|
||||
|
||||
_cachedUser = await FetchUser();
|
||||
_userLastCheck = now;
|
||||
|
||||
return _cachedUser;
|
||||
}
|
||||
|
||||
private async Task<ClaimsPrincipal> FetchUser()
|
||||
_cachedUser = await FetchUser();
|
||||
_userLastCheck = now;
|
||||
|
||||
return _cachedUser;
|
||||
}
|
||||
|
||||
private async Task<ClaimsPrincipal> FetchUser()
|
||||
{
|
||||
UserInfo user = null;
|
||||
|
||||
try
|
||||
{
|
||||
UserInfo user = null;
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Fetching user details from web api.");
|
||||
user = await _httpClient.GetFromJsonAsync<UserInfo>("User");
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
_logger.LogWarning(exc, "Fetching user failed.");
|
||||
}
|
||||
|
||||
if (user == null || !user.IsAuthenticated)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var identity = new ClaimsIdentity(
|
||||
nameof(CustomAuthStateProvider),
|
||||
user.NameClaimType,
|
||||
user.RoleClaimType);
|
||||
|
||||
if (user.Claims != null)
|
||||
{
|
||||
foreach (var claim in user.Claims)
|
||||
{
|
||||
identity.AddClaim(new Claim(claim.Type, claim.Value));
|
||||
}
|
||||
}
|
||||
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Token);
|
||||
|
||||
return new ClaimsPrincipal(identity);
|
||||
_logger.LogInformation("Fetching user details from web api.");
|
||||
user = await _httpClient.GetFromJsonAsync<UserInfo>("User");
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
_logger.LogWarning(exc, "Fetching user failed.");
|
||||
}
|
||||
|
||||
if (user == null || !user.IsAuthenticated)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var identity = new ClaimsIdentity(
|
||||
nameof(CustomAuthStateProvider),
|
||||
user.NameClaimType,
|
||||
user.RoleClaimType);
|
||||
|
||||
if (user.Claims != null)
|
||||
{
|
||||
foreach (var claim in user.Claims)
|
||||
{
|
||||
identity.AddClaim(new Claim(claim.Type, claim.Value));
|
||||
}
|
||||
}
|
||||
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Token);
|
||||
|
||||
return new ClaimsPrincipal(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace BlazorAdmin.Helpers
|
||||
namespace BlazorAdmin.Helpers;
|
||||
|
||||
public class BlazorComponent : ComponentBase
|
||||
{
|
||||
public class BlazorComponent : ComponentBase
|
||||
private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_refresh.RefreshRequested += DoRefresh;
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
public void CallRequestRefresh()
|
||||
{
|
||||
_refresh.CallRequestRefresh();
|
||||
}
|
||||
|
||||
private void DoRefresh()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
_refresh.RefreshRequested += DoRefresh;
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
public void CallRequestRefresh()
|
||||
{
|
||||
_refresh.CallRequestRefresh();
|
||||
}
|
||||
|
||||
private void DoRefresh()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace BlazorAdmin.Helpers
|
||||
namespace BlazorAdmin.Helpers;
|
||||
|
||||
public class BlazorLayoutComponent : LayoutComponentBase
|
||||
{
|
||||
public class BlazorLayoutComponent : LayoutComponentBase
|
||||
private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance;
|
||||
_refresh.RefreshRequested += DoRefresh;
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_refresh.RefreshRequested += DoRefresh;
|
||||
base.OnInitialized();
|
||||
}
|
||||
public void CallRequestRefresh()
|
||||
{
|
||||
_refresh.CallRequestRefresh();
|
||||
}
|
||||
|
||||
public void CallRequestRefresh()
|
||||
{
|
||||
_refresh.CallRequestRefresh();
|
||||
}
|
||||
|
||||
private void DoRefresh()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
private void DoRefresh()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace BlazorAdmin.Helpers
|
||||
namespace BlazorAdmin.Helpers;
|
||||
|
||||
internal sealed class RefreshBroadcast
|
||||
{
|
||||
internal sealed class RefreshBroadcast
|
||||
private static readonly Lazy<RefreshBroadcast>
|
||||
Lazy =
|
||||
new Lazy<RefreshBroadcast>
|
||||
(() => new RefreshBroadcast());
|
||||
|
||||
public static RefreshBroadcast Instance => Lazy.Value;
|
||||
|
||||
private RefreshBroadcast()
|
||||
{
|
||||
private static readonly Lazy<RefreshBroadcast>
|
||||
Lazy =
|
||||
new Lazy<RefreshBroadcast>
|
||||
(() => new RefreshBroadcast());
|
||||
}
|
||||
|
||||
public static RefreshBroadcast Instance => Lazy.Value;
|
||||
|
||||
private RefreshBroadcast()
|
||||
{
|
||||
}
|
||||
|
||||
public event Action RefreshRequested;
|
||||
public void CallRequestRefresh()
|
||||
{
|
||||
RefreshRequested?.Invoke();
|
||||
}
|
||||
public event Action RefreshRequested;
|
||||
public void CallRequestRefresh()
|
||||
{
|
||||
RefreshRequested?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,88 +1,87 @@
|
||||
using BlazorAdmin.Services;
|
||||
using System;
|
||||
using BlazorAdmin.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
|
||||
namespace BlazorAdmin.Helpers
|
||||
namespace BlazorAdmin.Helpers;
|
||||
|
||||
public class ToastComponent : ComponentBase, IDisposable
|
||||
{
|
||||
public class ToastComponent : ComponentBase, IDisposable
|
||||
[Inject]
|
||||
ToastService ToastService
|
||||
{
|
||||
[Inject]
|
||||
ToastService ToastService
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string Heading
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string Message
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected bool IsVisible
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string BackgroundCssClass
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string IconCssClass
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
ToastService.OnShow += ShowToast;
|
||||
ToastService.OnHide += HideToast;
|
||||
}
|
||||
private void ShowToast(string message, ToastLevel level)
|
||||
{
|
||||
BuildToastSettings(level, message);
|
||||
IsVisible = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
private void HideToast()
|
||||
{
|
||||
IsVisible = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
private void BuildToastSettings(ToastLevel level, string message)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string Heading
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string Message
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected bool IsVisible
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string BackgroundCssClass
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string IconCssClass
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
ToastService.OnShow += ShowToast;
|
||||
ToastService.OnHide += HideToast;
|
||||
}
|
||||
private void ShowToast(string message, ToastLevel level)
|
||||
{
|
||||
BuildToastSettings(level, message);
|
||||
IsVisible = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
private void HideToast()
|
||||
{
|
||||
IsVisible = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
private void BuildToastSettings(ToastLevel level, string message)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case ToastLevel.Info:
|
||||
BackgroundCssClass = "bg-info";
|
||||
IconCssClass = "info";
|
||||
Heading = "Info";
|
||||
break;
|
||||
case ToastLevel.Success:
|
||||
BackgroundCssClass = "bg-success";
|
||||
IconCssClass = "check";
|
||||
Heading = "Success";
|
||||
break;
|
||||
case ToastLevel.Warning:
|
||||
BackgroundCssClass = "bg-warning";
|
||||
IconCssClass = "exclamation";
|
||||
Heading = "Warning";
|
||||
break;
|
||||
case ToastLevel.Error:
|
||||
BackgroundCssClass = "bg-danger";
|
||||
IconCssClass = "times";
|
||||
Heading = "Error";
|
||||
break;
|
||||
}
|
||||
Message = message;
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
ToastService.OnShow -= ShowToast;
|
||||
case ToastLevel.Info:
|
||||
BackgroundCssClass = "bg-info";
|
||||
IconCssClass = "info";
|
||||
Heading = "Info";
|
||||
break;
|
||||
case ToastLevel.Success:
|
||||
BackgroundCssClass = "bg-success";
|
||||
IconCssClass = "check";
|
||||
Heading = "Success";
|
||||
break;
|
||||
case ToastLevel.Warning:
|
||||
BackgroundCssClass = "bg-warning";
|
||||
IconCssClass = "exclamation";
|
||||
Heading = "Warning";
|
||||
break;
|
||||
case ToastLevel.Error:
|
||||
BackgroundCssClass = "bg-danger";
|
||||
IconCssClass = "times";
|
||||
Heading = "Error";
|
||||
break;
|
||||
}
|
||||
Message = message;
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
ToastService.OnShow -= ShowToast;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
using Microsoft.JSInterop;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BlazorAdmin.JavaScript
|
||||
namespace BlazorAdmin.JavaScript;
|
||||
|
||||
public class Cookies
|
||||
{
|
||||
public class Cookies
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
|
||||
public Cookies(IJSRuntime jsRuntime)
|
||||
{
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public Cookies(IJSRuntime jsRuntime)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
public async Task DeleteCookie(string name)
|
||||
{
|
||||
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.DeleteCookie, name);
|
||||
}
|
||||
|
||||
public async Task DeleteCookie(string name)
|
||||
{
|
||||
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.DeleteCookie, name);
|
||||
}
|
||||
|
||||
public async Task<string> GetCookie(string name)
|
||||
{
|
||||
return await _jsRuntime.InvokeAsync<string>(JSInteropConstants.GetCookie, name);
|
||||
}
|
||||
public async Task<string> GetCookie(string name)
|
||||
{
|
||||
return await _jsRuntime.InvokeAsync<string>(JSInteropConstants.GetCookie, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
using Microsoft.JSInterop;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BlazorAdmin.JavaScript
|
||||
namespace BlazorAdmin.JavaScript;
|
||||
|
||||
public class Css
|
||||
{
|
||||
public class Css
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
|
||||
public Css(IJSRuntime jsRuntime)
|
||||
{
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public Css(IJSRuntime jsRuntime)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public async Task ShowBodyOverflow()
|
||||
{
|
||||
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.ShowBodyOverflow);
|
||||
}
|
||||
public async Task ShowBodyOverflow()
|
||||
{
|
||||
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.ShowBodyOverflow);
|
||||
}
|
||||
|
||||
public async Task<string> HideBodyOverflow()
|
||||
{
|
||||
return await _jsRuntime.InvokeAsync<string>(JSInteropConstants.HideBodyOverflow);
|
||||
}
|
||||
public async Task<string> HideBodyOverflow()
|
||||
{
|
||||
return await _jsRuntime.InvokeAsync<string>(JSInteropConstants.HideBodyOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
namespace BlazorAdmin.JavaScript
|
||||
namespace BlazorAdmin.JavaScript;
|
||||
|
||||
public static class JSInteropConstants
|
||||
{
|
||||
public static class JSInteropConstants
|
||||
{
|
||||
public static string DeleteCookie => "deleteCookie";
|
||||
public static string GetCookie => "getCookie";
|
||||
public static string RouteOutside => "routeOutside";
|
||||
public static string HideBodyOverflow => "hideBodyOverflow";
|
||||
public static string ShowBodyOverflow => "showBodyOverflow";
|
||||
}
|
||||
public static string DeleteCookie => "deleteCookie";
|
||||
public static string GetCookie => "getCookie";
|
||||
public static string RouteOutside => "routeOutside";
|
||||
public static string HideBodyOverflow => "hideBodyOverflow";
|
||||
public static string ShowBodyOverflow => "showBodyOverflow";
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
using Microsoft.JSInterop;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BlazorAdmin.JavaScript
|
||||
namespace BlazorAdmin.JavaScript;
|
||||
|
||||
public class Route
|
||||
{
|
||||
public class Route
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
|
||||
public Route(IJSRuntime jsRuntime)
|
||||
{
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public Route(IJSRuntime jsRuntime)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public async Task RouteOutside(string path)
|
||||
{
|
||||
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.RouteOutside, path);
|
||||
}
|
||||
public async Task RouteOutside(string path)
|
||||
{
|
||||
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.RouteOutside, path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,68 @@
|
||||
using BlazorAdmin.Helpers;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorAdmin.Helpers;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlazorAdmin.Pages.CatalogItemPage
|
||||
namespace BlazorAdmin.Pages.CatalogItemPage;
|
||||
|
||||
public partial class List : BlazorComponent
|
||||
{
|
||||
public partial class List : BlazorComponent
|
||||
[Microsoft.AspNetCore.Components.Inject]
|
||||
public ICatalogItemService CatalogItemService { get; set; }
|
||||
|
||||
[Microsoft.AspNetCore.Components.Inject]
|
||||
public ICatalogLookupDataService<CatalogBrand> CatalogBrandService { get; set; }
|
||||
|
||||
[Microsoft.AspNetCore.Components.Inject]
|
||||
public ICatalogLookupDataService<CatalogType> CatalogTypeService { get; set; }
|
||||
|
||||
private List<CatalogItem> catalogItems = new List<CatalogItem>();
|
||||
private List<CatalogType> catalogTypes = new List<CatalogType>();
|
||||
private List<CatalogBrand> catalogBrands = new List<CatalogBrand>();
|
||||
|
||||
private Edit EditComponent { get; set; }
|
||||
private Delete DeleteComponent { get; set; }
|
||||
private Details DetailsComponent { get; set; }
|
||||
private Create CreateComponent { get; set; }
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
[Microsoft.AspNetCore.Components.Inject]
|
||||
public ICatalogItemService CatalogItemService { get; set; }
|
||||
|
||||
[Microsoft.AspNetCore.Components.Inject]
|
||||
public ICatalogLookupDataService<CatalogBrand> CatalogBrandService { get; set; }
|
||||
|
||||
[Microsoft.AspNetCore.Components.Inject]
|
||||
public ICatalogLookupDataService<CatalogType> CatalogTypeService { get; set; }
|
||||
|
||||
private List<CatalogItem> catalogItems = new List<CatalogItem>();
|
||||
private List<CatalogType> catalogTypes = new List<CatalogType>();
|
||||
private List<CatalogBrand> catalogBrands = new List<CatalogBrand>();
|
||||
|
||||
private Edit EditComponent { get; set; }
|
||||
private Delete DeleteComponent { get; set; }
|
||||
private Details DetailsComponent { get; set; }
|
||||
private Create CreateComponent { get; set; }
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
catalogItems = await CatalogItemService.List();
|
||||
catalogTypes = await CatalogTypeService.List();
|
||||
catalogBrands = await CatalogBrandService.List();
|
||||
|
||||
CallRequestRefresh();
|
||||
}
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
private async void DetailsClick(int id)
|
||||
{
|
||||
await DetailsComponent.Open(id);
|
||||
}
|
||||
|
||||
private async Task CreateClick()
|
||||
{
|
||||
await CreateComponent.Open();
|
||||
}
|
||||
|
||||
private async Task EditClick(int id)
|
||||
{
|
||||
await EditComponent.Open(id);
|
||||
}
|
||||
|
||||
private async Task DeleteClick(int id)
|
||||
{
|
||||
await DeleteComponent.Open(id);
|
||||
}
|
||||
|
||||
private async Task ReloadCatalogItems()
|
||||
if (firstRender)
|
||||
{
|
||||
catalogItems = await CatalogItemService.List();
|
||||
StateHasChanged();
|
||||
catalogTypes = await CatalogTypeService.List();
|
||||
catalogBrands = await CatalogBrandService.List();
|
||||
|
||||
CallRequestRefresh();
|
||||
}
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
private async void DetailsClick(int id)
|
||||
{
|
||||
await DetailsComponent.Open(id);
|
||||
}
|
||||
|
||||
private async Task CreateClick()
|
||||
{
|
||||
await CreateComponent.Open();
|
||||
}
|
||||
|
||||
private async Task EditClick(int id)
|
||||
{
|
||||
await EditComponent.Open(id);
|
||||
}
|
||||
|
||||
private async Task DeleteClick(int id)
|
||||
{
|
||||
await DeleteComponent.Open(id);
|
||||
}
|
||||
|
||||
private async Task ReloadCatalogItems()
|
||||
{
|
||||
catalogItems = await CatalogItemService.List();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorAdmin.Services;
|
||||
using Blazored.LocalStorage;
|
||||
using BlazorShared;
|
||||
@@ -7,50 +10,46 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlazorAdmin
|
||||
namespace BlazorAdmin;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public class Program
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
builder.RootComponents.Add<App>("#admin");
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
builder.RootComponents.Add<App>("#admin");
|
||||
|
||||
var baseUrlConfig = new BaseUrlConfiguration();
|
||||
builder.Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig);
|
||||
builder.Services.AddScoped<BaseUrlConfiguration>(sp => baseUrlConfig);
|
||||
var baseUrlConfig = new BaseUrlConfiguration();
|
||||
builder.Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig);
|
||||
builder.Services.AddScoped<BaseUrlConfiguration>(sp => baseUrlConfig);
|
||||
|
||||
builder.Services.AddScoped(sp => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
builder.Services.AddScoped(sp => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
|
||||
builder.Services.AddScoped<ToastService>();
|
||||
builder.Services.AddScoped<HttpService>();
|
||||
builder.Services.AddScoped<ToastService>();
|
||||
builder.Services.AddScoped<HttpService>();
|
||||
|
||||
builder.Services.AddBlazoredLocalStorage();
|
||||
builder.Services.AddBlazoredLocalStorage();
|
||||
|
||||
builder.Services.AddAuthorizationCore();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
|
||||
builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService<AuthenticationStateProvider>());
|
||||
builder.Services.AddAuthorizationCore();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
|
||||
builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService<AuthenticationStateProvider>());
|
||||
|
||||
builder.Services.AddBlazorServices();
|
||||
builder.Services.AddBlazorServices();
|
||||
|
||||
builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));
|
||||
builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));
|
||||
|
||||
await ClearLocalStorageCache(builder.Services);
|
||||
await ClearLocalStorageCache(builder.Services);
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
}
|
||||
await builder.Build().RunAsync();
|
||||
}
|
||||
|
||||
private static async Task ClearLocalStorageCache(IServiceCollection services)
|
||||
{
|
||||
var sp = services.BuildServiceProvider();
|
||||
var localStorageService = sp.GetRequiredService<ILocalStorageService>();
|
||||
private static async Task ClearLocalStorageCache(IServiceCollection services)
|
||||
{
|
||||
var sp = services.BuildServiceProvider();
|
||||
var localStorageService = sp.GetRequiredService<ILocalStorageService>();
|
||||
|
||||
await localStorageService.RemoveItemAsync(typeof(CatalogBrand).Name);
|
||||
await localStorageService.RemoveItemAsync(typeof(CatalogType).Name);
|
||||
}
|
||||
await localStorageService.RemoveItemAsync(typeof(CatalogBrand).Name);
|
||||
await localStorageService.RemoveItemAsync(typeof(CatalogType).Name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class CacheEntry<T>
|
||||
{
|
||||
public class CacheEntry<T>
|
||||
public CacheEntry(T item)
|
||||
{
|
||||
public CacheEntry(T item)
|
||||
{
|
||||
Value = item;
|
||||
}
|
||||
public CacheEntry()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public T Value { get; set; }
|
||||
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
|
||||
Value = item;
|
||||
}
|
||||
public CacheEntry()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public T Value { get; set; }
|
||||
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
@@ -1,114 +1,113 @@
|
||||
using Blazored.LocalStorage;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Blazored.LocalStorage;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class CachedCatalogItemServiceDecorator : ICatalogItemService
|
||||
{
|
||||
public class CachedCatalogItemServiceDecorator : ICatalogItemService
|
||||
private readonly ILocalStorageService _localStorageService;
|
||||
private readonly CatalogItemService _catalogItemService;
|
||||
private ILogger<CachedCatalogItemServiceDecorator> _logger;
|
||||
|
||||
public CachedCatalogItemServiceDecorator(ILocalStorageService localStorageService,
|
||||
CatalogItemService catalogItemService,
|
||||
ILogger<CachedCatalogItemServiceDecorator> logger)
|
||||
{
|
||||
private readonly ILocalStorageService _localStorageService;
|
||||
private readonly CatalogItemService _catalogItemService;
|
||||
private ILogger<CachedCatalogItemServiceDecorator> _logger;
|
||||
_localStorageService = localStorageService;
|
||||
_catalogItemService = catalogItemService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public CachedCatalogItemServiceDecorator(ILocalStorageService localStorageService,
|
||||
CatalogItemService catalogItemService,
|
||||
ILogger<CachedCatalogItemServiceDecorator> logger)
|
||||
public async Task<List<CatalogItem>> ListPaged(int pageSize)
|
||||
{
|
||||
string key = "items";
|
||||
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogItem>>>(key);
|
||||
if (cacheEntry != null)
|
||||
{
|
||||
_localStorageService = localStorageService;
|
||||
_catalogItemService = catalogItemService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<List<CatalogItem>> ListPaged(int pageSize)
|
||||
{
|
||||
string key = "items";
|
||||
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogItem>>>(key);
|
||||
if (cacheEntry != null)
|
||||
_logger.LogInformation("Loading items from local storage.");
|
||||
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||
{
|
||||
_logger.LogInformation("Loading items from local storage.");
|
||||
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||
{
|
||||
return cacheEntry.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Loading {key} from local storage.");
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
}
|
||||
return cacheEntry.Value;
|
||||
}
|
||||
|
||||
var items = await _catalogItemService.ListPaged(pageSize);
|
||||
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<List<CatalogItem>> List()
|
||||
{
|
||||
string key = "items";
|
||||
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogItem>>>(key);
|
||||
if (cacheEntry != null)
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("Loading items from local storage.");
|
||||
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||
{
|
||||
return cacheEntry.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Loading {key} from local storage.");
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
}
|
||||
_logger.LogInformation($"Loading {key} from local storage.");
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
}
|
||||
|
||||
var items = await _catalogItemService.List();
|
||||
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> GetById(int id)
|
||||
var items = await _catalogItemService.ListPaged(pageSize);
|
||||
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<List<CatalogItem>> List()
|
||||
{
|
||||
string key = "items";
|
||||
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogItem>>>(key);
|
||||
if (cacheEntry != null)
|
||||
{
|
||||
return (await List()).FirstOrDefault(x => x.Id == id);
|
||||
_logger.LogInformation("Loading items from local storage.");
|
||||
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||
{
|
||||
return cacheEntry.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Loading {key} from local storage.");
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
|
||||
{
|
||||
var result = await _catalogItemService.Create(catalogItem);
|
||||
await RefreshLocalStorageList();
|
||||
var items = await _catalogItemService.List();
|
||||
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
return items;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
public async Task<CatalogItem> GetById(int id)
|
||||
{
|
||||
return (await List()).FirstOrDefault(x => x.Id == id);
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> Edit(CatalogItem catalogItem)
|
||||
{
|
||||
var result = await _catalogItemService.Edit(catalogItem);
|
||||
await RefreshLocalStorageList();
|
||||
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
|
||||
{
|
||||
var result = await _catalogItemService.Create(catalogItem);
|
||||
await RefreshLocalStorageList();
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<string> Delete(int id)
|
||||
{
|
||||
var result = await _catalogItemService.Delete(id);
|
||||
await RefreshLocalStorageList();
|
||||
public async Task<CatalogItem> Edit(CatalogItem catalogItem)
|
||||
{
|
||||
var result = await _catalogItemService.Edit(catalogItem);
|
||||
await RefreshLocalStorageList();
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task RefreshLocalStorageList()
|
||||
{
|
||||
string key = "items";
|
||||
public async Task<string> Delete(int id)
|
||||
{
|
||||
var result = await _catalogItemService.Delete(id);
|
||||
await RefreshLocalStorageList();
|
||||
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
var items = await _catalogItemService.List();
|
||||
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task RefreshLocalStorageList()
|
||||
{
|
||||
string key = "items";
|
||||
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
var items = await _catalogItemService.List();
|
||||
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,52 @@
|
||||
using Blazored.LocalStorage;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Blazored.LocalStorage;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>
|
||||
: ICatalogLookupDataService<TLookupData>
|
||||
where TLookupData : LookupData
|
||||
where TReponse : ILookupDataResponse<TLookupData>
|
||||
{
|
||||
public class CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>
|
||||
: ICatalogLookupDataService<TLookupData>
|
||||
where TLookupData : LookupData
|
||||
where TReponse : ILookupDataResponse<TLookupData>
|
||||
private readonly ILocalStorageService _localStorageService;
|
||||
private readonly CatalogLookupDataService<TLookupData, TReponse> _catalogTypeService;
|
||||
private ILogger<CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>> _logger;
|
||||
|
||||
public CachedCatalogLookupDataServiceDecorator(ILocalStorageService localStorageService,
|
||||
CatalogLookupDataService<TLookupData, TReponse> catalogTypeService,
|
||||
ILogger<CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>> logger)
|
||||
{
|
||||
private readonly ILocalStorageService _localStorageService;
|
||||
private readonly CatalogLookupDataService<TLookupData, TReponse> _catalogTypeService;
|
||||
private ILogger<CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>> _logger;
|
||||
_localStorageService = localStorageService;
|
||||
_catalogTypeService = catalogTypeService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public CachedCatalogLookupDataServiceDecorator(ILocalStorageService localStorageService,
|
||||
CatalogLookupDataService<TLookupData, TReponse> catalogTypeService,
|
||||
ILogger<CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>> logger)
|
||||
public async Task<List<TLookupData>> List()
|
||||
{
|
||||
string key = typeof(TLookupData).Name;
|
||||
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<TLookupData>>>(key);
|
||||
if (cacheEntry != null)
|
||||
{
|
||||
_localStorageService = localStorageService;
|
||||
_catalogTypeService = catalogTypeService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<List<TLookupData>> List()
|
||||
{
|
||||
string key = typeof(TLookupData).Name;
|
||||
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<TLookupData>>>(key);
|
||||
if (cacheEntry != null)
|
||||
_logger.LogInformation($"Loading {key} from local storage.");
|
||||
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||
{
|
||||
_logger.LogInformation($"Loading {key} from local storage.");
|
||||
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||
{
|
||||
return cacheEntry.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Cache expired; removing {key} from local storage.");
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
}
|
||||
return cacheEntry.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Cache expired; removing {key} from local storage.");
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
}
|
||||
|
||||
var types = await _catalogTypeService.List();
|
||||
var entry = new CacheEntry<List<TLookupData>>(types);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
return types;
|
||||
}
|
||||
|
||||
var types = await _catalogTypeService.List();
|
||||
var entry = new CacheEntry<List<TLookupData>>(types);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
return types;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +1,96 @@
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class CatalogItemService : ICatalogItemService
|
||||
{
|
||||
public class CatalogItemService : ICatalogItemService
|
||||
private readonly ICatalogLookupDataService<CatalogBrand> _brandService;
|
||||
private readonly ICatalogLookupDataService<CatalogType> _typeService;
|
||||
private readonly HttpService _httpService;
|
||||
private readonly ILogger<CatalogItemService> _logger;
|
||||
|
||||
public CatalogItemService(ICatalogLookupDataService<CatalogBrand> brandService,
|
||||
ICatalogLookupDataService<CatalogType> typeService,
|
||||
HttpService httpService,
|
||||
ILogger<CatalogItemService> logger)
|
||||
{
|
||||
private readonly ICatalogLookupDataService<CatalogBrand> _brandService;
|
||||
private readonly ICatalogLookupDataService<CatalogType> _typeService;
|
||||
private readonly HttpService _httpService;
|
||||
private readonly ILogger<CatalogItemService> _logger;
|
||||
_brandService = brandService;
|
||||
_typeService = typeService;
|
||||
_httpService = httpService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public CatalogItemService(ICatalogLookupDataService<CatalogBrand> brandService,
|
||||
ICatalogLookupDataService<CatalogType> typeService,
|
||||
HttpService httpService,
|
||||
ILogger<CatalogItemService> logger)
|
||||
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
|
||||
{
|
||||
var response = await _httpService.HttpPost<CreateCatalogItemResponse>("catalog-items", catalogItem);
|
||||
return response?.CatalogItem;
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> Edit(CatalogItem catalogItem)
|
||||
{
|
||||
return (await _httpService.HttpPut<EditCatalogItemResult>("catalog-items", catalogItem)).CatalogItem;
|
||||
}
|
||||
|
||||
public async Task<string> Delete(int catalogItemId)
|
||||
{
|
||||
return (await _httpService.HttpDelete<DeleteCatalogItemResponse>("catalog-items", catalogItemId)).Status;
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> GetById(int id)
|
||||
{
|
||||
var brandListTask = _brandService.List();
|
||||
var typeListTask = _typeService.List();
|
||||
var itemGetTask = _httpService.HttpGet<EditCatalogItemResult>($"catalog-items/{id}");
|
||||
await Task.WhenAll(brandListTask, typeListTask, itemGetTask);
|
||||
var brands = brandListTask.Result;
|
||||
var types = typeListTask.Result;
|
||||
var catalogItem = itemGetTask.Result.CatalogItem;
|
||||
catalogItem.CatalogBrand = brands.FirstOrDefault(b => b.Id == catalogItem.CatalogBrandId)?.Name;
|
||||
catalogItem.CatalogType = types.FirstOrDefault(t => t.Id == catalogItem.CatalogTypeId)?.Name;
|
||||
return catalogItem;
|
||||
}
|
||||
|
||||
public async Task<List<CatalogItem>> ListPaged(int pageSize)
|
||||
{
|
||||
_logger.LogInformation("Fetching catalog items from API.");
|
||||
|
||||
var brandListTask = _brandService.List();
|
||||
var typeListTask = _typeService.List();
|
||||
var itemListTask = _httpService.HttpGet<PagedCatalogItemResponse>($"catalog-items?PageSize=10");
|
||||
await Task.WhenAll(brandListTask, typeListTask, itemListTask);
|
||||
var brands = brandListTask.Result;
|
||||
var types = typeListTask.Result;
|
||||
var items = itemListTask.Result.CatalogItems;
|
||||
foreach (var item in items)
|
||||
{
|
||||
_brandService = brandService;
|
||||
_typeService = typeService;
|
||||
_httpService = httpService;
|
||||
_logger = logger;
|
||||
item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name;
|
||||
item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
|
||||
public async Task<List<CatalogItem>> List()
|
||||
{
|
||||
_logger.LogInformation("Fetching catalog items from API.");
|
||||
|
||||
var brandListTask = _brandService.List();
|
||||
var typeListTask = _typeService.List();
|
||||
var itemListTask = _httpService.HttpGet<PagedCatalogItemResponse>($"catalog-items");
|
||||
await Task.WhenAll(brandListTask, typeListTask, itemListTask);
|
||||
var brands = brandListTask.Result;
|
||||
var types = typeListTask.Result;
|
||||
var items = itemListTask.Result.CatalogItems;
|
||||
foreach (var item in items)
|
||||
{
|
||||
var response = await _httpService.HttpPost<CreateCatalogItemResponse>("catalog-items", catalogItem);
|
||||
return response?.CatalogItem;
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> Edit(CatalogItem catalogItem)
|
||||
{
|
||||
return (await _httpService.HttpPut<EditCatalogItemResult>("catalog-items", catalogItem)).CatalogItem;
|
||||
}
|
||||
|
||||
public async Task<string> Delete(int catalogItemId)
|
||||
{
|
||||
return (await _httpService.HttpDelete<DeleteCatalogItemResponse>("catalog-items", catalogItemId)).Status;
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> GetById(int id)
|
||||
{
|
||||
var brandListTask = _brandService.List();
|
||||
var typeListTask = _typeService.List();
|
||||
var itemGetTask = _httpService.HttpGet<EditCatalogItemResult>($"catalog-items/{id}");
|
||||
await Task.WhenAll(brandListTask, typeListTask, itemGetTask);
|
||||
var brands = brandListTask.Result;
|
||||
var types = typeListTask.Result;
|
||||
var catalogItem = itemGetTask.Result.CatalogItem;
|
||||
catalogItem.CatalogBrand = brands.FirstOrDefault(b => b.Id == catalogItem.CatalogBrandId)?.Name;
|
||||
catalogItem.CatalogType = types.FirstOrDefault(t => t.Id == catalogItem.CatalogTypeId)?.Name;
|
||||
return catalogItem;
|
||||
}
|
||||
|
||||
public async Task<List<CatalogItem>> ListPaged(int pageSize)
|
||||
{
|
||||
_logger.LogInformation("Fetching catalog items from API.");
|
||||
|
||||
var brandListTask = _brandService.List();
|
||||
var typeListTask = _typeService.List();
|
||||
var itemListTask = _httpService.HttpGet<PagedCatalogItemResponse>($"catalog-items?PageSize=10");
|
||||
await Task.WhenAll(brandListTask, typeListTask, itemListTask);
|
||||
var brands = brandListTask.Result;
|
||||
var types = typeListTask.Result;
|
||||
var items = itemListTask.Result.CatalogItems;
|
||||
foreach (var item in items)
|
||||
{
|
||||
item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name;
|
||||
item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<List<CatalogItem>> List()
|
||||
{
|
||||
_logger.LogInformation("Fetching catalog items from API.");
|
||||
|
||||
var brandListTask = _brandService.List();
|
||||
var typeListTask = _typeService.List();
|
||||
var itemListTask = _httpService.HttpGet<PagedCatalogItemResponse>($"catalog-items");
|
||||
await Task.WhenAll(brandListTask, typeListTask, itemListTask);
|
||||
var brands = brandListTask.Result;
|
||||
var types = typeListTask.Result;
|
||||
var items = itemListTask.Result.CatalogItems;
|
||||
foreach (var item in items)
|
||||
{
|
||||
item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name;
|
||||
item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name;
|
||||
}
|
||||
return items;
|
||||
item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name;
|
||||
item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
using BlazorShared;
|
||||
using BlazorShared.Attributes;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorShared;
|
||||
using BlazorShared.Attributes;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class CatalogLookupDataService<TLookupData, TReponse>
|
||||
: ICatalogLookupDataService<TLookupData>
|
||||
where TLookupData : LookupData
|
||||
where TReponse : ILookupDataResponse<TLookupData>
|
||||
{
|
||||
public class CatalogLookupDataService<TLookupData, TReponse>
|
||||
: ICatalogLookupDataService<TLookupData>
|
||||
where TLookupData : LookupData
|
||||
where TReponse : ILookupDataResponse<TLookupData>
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<CatalogLookupDataService<TLookupData, TReponse>> _logger;
|
||||
private readonly string _apiUrl;
|
||||
|
||||
public CatalogLookupDataService(HttpClient httpClient,
|
||||
BaseUrlConfiguration baseUrlConfiguration,
|
||||
ILogger<CatalogLookupDataService<TLookupData, TReponse>> logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
_apiUrl = baseUrlConfiguration.ApiBase;
|
||||
}
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<CatalogLookupDataService<TLookupData, TReponse>> _logger;
|
||||
private readonly string _apiUrl;
|
||||
public async Task<List<TLookupData>> List()
|
||||
{
|
||||
var endpointName = typeof(TLookupData).GetCustomAttribute<EndpointAttribute>().Name;
|
||||
_logger.LogInformation($"Fetching {typeof(TLookupData).Name} from API. Enpoint : {endpointName}");
|
||||
|
||||
public CatalogLookupDataService(HttpClient httpClient,
|
||||
BaseUrlConfiguration baseUrlConfiguration,
|
||||
ILogger<CatalogLookupDataService<TLookupData, TReponse>> logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
_apiUrl = baseUrlConfiguration.ApiBase;
|
||||
}
|
||||
|
||||
public async Task<List<TLookupData>> List()
|
||||
{
|
||||
var endpointName = typeof(TLookupData).GetCustomAttribute<EndpointAttribute>().Name;
|
||||
_logger.LogInformation($"Fetching {typeof(TLookupData).Name} from API. Enpoint : {endpointName}");
|
||||
|
||||
var response = await _httpClient.GetFromJsonAsync<TReponse>($"{_apiUrl}{endpointName}");
|
||||
return response.List;
|
||||
}
|
||||
var response = await _httpClient.GetFromJsonAsync<TReponse>($"{_apiUrl}{endpointName}");
|
||||
return response.List;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,96 +1,95 @@
|
||||
using BlazorShared;
|
||||
using BlazorShared.Models;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorShared;
|
||||
using BlazorShared.Models;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class HttpService
|
||||
{
|
||||
public class HttpService
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ToastService _toastService;
|
||||
private readonly string _apiUrl;
|
||||
|
||||
|
||||
public HttpService(HttpClient httpClient, BaseUrlConfiguration baseUrlConfiguration, ToastService toastService)
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ToastService _toastService;
|
||||
private readonly string _apiUrl;
|
||||
_httpClient = httpClient;
|
||||
_toastService = toastService;
|
||||
_apiUrl = baseUrlConfiguration.ApiBase;
|
||||
}
|
||||
|
||||
|
||||
public HttpService(HttpClient httpClient, BaseUrlConfiguration baseUrlConfiguration, ToastService toastService)
|
||||
public async Task<T> HttpGet<T>(string uri)
|
||||
where T : class
|
||||
{
|
||||
var result = await _httpClient.GetAsync($"{_apiUrl}{uri}");
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_toastService = toastService;
|
||||
_apiUrl = baseUrlConfiguration.ApiBase;
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<T> HttpGet<T>(string uri)
|
||||
where T : class
|
||||
{
|
||||
var result = await _httpClient.GetAsync($"{_apiUrl}{uri}");
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
public async Task<T> HttpDelete<T>(string uri, int id)
|
||||
where T : class
|
||||
{
|
||||
var result = await _httpClient.DeleteAsync($"{_apiUrl}{uri}/{id}");
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<T> HttpDelete<T>(string uri, int id)
|
||||
where T : class
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
public async Task<T> HttpPost<T>(string uri, object dataToSend)
|
||||
where T : class
|
||||
{
|
||||
var content = ToJson(dataToSend);
|
||||
|
||||
var result = await _httpClient.PostAsync($"{_apiUrl}{uri}", content);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
var result = await _httpClient.DeleteAsync($"{_apiUrl}{uri}/{id}");
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
public async Task<T> HttpPost<T>(string uri, object dataToSend)
|
||||
where T : class
|
||||
{
|
||||
var content = ToJson(dataToSend);
|
||||
|
||||
var result = await _httpClient.PostAsync($"{_apiUrl}{uri}", content);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
var exception = JsonSerializer.Deserialize<ErrorDetails>(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
});
|
||||
_toastService.ShowToast($"Error : {exception.Message}", ToastLevel.Error);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
public async Task<T> HttpPut<T>(string uri, object dataToSend)
|
||||
where T : class
|
||||
{
|
||||
var content = ToJson(dataToSend);
|
||||
|
||||
var result = await _httpClient.PutAsync($"{_apiUrl}{uri}", content);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
_toastService.ShowToast("Error", ToastLevel.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
private StringContent ToJson(object obj)
|
||||
{
|
||||
return new StringContent(JsonSerializer.Serialize(obj), Encoding.UTF8, "application/json");
|
||||
}
|
||||
|
||||
private async Task<T> FromHttpResponseMessage<T>(HttpResponseMessage result)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions
|
||||
var exception = JsonSerializer.Deserialize<ErrorDetails>(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
});
|
||||
_toastService.ShowToast($"Error : {exception.Message}", ToastLevel.Error);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
public async Task<T> HttpPut<T>(string uri, object dataToSend)
|
||||
where T : class
|
||||
{
|
||||
var content = ToJson(dataToSend);
|
||||
|
||||
var result = await _httpClient.PutAsync($"{_apiUrl}{uri}", content);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
_toastService.ShowToast("Error", ToastLevel.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
private StringContent ToJson(object obj)
|
||||
{
|
||||
return new StringContent(JsonSerializer.Serialize(obj), Encoding.UTF8, "application/json");
|
||||
}
|
||||
|
||||
private async Task<T> FromHttpResponseMessage<T>(HttpResponseMessage result)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,54 @@
|
||||
using System;
|
||||
using System.Timers;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
{
|
||||
public enum ToastLevel
|
||||
{
|
||||
Info,
|
||||
Success,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class ToastService : IDisposable
|
||||
public enum ToastLevel
|
||||
{
|
||||
Info,
|
||||
Success,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
public class ToastService : IDisposable
|
||||
{
|
||||
public event Action<string, ToastLevel> OnShow;
|
||||
public event Action OnHide;
|
||||
private Timer Countdown;
|
||||
public void ShowToast(string message, ToastLevel level)
|
||||
{
|
||||
public event Action<string, ToastLevel> OnShow;
|
||||
public event Action OnHide;
|
||||
private Timer Countdown;
|
||||
public void ShowToast(string message, ToastLevel level)
|
||||
OnShow?.Invoke(message, level);
|
||||
StartCountdown();
|
||||
}
|
||||
private void StartCountdown()
|
||||
{
|
||||
SetCountdown();
|
||||
if (Countdown.Enabled)
|
||||
{
|
||||
OnShow?.Invoke(message, level);
|
||||
StartCountdown();
|
||||
Countdown.Stop();
|
||||
Countdown.Start();
|
||||
}
|
||||
private void StartCountdown()
|
||||
else
|
||||
{
|
||||
SetCountdown();
|
||||
if (Countdown.Enabled)
|
||||
{
|
||||
Countdown.Stop();
|
||||
Countdown.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Countdown.Start();
|
||||
}
|
||||
Countdown.Start();
|
||||
}
|
||||
private void SetCountdown()
|
||||
}
|
||||
private void SetCountdown()
|
||||
{
|
||||
if (Countdown == null)
|
||||
{
|
||||
if (Countdown == null)
|
||||
{
|
||||
Countdown = new Timer(3000);
|
||||
Countdown.Elapsed += HideToast;
|
||||
Countdown.AutoReset = false;
|
||||
}
|
||||
}
|
||||
private void HideToast(object source, ElapsedEventArgs args)
|
||||
{
|
||||
OnHide?.Invoke();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Countdown?.Dispose();
|
||||
Countdown = new Timer(3000);
|
||||
Countdown.Elapsed += HideToast;
|
||||
Countdown.AutoReset = false;
|
||||
}
|
||||
}
|
||||
private void HideToast(object source, ElapsedEventArgs args)
|
||||
{
|
||||
OnHide?.Invoke();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Countdown?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,20 +3,19 @@ using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BlazorAdmin
|
||||
{
|
||||
public static class ServicesConfiguration
|
||||
{
|
||||
public static IServiceCollection AddBlazorServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<ICatalogLookupDataService<CatalogBrand>, CachedCatalogLookupDataServiceDecorator<CatalogBrand,CatalogBrandResponse>>();
|
||||
services.AddScoped<CatalogLookupDataService<CatalogBrand, CatalogBrandResponse>>();
|
||||
services.AddScoped<ICatalogLookupDataService<CatalogType>, CachedCatalogLookupDataServiceDecorator<CatalogType,CatalogTypeResponse>>();
|
||||
services.AddScoped<CatalogLookupDataService<CatalogType, CatalogTypeResponse>>();
|
||||
services.AddScoped<ICatalogItemService, CachedCatalogItemServiceDecorator>();
|
||||
services.AddScoped<CatalogItemService>();
|
||||
namespace BlazorAdmin;
|
||||
|
||||
return services;
|
||||
}
|
||||
public static class ServicesConfiguration
|
||||
{
|
||||
public static IServiceCollection AddBlazorServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<ICatalogLookupDataService<CatalogBrand>, CachedCatalogLookupDataServiceDecorator<CatalogBrand, CatalogBrandResponse>>();
|
||||
services.AddScoped<CatalogLookupDataService<CatalogBrand, CatalogBrandResponse>>();
|
||||
services.AddScoped<ICatalogLookupDataService<CatalogType>, CachedCatalogLookupDataServiceDecorator<CatalogType, CatalogTypeResponse>>();
|
||||
services.AddScoped<CatalogLookupDataService<CatalogType, CatalogTypeResponse>>();
|
||||
services.AddScoped<ICatalogItemService, CachedCatalogItemServiceDecorator>();
|
||||
services.AddScoped<CatalogItemService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace BlazorAdmin.Shared
|
||||
namespace BlazorAdmin.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// This is needed until 5.0 ships with native support
|
||||
/// https://www.pragimtech.com/blog/blazor/inputselect-does-not-support-system.int32/
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
public class CustomInputSelect<TValue> : InputSelect<TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// This is needed until 5.0 ships with native support
|
||||
/// https://www.pragimtech.com/blog/blazor/inputselect-does-not-support-system.int32/
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
public class CustomInputSelect<TValue> : InputSelect<TValue>
|
||||
protected override bool TryParseValueFromString(string value, out TValue result,
|
||||
out string validationErrorMessage)
|
||||
{
|
||||
protected override bool TryParseValueFromString(string value, out TValue result,
|
||||
out string validationErrorMessage)
|
||||
if (typeof(TValue) == typeof(int))
|
||||
{
|
||||
if (typeof(TValue) == typeof(int))
|
||||
if (int.TryParse(value, out var resultInt))
|
||||
{
|
||||
if (int.TryParse(value, out var resultInt))
|
||||
{
|
||||
result = (TValue)(object)resultInt;
|
||||
validationErrorMessage = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default;
|
||||
validationErrorMessage =
|
||||
$"The selected value {value} is not a valid number.";
|
||||
return false;
|
||||
}
|
||||
result = (TValue)(object)resultInt;
|
||||
validationErrorMessage = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.TryParseValueFromString(value, out result,
|
||||
out validationErrorMessage);
|
||||
result = default;
|
||||
validationErrorMessage =
|
||||
$"The selected value {value} is not a valid number.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.TryParseValueFromString(value, out result,
|
||||
out validationErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user