diff --git a/NetinaShop.AdminPanel.PWA/Components/Originals/SideBarUi.razor b/NetinaShop.AdminPanel.PWA/Components/Originals/SideBarUi.razor
new file mode 100644
index 0000000..f9b6ffe
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Components/Originals/SideBarUi.razor
@@ -0,0 +1,73 @@
+@inject NavigationManager NavigationManager
+
+
+
+ @* *@
+
+
+
+
+
+ @item.Text
+
+
+
+
+
+
+
+
+ @code
+ {
+
+
+ private void SelectedChange(TreeItemData? data)
+ {
+ if (data != null)
+ NavigationManager.NavigateTo(data.Page);
+ }
+ private HashSet TreeItems { get; set; } = new HashSet();
+
+ public class TreeItemData
+ {
+ public string Text { get; set; }
+
+ public string Icon { get; set; }
+
+ public string Page { get; set; }
+
+ private bool _isActive;
+
+ public bool IsActive
+ {
+ get { return _isActive; }
+ set
+ {
+
+ _isActive = value;
+ }
+ }
+
+
+ public HashSet TreeItems { get; set; } = new HashSet();
+
+ public TreeItemData(string text, string icon, string page)
+ {
+ Page = page;
+ Text = text;
+ Icon = icon;
+ }
+ }
+
+ protected override void OnInitialized()
+ {
+ TreeItems.Add(new TreeItemData("داشبورد", Icons.Material.Outlined.Dashboard, "HomePage"));
+ TreeItems.Add(new TreeItemData("فروش", Icons.Material.Outlined.ShoppingCart, "ProductsPage"));
+ TreeItems.Add(new TreeItemData("محصولات", Icons.Material.Outlined.CenterFocusStrong, "ProductsPage"));
+ TreeItems.Add(new TreeItemData("دسته بندی ها", Icons.Material.Outlined.AllInbox, "CategoriesPage"));
+ TreeItems.Add(new TreeItemData("برند ها", Icons.Custom.Brands.Facebook, "BrandsPage"));
+ TreeItems.Add(new TreeItemData("مشترکین", Icons.Material.Outlined.People, "ProductsPage"));
+ TreeItems.Add(new TreeItemData("وبلاگ", Icons.Material.Outlined.Web, "ProductsPage"));
+ TreeItems.Add(new TreeItemData("تنظیمات", Icons.Material.Outlined.Settings, "ProductsPage"));
+ }
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Dialogs/Originals/QuestionDialog.razor b/NetinaShop.AdminPanel.PWA/Dialogs/Originals/QuestionDialog.razor
new file mode 100644
index 0000000..94ce7e7
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Dialogs/Originals/QuestionDialog.razor
@@ -0,0 +1,24 @@
+
+
+ @ContentText
+
+
+
+ تایید
+
+ انصراف
+
+
+
+@code
+{
+
+ [CascadingParameter]
+ MudDialogInstance? MudDialog { get; set; }
+
+ [Parameter]
+ public string ContentText { get; set; } = string.Empty;
+
+ void Submit() => MudDialog?.Close(DialogResult.Ok(true));
+ void Cancel() => MudDialog?.Cancel();
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor b/NetinaShop.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor
new file mode 100644
index 0000000..dc65e5e
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor
@@ -0,0 +1,80 @@
+@using Radzen.Blazor
+
+
+
+
+
+
+ اطلاعات کلی
+ اطلاعات کلی محصول را به دقت وارد کنید
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ توضیحات تکمیلی
+ می توانید توضیحاتــ تکمیلی محصول را کامل وارد کنید
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ تصاویر محصول
+ می توانید برای محصول چند تصویر اپلود کنید
+
+
+
+
+
+
+ تایید
+ بستن
+
+
+
+
+@code {
+ [CascadingParameter] MudDialogInstance MudDialog { get; set; }
+
+ void Submit() => MudDialog.Close(DialogResult.Ok(true));
+ void Cancel() => MudDialog.Cancel();
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Dialogs/ProductCategoryActionDialogBox.razor b/NetinaShop.AdminPanel.PWA/Dialogs/ProductCategoryActionDialogBox.razor
new file mode 100644
index 0000000..0f7ee14
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Dialogs/ProductCategoryActionDialogBox.razor
@@ -0,0 +1,95 @@
+@using Blazorise.Extensions
+@inject ISnackbar Snackbar
+@inject IRestWrapper RestWrapper
+
+
+
+
+
+
+
+ اطلاعات کلی
+ اطلاعات کلی دسته بندی محصول را به دقت وارد کنید
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @e.Name
+
+
+
+
+
+
+
+
+
+
+
+
+ تایید
+ بستن
+
+
+
+
+@code {
+ [CascadingParameter] MudDialogInstance MudDialog { get; set; }
+
+ void Submit() => MudDialog.Close(DialogResult.Ok(true));
+ void Cancel() => MudDialog.Cancel();
+
+
+ private List _productCategories = new List();
+ private ProductCategorySDto? _selectedCategory;
+ private async Task> SearchCity(string city)
+ {
+ try
+ {
+ if (_productCategories.Count == 0)
+ {
+ _productCategories = await RestWrapper.CrudDtoApiRest(Address.ProductCategoryController).ReadAll();
+ }
+ if (city.IsNullOrEmpty())
+ return _productCategories;
+ return _productCategories.Where(c => c.Name.Contains(city));
+ }
+ catch (ApiException ex)
+ {
+ var exe = await ex.GetContentAsAsync();
+ if (exe != null)
+ Snackbar.Add(exe.Message, Severity.Error);
+ Snackbar.Add(ex.Content, Severity.Error);
+ return _productCategories;
+ }
+ catch (Exception e)
+ {
+ Snackbar.Add(e.Message, Severity.Error);
+ return _productCategories;
+ }
+ }
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Layout/MainLayout.razor b/NetinaShop.AdminPanel.PWA/Layout/MainLayout.razor
index 3b200f6..8d218c6 100644
--- a/NetinaShop.AdminPanel.PWA/Layout/MainLayout.razor
+++ b/NetinaShop.AdminPanel.PWA/Layout/MainLayout.razor
@@ -1,7 +1,4 @@
-@using Toolbelt.Blazor.PWA.Updater
-@using Toolbelt.Blazor.PWA.Updater.Service
-@using MudBlazor
-@inherits LayoutComponentBase
+@inherits LayoutComponentBase
@inject IPWAUpdaterService PwaUpdaterService
@@ -31,12 +28,39 @@
-
+
+
+
+
+
+
+
+
+ امیرحسین خادمی
+ 09214802813
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@code
@@ -52,13 +76,13 @@
},
Palette = new PaletteLight()
{
- Primary = "#356859",
- Secondary = "#FD5523",
+ Primary = "#001A46",
+ Secondary = "#E59F2E",
},
PaletteDark = new PaletteDark()
{
- Primary = "#356859",
- Secondary = "#FD5523",
+ Primary = "#001A46",
+ Secondary = "#E59F2E",
}
};
diff --git a/NetinaShop.AdminPanel.PWA/Models/Address.cs b/NetinaShop.AdminPanel.PWA/Models/Address.cs
new file mode 100644
index 0000000..22cd41f
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Models/Address.cs
@@ -0,0 +1,14 @@
+namespace NetinaShop.AdminPanel.PWA.Models;
+
+public static class Address
+{
+#if DEBUG
+ //public static string BaseAddress = "http://localhost:32770/api";
+ public static string BaseAddress = "https://apinetinashop.visabartar.com/api";
+#else
+ public static string BaseAddress = "https://apinetinashop.visabartar.com/api";
+#endif
+ public static string AuthController = $"{BaseAddress}/auth";
+ public static string UserController = $"{BaseAddress}/user";
+ public static string ProductCategoryController = $"{BaseAddress}/product/category";
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Models/Api/ApiResult.cs b/NetinaShop.AdminPanel.PWA/Models/Api/ApiResult.cs
new file mode 100644
index 0000000..830cfeb
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Models/Api/ApiResult.cs
@@ -0,0 +1,28 @@
+using NetinaShop.Common.Extensions;
+using NetinaShop.Common.Models.Api;
+
+namespace NetinaShop.AdminPanel.PWA.Models.Api;
+
+public class ApiResult
+{
+ public ApiResult(bool isSuccess, ApiResultStatusCode statusCode, string message = null)
+ {
+ IsSuccess = isSuccess;
+ StatusCode = statusCode;
+ Message = message ?? statusCode.ToDisplay();
+ }
+
+ public bool IsSuccess { get; set; }
+ public ApiResultStatusCode StatusCode { get; set; }
+
+ public string Message { get; set; }
+}
+
+public class ApiResult : ApiResult where TData : class
+{
+ public ApiResult(bool isSuccess, ApiResultStatusCode statusCode, TData data, string message = null) : base(isSuccess, statusCode, message)
+ {
+ Data = data;
+ }
+ public TData Data { get; set; }
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Models/BaseViewModel.cs b/NetinaShop.AdminPanel.PWA/Models/BaseViewModel.cs
new file mode 100644
index 0000000..e633512
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Models/BaseViewModel.cs
@@ -0,0 +1,33 @@
+namespace NetinaShop.AdminPanel.PWA.Models;
+
+public class BaseViewModel
+{
+ public bool IsProcessing { get; set; } = false;
+
+ public virtual void Initialize()
+ {
+
+ }
+ public virtual Task InitializeAsync()
+ {
+ return Task.CompletedTask;
+ }
+}
+
+public class BaseViewModel
+{
+ public bool IsProcessing { get; set; } = false;
+ public TPageDto PageDto { get; set; }
+ public BaseViewModel()
+ {
+ PageDto = Activator.CreateInstance();
+ }
+ public virtual void Initialize()
+ {
+
+ }
+ public virtual Task InitializeAsync()
+ {
+ return Task.CompletedTask;
+ }
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Models/LocalStorageKeys.cs b/NetinaShop.AdminPanel.PWA/Models/LocalStorageKeys.cs
new file mode 100644
index 0000000..32e7cd0
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Models/LocalStorageKeys.cs
@@ -0,0 +1,7 @@
+namespace NetinaShop.AdminPanel.PWA.Models;
+
+public static class LocalStorageKeys
+{
+ public const string Token = nameof(Token);
+ public const string UserInfo = nameof(UserInfo);
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/NetinaShop.AdminPanel.PWA.csproj b/NetinaShop.AdminPanel.PWA/NetinaShop.AdminPanel.PWA.csproj
index 3171793..ce257a5 100644
--- a/NetinaShop.AdminPanel.PWA/NetinaShop.AdminPanel.PWA.csproj
+++ b/NetinaShop.AdminPanel.PWA/NetinaShop.AdminPanel.PWA.csproj
@@ -1,4 +1,4 @@
-
+
net8.0
@@ -18,14 +18,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NetinaShop.AdminPanel.PWA/Pages/BrandsPage.razor b/NetinaShop.AdminPanel.PWA/Pages/BrandsPage.razor
new file mode 100644
index 0000000..3691a9b
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Pages/BrandsPage.razor
@@ -0,0 +1,7 @@
+@page "/BrandsPage"
+
+BrandsPage
+
+@code {
+
+}
diff --git a/NetinaShop.AdminPanel.PWA/Pages/CategoriesPage.razor b/NetinaShop.AdminPanel.PWA/Pages/CategoriesPage.razor
new file mode 100644
index 0000000..e902692
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Pages/CategoriesPage.razor
@@ -0,0 +1,77 @@
+@page "/CategoriesPage"
+@using NetinaShop.AdminPanel.PWA.Utilities
+@inject IDialogService DialogService
+@inject NavigationManager NavigationManager
+@inject IRestWrapper RestWrapper
+@inject ISnackbar Snackbar
+@inject IUserUtility UserUtility
+
+
+
+
+
+
+ دسته بندی ها
+ 124 عدد
+
+
+ افزودن دسته بندی
+
+
+
+
+
+
+
+
+
+
+
+ @if (@context.Item.IsMain)
+ {
+ بلی
+ }
+ else
+ {
+ خیر
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@code
+{
+ public CategoriesPageViewModel ViewModel { get; set; }
+ protected override async Task OnInitializedAsync()
+ {
+ ViewModel = new CategoriesPageViewModel(NavigationManager, Snackbar, UserUtility, RestWrapper, DialogService);
+ await ViewModel.InitializeAsync();
+ await base.OnInitializedAsync();
+ }
+
+}
diff --git a/NetinaShop.AdminPanel.PWA/Pages/CategoriesPage.razor.cs b/NetinaShop.AdminPanel.PWA/Pages/CategoriesPage.razor.cs
new file mode 100644
index 0000000..46e552d
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Pages/CategoriesPage.razor.cs
@@ -0,0 +1,98 @@
+using Microsoft.AspNetCore.Components;
+using Microsoft.JSInterop;
+using MudBlazor;
+using NetinaShop.AdminPanel.PWA.Dialogs;
+using NetinaShop.AdminPanel.PWA.Dialogs.Originals;
+using NetinaShop.AdminPanel.PWA.Models.Api;
+using NetinaShop.AdminPanel.PWA.Services.RestServices;
+using NetinaShop.AdminPanel.PWA.Utilities;
+using NetinaShop.Domain.Entities.ProductCategories;
+
+namespace NetinaShop.AdminPanel.PWA.Pages;
+
+public class CategoriesPageViewModel : BaseViewModel>
+{
+ private readonly NavigationManager _navigationManager;
+ private readonly ISnackbar _snackbar;
+ private readonly IUserUtility _userUtility;
+ private readonly IDialogService _dialogService;
+ private readonly IRestWrapper _restWrapper;
+
+ public CategoriesPageViewModel(NavigationManager navigationManager, ISnackbar snackbar, IUserUtility userUtility, IRestWrapper restWrapper, IDialogService dialogService)
+ {
+ _navigationManager = navigationManager;
+ _snackbar = snackbar;
+ _userUtility = userUtility;
+ _restWrapper = restWrapper;
+ _dialogService = dialogService;
+ }
+
+ public override async Task InitializeAsync()
+ {
+ try
+ {
+ IsProcessing = true;
+ var dto = await _restWrapper.CrudDtoApiRest(Address.ProductCategoryController)
+ .ReadAll();
+ PageDto = dto;
+ }
+ catch (ApiException ex)
+ {
+ var exe = await ex.GetContentAsAsync();
+ _snackbar.Add(exe != null ? exe.Message : ex.Content, Severity.Error);
+ }
+ catch (Exception e)
+ {
+ _snackbar.Add(e.Message, Severity.Error);
+ }
+ finally
+ {
+
+ IsProcessing = false;
+ }
+ await base.InitializeAsync();
+ }
+
+ public async Task AddProductClicked()
+ {
+ DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, DisableBackdropClick = true };
+ await _dialogService.ShowAsync("افزودن دسته جدید", maxWidth);
+ }
+
+ public async Task DeleteProductCategoryAsync(Guid selectedCategoryId)
+ {
+ var options = new DialogOptions { CloseOnEscapeKey = true };
+ var parameters = new DialogParameters();
+ parameters.Add(x => x.ContentText, "آیا از حذف دسته بندی اطمینان دارید ?");
+ var dialogReference = await _dialogService.ShowAsync("حذف شرح حال", parameters, options);
+ var result = await dialogReference.Result;
+ if (!result.Canceled)
+ {
+
+ try
+ {
+
+ IsProcessing = true;
+ var token = await _userUtility.GetBearerTokenAsync();
+ await _restWrapper.CrudDtoApiRest(Address.ProductCategoryController)
+ .Delete(selectedCategoryId, token);
+ _snackbar.Add("حذف دسته بندی با موفقیت انجام شد", Severity.Success);
+
+ }
+ catch (ApiException ex)
+ {
+ var exe = await ex.GetContentAsAsync();
+ _snackbar.Add(exe != null ? exe.Message : ex.Content, Severity.Error);
+ }
+ catch (Exception e)
+ {
+ _snackbar.Add(e.Message, Severity.Error);
+ }
+ finally
+ {
+
+ IsProcessing = false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Pages/Home.razor b/NetinaShop.AdminPanel.PWA/Pages/Home.razor
index 9001e0b..43565ac 100644
--- a/NetinaShop.AdminPanel.PWA/Pages/Home.razor
+++ b/NetinaShop.AdminPanel.PWA/Pages/Home.razor
@@ -1,7 +1,7 @@
-@page "/"
+@page "/HomePage"
-Home
+
+
-Hello, world!
-
-Welcome to your new app.
+
+
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Pages/ProductsPage.razor b/NetinaShop.AdminPanel.PWA/Pages/ProductsPage.razor
new file mode 100644
index 0000000..dcbf687
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Pages/ProductsPage.razor
@@ -0,0 +1,95 @@
+@page "/ProductsPage"
+@using NetinaShop.AdminPanel.PWA.Dialogs
+@inject IDialogService DialogService
+
+
+
+
+
+ محصولاتــــ
+ 124 عدد
+
+ افزودن محصول
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@code
+{
+ List Products { get; set; } = new List();
+ protected override Task OnInitializedAsync()
+ {
+ Products.Add(new ProductSDto
+ {
+ PersianName = "شامپو ضدشوره هد اند شولدرز (Head & Shoulders) مدل Lemon 2in1 arada حجم 350 میلی لیتر",
+ BrandNames = "شوریدر",
+ CategoryName = "شوینده سر",
+ Cost = 1200000,
+
+ });
+ Products.Add(new ProductSDto
+ {
+ PersianName = "شامپو ضدشوره هد اند شولدرز (Head & Shoulders) مدل Lemon 2in1 arada حجم 350 میلی لیتر",
+ BrandNames = "شوریدر",
+ CategoryName = "شوینده سر",
+ Cost = 1200000,
+
+ });
+ Products.Add(new ProductSDto
+ {
+ PersianName = "شامپو ضدشوره هد اند شولدرز (Head & Shoulders) مدل Lemon 2in1 arada حجم 350 میلی لیتر",
+ BrandNames = "شوریدر",
+ CategoryName = "شوینده سر",
+ Cost = 1200000,
+
+ });
+ Products.Add(new ProductSDto
+ {
+ PersianName = "شامپو ضدشوره هد اند شولدرز (Head & Shoulders) مدل Lemon 2in1 arada حجم 350 میلی لیتر",
+ BrandNames = "شوریدر",
+ CategoryName = "شوینده سر",
+ Cost = 1200000,
+
+ });
+ Products.Add(new ProductSDto
+ {
+ PersianName = "شامپو ضدشوره هد اند شولدرز (Head & Shoulders) مدل Lemon 2in1 arada حجم 350 میلی لیتر",
+ BrandNames = "شوریدر",
+ CategoryName = "شوینده سر",
+ Cost = 1200000,
+
+ });
+ return base.OnInitializedAsync();
+ }
+
+ private async Task AddProductClicked()
+ {
+ DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true };
+ var options = new DialogOptions { CloseOnEscapeKey = true , Position = DialogPosition.Center, MaxWidth = MaxWidth.Large , DisableBackdropClick = true};
+ await DialogService.ShowAsync("افزودن محصول جدید", maxWidth);
+ }
+}
diff --git a/NetinaShop.AdminPanel.PWA/Program.cs b/NetinaShop.AdminPanel.PWA/Program.cs
index 6a57b0f..6e81247 100644
--- a/NetinaShop.AdminPanel.PWA/Program.cs
+++ b/NetinaShop.AdminPanel.PWA/Program.cs
@@ -4,6 +4,8 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MudBlazor;
using MudBlazor.Services;
using NetinaShop.AdminPanel.PWA;
+using NetinaShop.AdminPanel.PWA.Services.RestServices;
+using NetinaShop.AdminPanel.PWA.Utilities;
using Toolbelt.Blazor.Extensions.DependencyInjection;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
@@ -18,6 +20,8 @@ builder.Services.AddMudServices(config =>
config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomCenter;
});
+builder.Services.AddScoped();
+builder.Services.AddScoped();
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddPWAUpdater();
await builder.Build().RunAsync();
diff --git a/NetinaShop.AdminPanel.PWA/Services/RestServices/IAuthRestApi.cs b/NetinaShop.AdminPanel.PWA/Services/RestServices/IAuthRestApi.cs
new file mode 100644
index 0000000..1229c3b
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Services/RestServices/IAuthRestApi.cs
@@ -0,0 +1,13 @@
+namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
+
+public interface IAuthRestApi
+{
+ [Get("/verifycode")]
+ public Task GetVerifyCodeAsync([Query] string phoneNumber);
+
+ [Post("/login/code")]
+ public Task> LoginWithVerifyCodeAsync([Body] LoginRequestDto request);
+
+ [Post("/signup")]
+ public Task> CompleteSignUpAsync([Body] SignUpRequestDto request, [Header("Authorization")] string authorization);
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Services/RestServices/ICrudApiRest.cs b/NetinaShop.AdminPanel.PWA/Services/RestServices/ICrudApiRest.cs
new file mode 100644
index 0000000..53d7e56
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Services/RestServices/ICrudApiRest.cs
@@ -0,0 +1,47 @@
+using Refit;
+
+namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
+
+public interface ICrudApiRest where T : class
+{
+ [Post("")]
+ Task Create([Body] T payload, [Header("Authorization")] string authorization);
+
+ [Get("")]
+ Task> ReadAll([Query] int page,[Header("Authorization")] string authorization);
+
+ [Get("/{key}")]
+ Task ReadOne(TKey key, [Header("Authorization")] string authorization);
+
+ [Put("")]
+ Task Update([Body] T payload, [Header("Authorization")] string authorization);
+
+ [Delete("/{key}")]
+ Task Delete(TKey key, [Header("Authorization")] string authorization);
+
+}
+public interface ICrudDtoApiRest where T : class where TDto : class
+{
+ [Post("")]
+ Task Create([Body] T payload, [Header("Authorization")] string authorization);
+ [Post("")]
+ Task Create([Body] TDto payload, [Header("Authorization")] string authorization);
+
+ [Get("")]
+ Task> ReadAll([Query]int page,[Header("Authorization")] string authorization);
+
+ [Get("")]
+ Task> ReadAll();
+
+ [Get("/{key}")]
+ Task ReadOne(TKey key, [Header("Authorization")] string authorization);
+
+ [Put("")]
+ Task Update([Body] T payload, [Header("Authorization")] string authorization);
+ [Put("")]
+ Task Update([Body] TDto payload, [Header("Authorization")] string authorization);
+
+ [Delete("/{key}")]
+ Task Delete(TKey key, [Header("Authorization")] string authorization);
+
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Services/RestServices/IRestWrapper.cs b/NetinaShop.AdminPanel.PWA/Services/RestServices/IRestWrapper.cs
new file mode 100644
index 0000000..d9f7d31
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Services/RestServices/IRestWrapper.cs
@@ -0,0 +1,11 @@
+namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
+
+public interface IRestWrapper
+{
+ public ICrudApiRest CrudApiRest(string address) where T : class;
+ public ICrudDtoApiRest CrudDtoApiRest(string address) where T : class where TDto : class;
+
+
+ public IAuthRestApi AuthRestApi { get; }
+ public IUserRestApi UserRestApi { get; }
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Services/RestServices/IUserRestApi.cs b/NetinaShop.AdminPanel.PWA/Services/RestServices/IUserRestApi.cs
new file mode 100644
index 0000000..a3cbcdf
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Services/RestServices/IUserRestApi.cs
@@ -0,0 +1,9 @@
+namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
+
+public interface IUserRestApi
+{
+
+
+ [Put("")]
+ Task UpdateUserAsync([Body]UserActionRequestDto request, [Header("Authorization")] string authorization);
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Services/RestServices/RestWrapper.cs b/NetinaShop.AdminPanel.PWA/Services/RestServices/RestWrapper.cs
new file mode 100644
index 0000000..793da03
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Services/RestServices/RestWrapper.cs
@@ -0,0 +1,23 @@
+namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
+
+public class RestWrapper : IRestWrapper
+{
+
+ private static RefitSettings setting = new RefitSettings(new NewtonsoftJsonContentSerializer(new JsonSerializerSettings
+ {
+ Formatting = Newtonsoft.Json.Formatting.Indented,
+ ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore,
+
+ }));
+
+ public ICrudApiRest CrudApiRest(string address) where T : class
+ {
+ return RestService.For>(address, setting);
+ }
+ public ICrudDtoApiRest CrudDtoApiRest(string address) where T : class where TDto : class
+ {
+ return RestService.For>(address, setting);
+ }
+ public IAuthRestApi AuthRestApi => RestService.For(Address.AuthController, setting);
+ public IUserRestApi UserRestApi => RestService.For(Address.UserController, setting);
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Utilities/IUserUtility.cs b/NetinaShop.AdminPanel.PWA/Utilities/IUserUtility.cs
new file mode 100644
index 0000000..39276b8
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Utilities/IUserUtility.cs
@@ -0,0 +1,11 @@
+namespace NetinaShop.AdminPanel.PWA.Utilities;
+
+public interface IUserUtility
+{
+ public Task GetBearerTokenAsync();
+ public Task SetBearerTokenAsync(string token);
+ public Task GetUserAsync();
+ public Task SetUserAsync(ApplicationUserSDto user);
+ public Task LogoutAsync();
+
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Utilities/UserUtility.cs b/NetinaShop.AdminPanel.PWA/Utilities/UserUtility.cs
new file mode 100644
index 0000000..812948f
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Utilities/UserUtility.cs
@@ -0,0 +1,26 @@
+namespace NetinaShop.AdminPanel.PWA.Utilities;
+
+public class UserUtility : IUserUtility
+{
+ private readonly ILocalStorageService _localStorageService;
+
+ public UserUtility(ILocalStorageService localStorageService)
+ {
+ _localStorageService = localStorageService;
+ }
+
+ public async Task GetBearerTokenAsync() => await _localStorageService.GetItemAsStringAsync(LocalStorageKeys.Token);
+ public async Task SetBearerTokenAsync(string token) => await _localStorageService.SetItemAsStringAsync(LocalStorageKeys.Token, token);
+
+ public async Task GetUserAsync() => await _localStorageService.GetItemAsync(LocalStorageKeys.UserInfo);
+ public async Task SetUserAsync(ApplicationUserSDto user) => await _localStorageService.SetItemAsync(LocalStorageKeys.UserInfo, user);
+ public async Task LogoutAsync()
+ {
+ await _localStorageService.RemoveItemAsync(LocalStorageKeys.Token);
+ await _localStorageService.RemoveItemAsync(LocalStorageKeys.UserInfo);
+ }
+
+ //public AccessToken? AccessToken { get; set; }
+ //public List UserClaims => AccessToken == null ? new List() : AccessToken.Permissions;
+ //public bool HasPermissionTo(string permission) => UserClaims.Any(c => c == permission);
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/Utilities/UtilityWrapper.cs b/NetinaShop.AdminPanel.PWA/Utilities/UtilityWrapper.cs
new file mode 100644
index 0000000..eb72432
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/Utilities/UtilityWrapper.cs
@@ -0,0 +1,11 @@
+namespace NetinaShop.AdminPanel.PWA.Utilities;
+
+public class UtilityWrapper
+{
+ private static UtilityWrapper? _instance;
+ public static UtilityWrapper Instance
+ {
+ get { return _instance ??= new UtilityWrapper(); }
+ }
+
+}
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/_Imports.razor b/NetinaShop.AdminPanel.PWA/_Imports.razor
index 1e9ad3c..6a75c3d 100644
--- a/NetinaShop.AdminPanel.PWA/_Imports.razor
+++ b/NetinaShop.AdminPanel.PWA/_Imports.razor
@@ -8,3 +8,16 @@
@using Microsoft.JSInterop
@using NetinaShop.AdminPanel.PWA
@using NetinaShop.AdminPanel.PWA.Layout
+@using MudBlazor
+@using Toolbelt.Blazor.PWA.Updater.Service
+@using Toolbelt.Blazor.PWA.Updater
+@using NetinaShop.AdminPanel.PWA.Components.Originals
+@using NetinaShop.Domain.Dtos.SmallDtos
+@using NetinaShop.AdminPanel.PWA.Dialogs
+@using NetinaShop.Common.Models.Api
+@using NetinaShop.Common.Extensions
+@using NetinaShop.AdminPanel.PWA.Models.Api
+@using NetinaShop.AdminPanel.PWA.Models
+@using Refit
+@using NetinaShop.Domain.Entities.ProductCategories
+@using NetinaShop.AdminPanel.PWA.Services.RestServices
\ No newline at end of file
diff --git a/NetinaShop.AdminPanel.PWA/tailwind.config.js b/NetinaShop.AdminPanel.PWA/tailwind.config.js
index ba45062..e22dfef 100644
--- a/NetinaShop.AdminPanel.PWA/tailwind.config.js
+++ b/NetinaShop.AdminPanel.PWA/tailwind.config.js
@@ -24,23 +24,6 @@ module.exports = {
"iranyekan": ["'iranyekan'"],
},
extend: {
- colors: {
- secondary: {
- 100: "#0a202b",
- 200: "#0f222b",
- 300: "#162830",
- 400: "#192930",
- 500: "#1c2b31",
- 600: "#1e2b30",
- 700: "#1d272b",
- 800: "#21292c",
- 900: "#212729",
- },
- visa2: {
- 100: "#F0BC5E",
- 200: "#eca521",
- },
- },
}
}
}
diff --git a/NetinaShop.AdminPanel.PWA/wwwroot/css/app.css b/NetinaShop.AdminPanel.PWA/wwwroot/css/app.css
index 75770c5..d90bca3 100644
--- a/NetinaShop.AdminPanel.PWA/wwwroot/css/app.css
+++ b/NetinaShop.AdminPanel.PWA/wwwroot/css/app.css
@@ -88,10 +88,9 @@
}
:root {
- --color-primary: rgba(53, 104, 89, 1);
- --color-secondary: rgba(253, 85, 35, 1);
- --color-medicalhistory: rgba(253, 216, 53, 1);
- --color-medicalhistory-template: rgba(41, 187, 189, 1);
+ --color-primary: rgba(9, 16, 68, 1);
+ --color-secondary: rgba(229, 159, 46, 1);
+ --color-background: rgba(243, 244, 246, 1);
}
}
diff --git a/NetinaShop.AdminPanel.PWA/wwwroot/css/app.min.css b/NetinaShop.AdminPanel.PWA/wwwroot/css/app.min.css
index 1f8b14b..6ab7b13 100644
--- a/NetinaShop.AdminPanel.PWA/wwwroot/css/app.min.css
+++ b/NetinaShop.AdminPanel.PWA/wwwroot/css/app.min.css
@@ -397,10 +397,9 @@ video {
}
:root {
- --color-primary: rgba(53, 104, 89, 1);
- --color-secondary: rgba(253, 85, 35, 1);
- --color-medicalhistory: rgba(253, 216, 53, 1);
- --color-medicalhistory-template: rgba(41, 187, 189, 1);
+ --color-primary: rgba(9, 16, 68, 1);
+ --color-secondary: rgba(229, 159, 46, 1);
+ --color-background: rgba(243, 244, 246, 1);
}
*, ::before, ::after {
@@ -502,12 +501,94 @@ video {
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
}
-.table {
- display: table;
+.mx-4 {
+ margin-left: 1rem;
+ margin-right: 1rem;
+}
+.mx-auto {
+ margin-left: auto;
+ margin-right: auto;
+}
+.my-1 {
+ margin-top: 0.25rem;
+ margin-bottom: 0.25rem;
+}
+.my-auto {
+ margin-top: auto;
+ margin-bottom: auto;
+}
+.-ml-4 {
+ margin-left: -1rem;
+}
+.-mt-3 {
+ margin-top: -0.75rem;
+}
+.mb-10 {
+ margin-bottom: 2.5rem;
+}
+.mb-5 {
+ margin-bottom: 1.25rem;
+}
+.mr-1 {
+ margin-right: 0.25rem;
+}
+.mr-2 {
+ margin-right: 0.5rem;
+}
+.mt-3 {
+ margin-top: 0.75rem;
+}
+.mt-4 {
+ margin-top: 1rem;
+}
+.mt-5 {
+ margin-top: 1.25rem;
+}
+.flex {
+ display: flex;
+}
+.h-screen {
+ height: 100vh;
+}
+.w-full {
+ width: 100%;
+}
+.w-screen {
+ width: 100vw;
+}
+.flex-row {
+ flex-direction: row;
+}
+.justify-end {
+ justify-content: flex-end;
}
.border {
border-width: 1px;
}
+.bg-\[--color-background\] {
+ background-color: var(--color-background);
+}
+.bg-white {
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
+}
+.p-8 {
+ padding: 2rem;
+}
+.py-2 {
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+}
+.py-3 {
+ padding-top: 0.75rem;
+ padding-bottom: 0.75rem;
+}
+.pt-8 {
+ padding-top: 2rem;
+}
+.font-bold {
+ font-weight: 700;
+}
@font-face {
font-family: iranyekan;
diff --git a/NetinaShop.AdminPanel.PWA/wwwroot/css/app.output.css b/NetinaShop.AdminPanel.PWA/wwwroot/css/app.output.css
new file mode 100644
index 0000000..67cbfdc
--- /dev/null
+++ b/NetinaShop.AdminPanel.PWA/wwwroot/css/app.output.css
@@ -0,0 +1,886 @@
+/*
+! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com
+*/
+
+/*
+1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
+2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
+*/
+
+*,
+::before,
+::after {
+ box-sizing: border-box;
+ /* 1 */
+ border-width: 0;
+ /* 2 */
+ border-style: solid;
+ /* 2 */
+ border-color: #e5e7eb;
+ /* 2 */
+}
+
+::before,
+::after {
+ --tw-content: '';
+}
+
+/*
+1. Use a consistent sensible line-height in all browsers.
+2. Prevent adjustments of font size after orientation changes in iOS.
+3. Use a more readable tab size.
+4. Use the user's configured `sans` font-family by default.
+5. Use the user's configured `sans` font-feature-settings by default.
+6. Use the user's configured `sans` font-variation-settings by default.
+7. Disable tap highlights on iOS
+*/
+
+html,
+:host {
+ line-height: 1.5;
+ /* 1 */
+ -webkit-text-size-adjust: 100%;
+ /* 2 */
+ -moz-tab-size: 4;
+ /* 3 */
+ -o-tab-size: 4;
+ tab-size: 4;
+ /* 3 */
+ font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ /* 4 */
+ font-feature-settings: normal;
+ /* 5 */
+ font-variation-settings: normal;
+ /* 6 */
+ -webkit-tap-highlight-color: transparent;
+ /* 7 */
+}
+
+/*
+1. Remove the margin in all browsers.
+2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
+*/
+
+body {
+ margin: 0;
+ /* 1 */
+ line-height: inherit;
+ /* 2 */
+}
+
+/*
+1. Add the correct height in Firefox.
+2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
+3. Ensure horizontal rules are visible by default.
+*/
+
+hr {
+ height: 0;
+ /* 1 */
+ color: inherit;
+ /* 2 */
+ border-top-width: 1px;
+ /* 3 */
+}
+
+/*
+Add the correct text decoration in Chrome, Edge, and Safari.
+*/
+
+abbr:where([title]) {
+ -webkit-text-decoration: underline dotted;
+ text-decoration: underline dotted;
+}
+
+/*
+Remove the default font size and weight for headings.
+*/
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-size: inherit;
+ font-weight: inherit;
+}
+
+/*
+Reset links to optimize for opt-in styling instead of opt-out.
+*/
+
+a {
+ color: inherit;
+ text-decoration: inherit;
+}
+
+/*
+Add the correct font weight in Edge and Safari.
+*/
+
+b,
+strong {
+ font-weight: bolder;
+}
+
+/*
+1. Use the user's configured `mono` font-family by default.
+2. Use the user's configured `mono` font-feature-settings by default.
+3. Use the user's configured `mono` font-variation-settings by default.
+4. Correct the odd `em` font sizing in all browsers.
+*/
+
+code,
+kbd,
+samp,
+pre {
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ /* 1 */
+ font-feature-settings: normal;
+ /* 2 */
+ font-variation-settings: normal;
+ /* 3 */
+ font-size: 1em;
+ /* 4 */
+}
+
+/*
+Add the correct font size in all browsers.
+*/
+
+small {
+ font-size: 80%;
+}
+
+/*
+Prevent `sub` and `sup` elements from affecting the line height in all browsers.
+*/
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+sup {
+ top: -0.5em;
+}
+
+/*
+1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
+2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
+3. Remove gaps between table borders by default.
+*/
+
+table {
+ text-indent: 0;
+ /* 1 */
+ border-color: inherit;
+ /* 2 */
+ border-collapse: collapse;
+ /* 3 */
+}
+
+/*
+1. Change the font styles in all browsers.
+2. Remove the margin in Firefox and Safari.
+3. Remove default padding in all browsers.
+*/
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ font-family: inherit;
+ /* 1 */
+ font-feature-settings: inherit;
+ /* 1 */
+ font-variation-settings: inherit;
+ /* 1 */
+ font-size: 100%;
+ /* 1 */
+ font-weight: inherit;
+ /* 1 */
+ line-height: inherit;
+ /* 1 */
+ color: inherit;
+ /* 1 */
+ margin: 0;
+ /* 2 */
+ padding: 0;
+ /* 3 */
+}
+
+/*
+Remove the inheritance of text transform in Edge and Firefox.
+*/
+
+button,
+select {
+ text-transform: none;
+}
+
+/*
+1. Correct the inability to style clickable types in iOS and Safari.
+2. Remove default button styles.
+*/
+
+button,
+[type='button'],
+[type='reset'],
+[type='submit'] {
+ -webkit-appearance: button;
+ /* 1 */
+ background-color: transparent;
+ /* 2 */
+ background-image: none;
+ /* 2 */
+}
+
+/*
+Use the modern Firefox focus style for all focusable elements.
+*/
+
+:-moz-focusring {
+ outline: auto;
+}
+
+/*
+Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
+*/
+
+:-moz-ui-invalid {
+ box-shadow: none;
+}
+
+/*
+Add the correct vertical alignment in Chrome and Firefox.
+*/
+
+progress {
+ vertical-align: baseline;
+}
+
+/*
+Correct the cursor style of increment and decrement buttons in Safari.
+*/
+
+::-webkit-inner-spin-button,
+::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/*
+1. Correct the odd appearance in Chrome and Safari.
+2. Correct the outline style in Safari.
+*/
+
+[type='search'] {
+ -webkit-appearance: textfield;
+ /* 1 */
+ outline-offset: -2px;
+ /* 2 */
+}
+
+/*
+Remove the inner padding in Chrome and Safari on macOS.
+*/
+
+::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/*
+1. Correct the inability to style clickable types in iOS and Safari.
+2. Change font properties to `inherit` in Safari.
+*/
+
+::-webkit-file-upload-button {
+ -webkit-appearance: button;
+ /* 1 */
+ font: inherit;
+ /* 2 */
+}
+
+/*
+Add the correct display in Chrome and Safari.
+*/
+
+summary {
+ display: list-item;
+}
+
+/*
+Removes the default spacing and border for appropriate elements.
+*/
+
+blockquote,
+dl,
+dd,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+hr,
+figure,
+p,
+pre {
+ margin: 0;
+}
+
+fieldset {
+ margin: 0;
+ padding: 0;
+}
+
+legend {
+ padding: 0;
+}
+
+ol,
+ul,
+menu {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+/*
+Reset default styling for dialogs.
+*/
+
+dialog {
+ padding: 0;
+}
+
+/*
+Prevent resizing textareas horizontally by default.
+*/
+
+textarea {
+ resize: vertical;
+}
+
+/*
+1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
+2. Set the default placeholder color to the user's configured gray 400 color.
+*/
+
+input::-moz-placeholder, textarea::-moz-placeholder {
+ opacity: 1;
+ /* 1 */
+ color: #9ca3af;
+ /* 2 */
+}
+
+input::placeholder,
+textarea::placeholder {
+ opacity: 1;
+ /* 1 */
+ color: #9ca3af;
+ /* 2 */
+}
+
+/*
+Set the default cursor for buttons.
+*/
+
+button,
+[role="button"] {
+ cursor: pointer;
+}
+
+/*
+Make sure disabled buttons don't get the pointer cursor.
+*/
+
+:disabled {
+ cursor: default;
+}
+
+/*
+1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
+2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
+ This can trigger a poorly considered lint error in some tools but is included by design.
+*/
+
+img,
+svg,
+video,
+canvas,
+audio,
+iframe,
+embed,
+object {
+ display: block;
+ /* 1 */
+ vertical-align: middle;
+ /* 2 */
+}
+
+/*
+Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
+*/
+
+img,
+video {
+ max-width: 100%;
+ height: auto;
+}
+
+/* Make elements with the HTML hidden attribute stay hidden by default */
+
+[hidden] {
+ display: none;
+}
+
+* {
+ font-family: iranyekan;
+}
+
+:root {
+ --color-primary: rgba(9, 16, 68, 1);
+ --color-secondary: rgba(229, 159, 46, 1);
+ --color-background: rgba(243, 244, 246, 1);
+}
+
+*, ::before, ::after {
+ --tw-border-spacing-x: 0;
+ --tw-border-spacing-y: 0;
+ --tw-translate-x: 0;
+ --tw-translate-y: 0;
+ --tw-rotate: 0;
+ --tw-skew-x: 0;
+ --tw-skew-y: 0;
+ --tw-scale-x: 1;
+ --tw-scale-y: 1;
+ --tw-pan-x: ;
+ --tw-pan-y: ;
+ --tw-pinch-zoom: ;
+ --tw-scroll-snap-strictness: proximity;
+ --tw-gradient-from-position: ;
+ --tw-gradient-via-position: ;
+ --tw-gradient-to-position: ;
+ --tw-ordinal: ;
+ --tw-slashed-zero: ;
+ --tw-numeric-figure: ;
+ --tw-numeric-spacing: ;
+ --tw-numeric-fraction: ;
+ --tw-ring-inset: ;
+ --tw-ring-offset-width: 0px;
+ --tw-ring-offset-color: #fff;
+ --tw-ring-color: rgb(59 130 246 / 0.5);
+ --tw-ring-offset-shadow: 0 0 #0000;
+ --tw-ring-shadow: 0 0 #0000;
+ --tw-shadow: 0 0 #0000;
+ --tw-shadow-colored: 0 0 #0000;
+ --tw-blur: ;
+ --tw-brightness: ;
+ --tw-contrast: ;
+ --tw-grayscale: ;
+ --tw-hue-rotate: ;
+ --tw-invert: ;
+ --tw-saturate: ;
+ --tw-sepia: ;
+ --tw-drop-shadow: ;
+ --tw-backdrop-blur: ;
+ --tw-backdrop-brightness: ;
+ --tw-backdrop-contrast: ;
+ --tw-backdrop-grayscale: ;
+ --tw-backdrop-hue-rotate: ;
+ --tw-backdrop-invert: ;
+ --tw-backdrop-opacity: ;
+ --tw-backdrop-saturate: ;
+ --tw-backdrop-sepia: ;
+}
+
+::backdrop {
+ --tw-border-spacing-x: 0;
+ --tw-border-spacing-y: 0;
+ --tw-translate-x: 0;
+ --tw-translate-y: 0;
+ --tw-rotate: 0;
+ --tw-skew-x: 0;
+ --tw-skew-y: 0;
+ --tw-scale-x: 1;
+ --tw-scale-y: 1;
+ --tw-pan-x: ;
+ --tw-pan-y: ;
+ --tw-pinch-zoom: ;
+ --tw-scroll-snap-strictness: proximity;
+ --tw-gradient-from-position: ;
+ --tw-gradient-via-position: ;
+ --tw-gradient-to-position: ;
+ --tw-ordinal: ;
+ --tw-slashed-zero: ;
+ --tw-numeric-figure: ;
+ --tw-numeric-spacing: ;
+ --tw-numeric-fraction: ;
+ --tw-ring-inset: ;
+ --tw-ring-offset-width: 0px;
+ --tw-ring-offset-color: #fff;
+ --tw-ring-color: rgb(59 130 246 / 0.5);
+ --tw-ring-offset-shadow: 0 0 #0000;
+ --tw-ring-shadow: 0 0 #0000;
+ --tw-shadow: 0 0 #0000;
+ --tw-shadow-colored: 0 0 #0000;
+ --tw-blur: ;
+ --tw-brightness: ;
+ --tw-contrast: ;
+ --tw-grayscale: ;
+ --tw-hue-rotate: ;
+ --tw-invert: ;
+ --tw-saturate: ;
+ --tw-sepia: ;
+ --tw-drop-shadow: ;
+ --tw-backdrop-blur: ;
+ --tw-backdrop-brightness: ;
+ --tw-backdrop-contrast: ;
+ --tw-backdrop-grayscale: ;
+ --tw-backdrop-hue-rotate: ;
+ --tw-backdrop-invert: ;
+ --tw-backdrop-opacity: ;
+ --tw-backdrop-saturate: ;
+ --tw-backdrop-sepia: ;
+}
+
+.mx-4 {
+ margin-left: 1rem;
+ margin-right: 1rem;
+}
+
+.mx-auto {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.my-1 {
+ margin-top: 0.25rem;
+ margin-bottom: 0.25rem;
+}
+
+.my-auto {
+ margin-top: auto;
+ margin-bottom: auto;
+}
+
+.-ml-4 {
+ margin-left: -1rem;
+}
+
+.-mt-3 {
+ margin-top: -0.75rem;
+}
+
+.mb-10 {
+ margin-bottom: 2.5rem;
+}
+
+.mb-5 {
+ margin-bottom: 1.25rem;
+}
+
+.mr-1 {
+ margin-right: 0.25rem;
+}
+
+.mr-2 {
+ margin-right: 0.5rem;
+}
+
+.mt-4 {
+ margin-top: 1rem;
+}
+
+.mt-5 {
+ margin-top: 1.25rem;
+}
+
+.mt-3 {
+ margin-top: 0.75rem;
+}
+
+.flex {
+ display: flex;
+}
+
+.h-screen {
+ height: 100vh;
+}
+
+.w-full {
+ width: 100%;
+}
+
+.w-screen {
+ width: 100vw;
+}
+
+.flex-row {
+ flex-direction: row;
+}
+
+.justify-end {
+ justify-content: flex-end;
+}
+
+.border {
+ border-width: 1px;
+}
+
+.bg-\[--color-background\] {
+ background-color: var(--color-background);
+}
+
+.bg-white {
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
+}
+
+.p-8 {
+ padding: 2rem;
+}
+
+.py-2 {
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+}
+
+.py-3 {
+ padding-top: 0.75rem;
+ padding-bottom: 0.75rem;
+}
+
+.pt-8 {
+ padding-top: 2rem;
+}
+
+.font-bold {
+ font-weight: 700;
+}
+
+@font-face {
+ font-family: iranyekan;
+
+ font-style: normal;
+
+ font-weight: bold;
+
+ src: url('../assets/fonts/eot/iranyekanwebboldfanum.eot');
+
+ src: url('../assets/fonts/eot/iranyekanwebboldfanum.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('../assets/fonts/woff/iranyekanwebboldfanum.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('../assets/fonts/ttf/iranyekanwebboldfanum.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: iranyekan;
+
+ font-style: normal;
+
+ font-weight: 100;
+
+ src: url('../assets/fonts/eot/iranyekanwebthinfanum.eot');
+
+ src: url('../assets/fonts/eot/iranyekanwebthinfanum.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('../assets/fonts/woff/iranyekanwebthinfanum.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('../assets/fonts/ttf/iranyekanwebthinfanum.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: iranyekan;
+
+ font-style: normal;
+
+ font-weight: 300;
+
+ src: url('../assets/fonts/eot/iranyekanweblightfanum.eot');
+
+ src: url('../assets/fonts/eot/iranyekanweblightfanum.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('../assets/fonts/woff/iranyekanweblightfanum.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('../assets/fonts/ttf/iranyekanweblightfanum.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: iranyekan;
+
+ font-style: normal;
+
+ font-weight: normal;
+
+ src: url('../assets/fonts/eot/iranyekanwebregularfanum.eot');
+
+ src: url('../assets/fonts/eot/iranyekanwebregularfanum.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('../assets/fonts/woff/iranyekanwebregularfanum.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('../assets/fonts/ttf/iranyekanwebregularfanum.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: iranyekan;
+
+ font-style: normal;
+
+ font-weight: 500;
+
+ src: url('../assets/fonts/eot/iranyekanwebmediumfanum.eot');
+
+ src: url('../assets/fonts/eot/iranyekanwebmediumfanum.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('../assets/fonts/woff/iranyekanwebmediumfanum.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('../assets/fonts/ttf/iranyekanwebmediumfanum.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: iranyekan;
+
+ font-style: normal;
+
+ font-weight: 800;
+
+ src: url('../assets/fonts/eot/iranyekanwebextraboldfanum.eot');
+
+ src: url('../assets/fonts/eot/iranyekanwebextraboldfanum.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('../assets/fonts/woff/iranyekanwebextraboldfanum.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('../assets/fonts/ttf/iranyekanwebextraboldfanum.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: iranyekan;
+
+ font-style: normal;
+
+ font-weight: 900;
+
+ src: url('../assets/fonts/eot/iranyekanwebblackfanum.eot');
+
+ src: url('../assets/fonts/eot/iranyekanwebblackfanum.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('../assets/fonts/woff/iranyekanwebblackfanum.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('../assets/fonts/ttf/iranyekanwebblackfanum.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: iranyekan;
+
+ font-style: normal;
+
+ font-weight: 950;
+
+ src: url('../assets/fonts/eot/iranyekanwebextrablackfanum.eot');
+
+ src: url('../assets/fonts/eot/iranyekanwebextrablackfanum.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
+ url('../assets/fonts/woff/iranyekanwebextrablackfanum.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
+ url('../assets/fonts/ttf/iranyekanwebextrablackfanum.ttf') format('truetype');
+}
+
+.mud-dialog-title {
+ font-family: iranyekan !important;
+}
+
+h1:focus {
+ outline: none;
+}
+
+a, .btn-link {
+ color: #0071c1;
+}
+
+.btn-primary {
+ color: #fff;
+ background-color: #1b6ec2;
+ border-color: #1861ac;
+}
+
+.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
+ box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
+}
+
+.content {
+ padding-top: 1.1rem;
+}
+
+.valid.modified:not([type=checkbox]) {
+ outline: 1px solid #26b050;
+}
+
+.invalid {
+ outline: 1px solid red;
+}
+
+.validation-message {
+ color: red;
+}
+
+#blazor-error-ui {
+ background: lightyellow;
+ bottom: 0;
+ box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
+ display: none;
+ left: 0;
+ padding: 0.6rem 1.25rem 0.7rem 1.25rem;
+ position: fixed;
+ width: 100%;
+ z-index: 1000;
+}
+
+#blazor-error-ui .dismiss {
+ cursor: pointer;
+ position: absolute;
+ right: 0.75rem;
+ top: 0.5rem;
+}
+
+.blazor-error-boundary {
+ background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
+ padding: 1rem 1rem 1rem 3.7rem;
+ color: white;
+}
+
+.blazor-error-boundary::after {
+ content: "An error has occurred."
+}
+
+.loading-progress {
+ position: relative;
+ display: block;
+ width: 8rem;
+ height: 8rem;
+ margin: 20vh auto 1rem auto;
+}
+
+.loading-progress circle {
+ fill: none;
+ stroke: #e0e0e0;
+ stroke-width: 0.6rem;
+ transform-origin: 50% 50%;
+ transform: rotate(-90deg);
+}
+
+.loading-progress circle:last-child {
+ stroke: #1b6ec2;
+ stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
+ transition: stroke-dasharray 0.05s ease-in-out;
+}
+
+.loading-progress-text {
+ position: absolute;
+ text-align: center;
+ font-weight: bold;
+ inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
+}
+
+.loading-progress-text:after {
+ content: var(--blazor-load-percentage-text, "Loading");
+}
+
+code {
+ color: #c02d76;
+}
diff --git a/NetinaShop.AdminPanel.PWA/wwwroot/index.html b/NetinaShop.AdminPanel.PWA/wwwroot/index.html
index afab26b..13fda64 100644
--- a/NetinaShop.AdminPanel.PWA/wwwroot/index.html
+++ b/NetinaShop.AdminPanel.PWA/wwwroot/index.html
@@ -20,6 +20,8 @@
+
+
@@ -39,6 +41,7 @@
🗙
+
diff --git a/NetinaShop.AdminPanel.sln b/NetinaShop.AdminPanel.sln
index e86122f..115824d 100644
--- a/NetinaShop.AdminPanel.sln
+++ b/NetinaShop.AdminPanel.sln
@@ -3,7 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34316.72
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetinaShop.AdminPanel.PWA", "NetinaShop.AdminPanel.PWA\NetinaShop.AdminPanel.PWA.csproj", "{EB154E26-A392-4521-B26D-1B82C8597201}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetinaShop.AdminPanel.PWA", "NetinaShop.AdminPanel.PWA\NetinaShop.AdminPanel.PWA.csproj", "{EB154E26-A392-4521-B26D-1B82C8597201}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetinaShop.Domain", "..\NetinaShop\NetinaShop.Domain\NetinaShop.Domain.csproj", "{FD9AD662-66FB-431A-A2C1-8D6262402FCE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetinaShop.Common", "..\NetinaShop\NetinaShop.Common\NetinaShop.Common.csproj", "{CE60F736-C3F2-4D81-862E-48135E5FFC9C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +19,14 @@ Global
{EB154E26-A392-4521-B26D-1B82C8597201}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB154E26-A392-4521-B26D-1B82C8597201}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB154E26-A392-4521-B26D-1B82C8597201}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FD9AD662-66FB-431A-A2C1-8D6262402FCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FD9AD662-66FB-431A-A2C1-8D6262402FCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FD9AD662-66FB-431A-A2C1-8D6262402FCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FD9AD662-66FB-431A-A2C1-8D6262402FCE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CE60F736-C3F2-4D81-862E-48135E5FFC9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CE60F736-C3F2-4D81-862E-48135E5FFC9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CE60F736-C3F2-4D81-862E-48135E5FFC9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CE60F736-C3F2-4D81-862E-48135E5FFC9C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE