confige project

add base files , add tailwind , add mudblazor , add pages , config category page
release
Amir Hossein Khademi 2024-01-22 17:26:29 +03:30
parent 5eb95445db
commit 493042812d
31 changed files with 1858 additions and 50 deletions

View File

@ -0,0 +1,73 @@
@inject NavigationManager NavigationManager
<MudStack class="w-full pt-8 h-screen bg-white">
@* <MudImage class="mx-15 mt-5" Src="https://vesmeh.com/wp-content/uploads/2023/02/logo-web.png.webp"></MudImage> *@
<MudTreeView T="TreeItemData" Items="@TreeItems" Hover="true" MultiSelection="false" SelectedValueChanged="SelectedChange">
<ItemTemplate Context="item">
<MudTreeViewItem Value="@item" Items="@item.TreeItems" Icon="@item.Icon">
<BodyContent>
<div class="py-3">
<MudText>@item.Text</MudText>
</div>
</BodyContent>
</MudTreeViewItem>
</ItemTemplate>
</MudTreeView>
</MudStack>
@code
{
private void SelectedChange(TreeItemData? data)
{
if (data != null)
NavigationManager.NavigateTo(data.Page);
}
private HashSet<TreeItemData> TreeItems { get; set; } = new HashSet<TreeItemData>();
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<TreeItemData> TreeItems { get; set; } = new HashSet<TreeItemData>();
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"));
}
}

View File

@ -0,0 +1,24 @@
<MudDialog>
<DialogContent>
<p>@ContentText</p>
</DialogContent>
<DialogActions>
<div class="flex flex-row w-full mt-3">
<MudButton Color="Color.Info" Variant="Variant.Filled" OnClick="Submit" DisableElevation="true">تایید</MudButton>
<MudButton Variant="Variant.Outlined" Color="Color.Error" OnClick="Cancel" class="mx-4">انصراف</MudButton>
</div>
</DialogActions>
</MudDialog>
@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();
}

View File

@ -0,0 +1,80 @@
@using Radzen.Blazor
<MudDialog class="mx-auto">
<DialogContent>
<MudStack>
<MudDivider/>
<MudStack Spacing="0">
<MudText Typo="Typo.h6">اطلاعات کلی</MudText>
<MudText Typo="Typo.caption">اطلاعات کلی محصول را به دقت وارد کنید</MudText>
</MudStack>
<MudGrid>
<MudItem lg="4" md="6">
<MudTextField T="string" Label="نام فارسی محصول" Variant="Variant.Outlined"></MudTextField>
</MudItem>
<MudItem lg="4" md="6">
<MudTextField T="string" Label="نام انگلیسی محصول" Variant="Variant.Outlined"></MudTextField>
</MudItem>
<MudItem lg="4" md="6">
<MudTextField T="string" Label="قیمت محصول" Variant="Variant.Outlined"></MudTextField>
</MudItem>
<MudItem lg="4" md="6">
<MudTextField T="string" Label="مبلغ بسته بندی" Variant="Variant.Outlined"></MudTextField>
</MudItem>
<MudItem lg="4" md="6">
<MudTextField T="string" Label="بیشترین خرید" Variant="Variant.Outlined"></MudTextField>
</MudItem>
<MudItem lg="4" md="6">
<MudTextField T="string" Label="ایا ارسال سریع دارد" Variant="Variant.Outlined"></MudTextField>
</MudItem>
<MudItem lg="12" md="12">
<MudTextField T="string" Label="توضیحاتــ" Variant="Variant.Outlined"></MudTextField>
</MudItem>
<MudStack class="mt-4 mx-4" Spacing="0">
<MudText Typo="Typo.h6">توضیحات تکمیلی</MudText>
<MudText Typo="Typo.caption">می توانید توضیحاتــ تکمیلی محصول را کامل وارد کنید</MudText>
</MudStack>
<MudItem lg="12" md="12">
<RadzenHtmlEditor>
<RadzenHtmlEditorUndo/>
<RadzenHtmlEditorRedo/>
<RadzenHtmlEditorSeparator/>
<RadzenHtmlEditorAlignLeft/>
<RadzenHtmlEditorAlignCenter/>
<RadzenHtmlEditorAlignRight/>
<RadzenHtmlEditorJustify/>
<RadzenHtmlEditorSeparator/>
<RadzenHtmlEditorBold/>
<RadzenHtmlEditorItalic/>
<RadzenHtmlEditorUnderline/>
<RadzenHtmlEditorStrikeThrough/>
<RadzenHtmlEditorSeparator/>
<RadzenHtmlEditorRemoveFormat/>
</RadzenHtmlEditor>
</MudItem>
<MudStack class="mt-4 mb-10 mx-4" Spacing="0">
<MudText Typo="Typo.h6">تصاویر محصول</MudText>
<MudText Typo="Typo.caption">می توانید برای محصول چند تصویر اپلود کنید</MudText>
</MudStack>
</MudGrid>
</MudStack>
</DialogContent>
<DialogActions>
<MudStack class="w-full" Row="true">
<MudButton DisableElevation="true" Size="Size.Large" StartIcon="@Icons.Material.Outlined.Check" Variant="Variant.Filled" Color="Color.Success" OnClick="Submit">تایید</MudButton>
<MudButton Variant="Variant.Outlined" Size="Size.Large" Color="Color.Error" OnClick="Cancel">بستن</MudButton>
</MudStack>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
void Submit() => MudDialog.Close(DialogResult.Ok(true));
void Cancel() => MudDialog.Cancel();
}

View File

@ -0,0 +1,95 @@
@using Blazorise.Extensions
@inject ISnackbar Snackbar
@inject IRestWrapper RestWrapper
<MudDialog class="mx-auto">
<DialogContent>
<MudStack>
<MudDivider class="-mt-3" />
<MudStack Spacing="0">
<MudText Typo="Typo.h6">اطلاعات کلی</MudText>
<MudText Typo="Typo.caption">اطلاعات کلی دسته بندی محصول را به دقت وارد کنید</MudText>
</MudStack>
<MudGrid>
<MudItem lg="4" md="6">
<MudTextField T="string" Label="نام دسته بندی" Variant="Variant.Outlined"></MudTextField>
</MudItem>
<MudItem lg="4" md="6">
<MudSelect T="bool" Label="آیا دسته بندی اصلی است ؟" ToStringFunc="b=>b.ToPersianString()" Variant="Variant.Outlined" AnchorOrigin="Origin.BottomCenter">
<MudSelectItem T="bool" Value="true" ></MudSelectItem>
<MudSelectItem T="bool" Value="false" />
</MudSelect>
</MudItem>
<MudItem lg="4" md="6">
<MudAutocomplete Required="true" ToStringFunc="dto => dto.Name" @bind-Value="@_selectedCategory"
SearchFunc="SearchCity"
T="ProductCategorySDto"
Label="دسته پدر"
Variant="Variant.Outlined">
<ProgressIndicatorInPopoverTemplate>
<MudList Clickable="false">
<MudListItem>
<div class="flex flex-row w-full mx-auto">
<MudProgressCircular class="my-auto mr-1 -ml-4" Size="Size.Small" Indeterminate="true" />
<p class="font-bold my-1 mx-auto text-md">منتظر بمانید</p>
</div>
</MudListItem>
</MudList>
</ProgressIndicatorInPopoverTemplate>
<ItemTemplate Context="e">
<p>@e.Name</p>
</ItemTemplate>
</MudAutocomplete>
</MudItem>
<MudItem lg="12" md="12">
<MudTextField T="string" Label="توضیحاتــ" Variant="Variant.Outlined"></MudTextField>
</MudItem>
</MudGrid>
</MudStack>
</DialogContent>
<DialogActions>
<MudStack class="w-full mx-4" Row="true">
<MudButton DisableElevation="true" Size="Size.Large" StartIcon="@Icons.Material.Outlined.Check" Variant="Variant.Filled" Color="Color.Success" OnClick="Submit">تایید</MudButton>
<MudButton Variant="Variant.Outlined" Size="Size.Large" Color="Color.Error" OnClick="Cancel">بستن</MudButton>
</MudStack>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
void Submit() => MudDialog.Close(DialogResult.Ok(true));
void Cancel() => MudDialog.Cancel();
private List<ProductCategorySDto> _productCategories = new List<ProductCategorySDto>();
private ProductCategorySDto? _selectedCategory;
private async Task<IEnumerable<ProductCategorySDto>> SearchCity(string city)
{
try
{
if (_productCategories.Count == 0)
{
_productCategories = await RestWrapper.CrudDtoApiRest<ProductCategory, ProductCategorySDto, Guid>(Address.ProductCategoryController).ReadAll();
}
if (city.IsNullOrEmpty())
return _productCategories;
return _productCategories.Where(c => c.Name.Contains(city));
}
catch (ApiException ex)
{
var exe = await ex.GetContentAsAsync<ApiResult>();
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;
}
}
}

View File

@ -1,7 +1,4 @@
@using Toolbelt.Blazor.PWA.Updater
@using Toolbelt.Blazor.PWA.Updater.Service
@using MudBlazor
@inherits LayoutComponentBase
@inherits LayoutComponentBase
@inject IPWAUpdaterService PwaUpdaterService
<style>
body .pwa-updater[b-pwa-updater] {
@ -18,11 +15,11 @@
body .pwa-updater-updatenow-button {
border: dashed 2px rgba(253, 216, 53, 1) !important;
font-family: iranyekan !important;
font-weight:800 !important;
font-weight: 800 !important;
}
body .pwa-updater-close-button {
color:#fff !important;
color: #fff !important;
}
</style>
<MudRTLProvider RightToLeft="true">
@ -31,12 +28,39 @@
<MudSnackbarProvider />
<MudLayout>
<MudAppBar class="py-2" Color="Color.Transparent" Fixed="false" Elevation="2">
<MudHidden Breakpoint="Breakpoint.SmAndUp">
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" />
</MudHidden>
<MudAvatar Size="Size.Large" Variant="Variant.Outlined">
<MudImage Src="https://img.freepik.com/free-photo/portrait-white-man-isolated_53876-40306.jpg?size=626&ext=jpg&ga=GA1.1.632798143.1705708800&semt=ais"></MudImage>
</MudAvatar>
<MudStack class="mr-2" Spacing="0">
<MudText Color="Color.Inherit" Typo="Typo.body1"><b>امیرحسین خادمی</b></MudText>
<MudText Color="Color.Inherit" Typo="Typo.caption">09214802813</MudText>
</MudStack>
<MudSpacer />
<MudIconButton Size="Size.Medium" Color="Color.Inherit" Icon="@Icons.Material.Outlined.Settings"/>
<MudIconButton Size="Size.Medium" Color="Color.Error" Icon="@Icons.Material.Outlined.ExitToApp" />
</MudAppBar>
<MudGrid Spacing="0">
<MudItem sm="0" md="3" lg="2">
<MudHidden Breakpoint="Breakpoint.SmAndDown">
<SideBarUi/>
</MudHidden>
</MudItem>
<MudItem sm="12" md="9" lg="10">
<div>
@Body
<div dir="ltr">
<PWAUpdater Text="@_updateText" ButtonCaption="اپدیت کنید" />
<PWAUpdater Text="@_updateText" ButtonCaption="اپدیت کنید"/>
</div>
</div>
</MudItem>
</MudGrid>
</MudLayout>
</MudRTLProvider>
@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",
}
};

View File

@ -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";
}

View File

@ -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<TData> : 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; }
}

View File

@ -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<TPageDto>
{
public bool IsProcessing { get; set; } = false;
public TPageDto PageDto { get; set; }
public BaseViewModel()
{
PageDto = Activator.CreateInstance<TPageDto>();
}
public virtual void Initialize()
{
}
public virtual Task InitializeAsync()
{
return Task.CompletedTask;
}
}

View File

@ -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);
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
@ -18,14 +18,35 @@
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0" PrivateAssets="all" />
<PackageReference Include="MudBlazor" Version="6.11.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Radzen.Blazor" Version="4.24.1" />
<PackageReference Include="Refit" Version="7.0.0" />
<PackageReference Include="Refit.HttpClientFactory" Version="7.0.0" />
<PackageReference Include="Refit.Newtonsoft.Json" Version="7.0.0" />
<PackageReference Include="Toolbelt.Blazor.PWA.Updater" Version="2.1.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\NetinaShop\NetinaShop.Domain\NetinaShop.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />
</ItemGroup>
<ItemGroup>
<Folder Include="Utilities\" />
<Folder Include="Services\RestServices\" />
</ItemGroup>
<ItemGroup>
<Using Include="Blazored.LocalStorage" />
<Using Include="NetinaShop.AdminPanel.PWA.Models" />
<Using Include="NetinaShop.Common.Models.Api" />
<Using Include="NetinaShop.Domain.Dtos.RequestDtos" />
<Using Include="NetinaShop.Domain.Dtos.SmallDtos" />
<Using Include="NetinaShop.Domain.Enums" />
<Using Include="Newtonsoft.Json" />
<Using Include="Refit" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,7 @@
@page "/BrandsPage"
<h3>BrandsPage</h3>
@code {
}

View File

@ -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
<MudStack class="w-full p-8 h-screen bg-[--color-background]">
<MudGrid>
<MudItem xs="12">
<MudStack Row="true" class="mb-5">
<MudText Typo="Typo.h4">دسته بندی ها</MudText>
<MudChip Color="Color.Info" Variant="Variant.Outlined">124 عدد</MudChip>
<MudSpacer />
<MudButton Variant="Variant.Filled"
DisableElevation="true"
StartIcon="@Icons.Material.Outlined.Add"
Color="Color.Secondary"
OnClick="ViewModel.AddProductClicked"
class="my-auto">افزودن دسته بندی</MudButton>
</MudStack>
<MudPaper>
<MudDataGrid Striped="true" T="ProductCategorySDto" Items="@ViewModel.PageDto" Filterable="false" Loading="@ViewModel.IsProcessing"
SortMode="@SortMode.None" Groupable="false">
<ToolBarContent>
<MudTextField T="string" Placeholder="جست جو بر اساس نام" Adornment="Adornment.Start" Immediate="true"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" class="my-auto"></MudTextField>
</ToolBarContent>
<Columns>
<PropertyColumn Title="نام دسته" Property="arg => arg.Name"/>
<PropertyColumn Title="توضیحاتــ" Property="arg => arg.Description"/>
<TemplateColumn Title="دسته اصلی" T="ProductCategorySDto">
<CellTemplate>
@if (@context.Item.IsMain)
{
<p>بلی</p>
}
else
{
<p>خیر</p>
}
</CellTemplate>
</TemplateColumn>
<TemplateColumn CellClass="d-flex justify-end">
<CellTemplate>
<MudStack Row="true">
<MudIconButton Icon="@Icons.Material.Filled.Edit" Size="@Size.Small" Variant="@Variant.Outlined" Color="@Color.Info"></MudIconButton>
<MudIconButton Icon="@Icons.Material.Filled.Delete"
Size="@Size.Small"
Variant="@Variant.Outlined"
OnClick="async() => await ViewModel.DeleteProductCategoryAsync(context.Item.Id)"
Color="@Color.Error"></MudIconButton>
</MudStack>
</CellTemplate>
</TemplateColumn>
</Columns>
</MudDataGrid>
</MudPaper>
</MudItem>
</MudGrid>
</MudStack>
@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();
}
}

View File

@ -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<List<ProductCategorySDto>>
{
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<ProductCategory, ProductCategorySDto, Guid>(Address.ProductCategoryController)
.ReadAll();
PageDto = dto;
}
catch (ApiException ex)
{
var exe = await ex.GetContentAsAsync<ApiResult>();
_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<ProductCategoryActionDialogBox>("افزودن دسته جدید", maxWidth);
}
public async Task DeleteProductCategoryAsync(Guid selectedCategoryId)
{
var options = new DialogOptions { CloseOnEscapeKey = true };
var parameters = new DialogParameters<QuestionDialog>();
parameters.Add(x => x.ContentText, "آیا از حذف دسته بندی اطمینان دارید ?");
var dialogReference = await _dialogService.ShowAsync<QuestionDialog>("حذف شرح حال", parameters, options);
var result = await dialogReference.Result;
if (!result.Canceled)
{
try
{
IsProcessing = true;
var token = await _userUtility.GetBearerTokenAsync();
await _restWrapper.CrudDtoApiRest<ProductCategory, ProductCategorySDto, Guid>(Address.ProductCategoryController)
.Delete(selectedCategoryId, token);
_snackbar.Add("حذف دسته بندی با موفقیت انجام شد", Severity.Success);
}
catch (ApiException ex)
{
var exe = await ex.GetContentAsAsync<ApiResult>();
_snackbar.Add(exe != null ? exe.Message : ex.Content, Severity.Error);
}
catch (Exception e)
{
_snackbar.Add(e.Message, Severity.Error);
}
finally
{
IsProcessing = false;
}
}
}
}

View File

@ -1,7 +1,7 @@
@page "/"
@page "/HomePage"
<PageTitle>Home</PageTitle>
<MudStack class="w-screen h-screen bg-[--color-background]">
<MudGrid>
<h1>Hello, world!</h1>
Welcome to your new app.
</MudGrid>
</MudStack>

View File

@ -0,0 +1,95 @@
@page "/ProductsPage"
@using NetinaShop.AdminPanel.PWA.Dialogs
@inject IDialogService DialogService
<MudStack class="w-full p-8 h-screen bg-[--color-background]">
<MudGrid>
<MudItem xs="12">
<MudStack Row="true" class="mb-5">
<MudText Typo="Typo.h4">محصولاتــــ</MudText>
<MudChip Color="Color.Info" Variant="Variant.Outlined">124 عدد</MudChip>
<MudSpacer/>
<MudButton Variant="Variant.Filled"
DisableElevation="true"
StartIcon="@Icons.Material.Outlined.Add"
Color="Color.Secondary"
OnClick="AddProductClicked"
class="my-auto">افزودن محصول</MudButton>
</MudStack>
<MudPaper>
<MudDataGrid T="ProductSDto" Items="@Products" Filterable="false" SortMode="@SortMode.None" Groupable="false">
<Columns>
<PropertyColumn Title="نام محصول" Property="arg => arg.PersianName" />
<PropertyColumn Title="دسته بندی" Property="arg => arg.CategoryName" />
<PropertyColumn Title="برند" Property="arg => arg.BrandNames" />
<PropertyColumn Title="قیمتــ" Property="arg => arg.Cost" />
<TemplateColumn CellClass="d-flex justify-end">
<CellTemplate>
<MudStack Row="true">
<MudIconButton Icon="@Icons.Material.Filled.Edit" Size="@Size.Small" Variant="@Variant.Outlined" Color="@Color.Info"></MudIconButton>
<MudIconButton Icon="@Icons.Material.Filled.Delete" Size="@Size.Small" Variant="@Variant.Outlined" Color="@Color.Error"></MudIconButton>
</MudStack>
</CellTemplate>
</TemplateColumn>
</Columns>
</MudDataGrid>
</MudPaper>
</MudItem>
</MudGrid>
</MudStack>
@code
{
List<ProductSDto> Products { get; set; } = new List<ProductSDto>();
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<ProductActionDialogBox>("افزودن محصول جدید", maxWidth);
}
}

View File

@ -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<IRestWrapper, RestWrapper>();
builder.Services.AddScoped<IUserUtility, UserUtility>();
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddPWAUpdater();
await builder.Build().RunAsync();

View File

@ -0,0 +1,13 @@
namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
public interface IAuthRestApi
{
[Get("/verifycode")]
public Task<SignUpStatus> GetVerifyCodeAsync([Query] string phoneNumber);
[Post("/login/code")]
public Task<AccessToken<ApplicationUserSDto>> LoginWithVerifyCodeAsync([Body] LoginRequestDto request);
[Post("/signup")]
public Task<AccessToken<ApplicationUserSDto>> CompleteSignUpAsync([Body] SignUpRequestDto request, [Header("Authorization")] string authorization);
}

View File

@ -0,0 +1,47 @@
using Refit;
namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
public interface ICrudApiRest<T, in TKey> where T : class
{
[Post("")]
Task Create([Body] T payload, [Header("Authorization")] string authorization);
[Get("")]
Task<List<T>> ReadAll([Query] int page,[Header("Authorization")] string authorization);
[Get("/{key}")]
Task<T> 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<T, TDto, in TKey> 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<List<TDto>> ReadAll([Query]int page,[Header("Authorization")] string authorization);
[Get("")]
Task<List<TDto>> ReadAll();
[Get("/{key}")]
Task<TDto> 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);
}

View File

@ -0,0 +1,11 @@
namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
public interface IRestWrapper
{
public ICrudApiRest<T, TKey> CrudApiRest<T, TKey>(string address) where T : class;
public ICrudDtoApiRest<T, TDto, TKey> CrudDtoApiRest<T, TDto, TKey>(string address) where T : class where TDto : class;
public IAuthRestApi AuthRestApi { get; }
public IUserRestApi UserRestApi { get; }
}

View File

@ -0,0 +1,9 @@
namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
public interface IUserRestApi
{
[Put("")]
Task UpdateUserAsync([Body]UserActionRequestDto request, [Header("Authorization")] string authorization);
}

View File

@ -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<T, TKey> CrudApiRest<T, TKey>(string address) where T : class
{
return RestService.For<ICrudApiRest<T, TKey>>(address, setting);
}
public ICrudDtoApiRest<T, TDto, TKey> CrudDtoApiRest<T, TDto, TKey>(string address) where T : class where TDto : class
{
return RestService.For<ICrudDtoApiRest<T, TDto, TKey>>(address, setting);
}
public IAuthRestApi AuthRestApi => RestService.For<IAuthRestApi>(Address.AuthController, setting);
public IUserRestApi UserRestApi => RestService.For<IUserRestApi>(Address.UserController, setting);
}

View File

@ -0,0 +1,11 @@
namespace NetinaShop.AdminPanel.PWA.Utilities;
public interface IUserUtility
{
public Task<string> GetBearerTokenAsync();
public Task SetBearerTokenAsync(string token);
public Task<ApplicationUserSDto> GetUserAsync();
public Task SetUserAsync(ApplicationUserSDto user);
public Task LogoutAsync();
}

View File

@ -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<string> GetBearerTokenAsync() => await _localStorageService.GetItemAsStringAsync(LocalStorageKeys.Token);
public async Task SetBearerTokenAsync(string token) => await _localStorageService.SetItemAsStringAsync(LocalStorageKeys.Token, token);
public async Task<ApplicationUserSDto> GetUserAsync() => await _localStorageService.GetItemAsync<ApplicationUserSDto>(LocalStorageKeys.UserInfo);
public async Task SetUserAsync(ApplicationUserSDto user) => await _localStorageService.SetItemAsync<ApplicationUserSDto>(LocalStorageKeys.UserInfo, user);
public async Task LogoutAsync()
{
await _localStorageService.RemoveItemAsync(LocalStorageKeys.Token);
await _localStorageService.RemoveItemAsync(LocalStorageKeys.UserInfo);
}
//public AccessToken<ApplicationUserSDto>? AccessToken { get; set; }
//public List<string> UserClaims => AccessToken == null ? new List<string>() : AccessToken.Permissions;
//public bool HasPermissionTo(string permission) => UserClaims.Any(c => c == permission);
}

View File

@ -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(); }
}
}

View File

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

View File

@ -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",
},
},
}
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -20,6 +20,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
<link href="_content/Toolbelt.Blazor.PWA.Updater/Toolbelt.Blazor.PWA.Updater.bundle.scp.css" rel="stylesheet" />
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
<link rel="stylesheet" href="_content/Radzen.Blazor/css/material-base.css">
<META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE">
</head>
@ -39,6 +41,7 @@
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
<!--<script>navigator.serviceWorker.register('service-worker.js');</script>-->
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
<script src="_content/Toolbelt.Blazor.PWA.Updater.Service/script.min.js"></script>

View File

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