diff --git a/NetinaShop.AdminPanel.PWA/Components/Originals/SideBarUi.razor b/NetinaShop.AdminPanel.PWA/Components/Originals/SideBarUi.razor index f8a35de..3636110 100644 --- a/NetinaShop.AdminPanel.PWA/Components/Originals/SideBarUi.razor +++ b/NetinaShop.AdminPanel.PWA/Components/Originals/SideBarUi.razor @@ -64,10 +64,11 @@ TreeItems.Add(new TreeItemData("داشبورد", Icons.Material.Outlined.Dashboard, "")); TreeItems.Add(new TreeItemData("فروش", Icons.Material.Outlined.ShoppingCart, "products")); TreeItems.Add(new TreeItemData("محصولات", Icons.Material.Outlined.CenterFocusStrong, "products")); - TreeItems.Add(new TreeItemData("دسته بندی ها", Icons.Material.Outlined.AllInbox, "categories")); - TreeItems.Add(new TreeItemData("برند ها", Icons.Custom.Brands.Facebook, "brands")); + TreeItems.Add(new TreeItemData("دسته بندی محصولات", Icons.Material.Outlined.AllInbox, "categories")); + TreeItems.Add(new TreeItemData("برند محصولات", Icons.Custom.Brands.Facebook, "brands")); + TreeItems.Add(new TreeItemData("بلاگ ها", Icons.Material.Outlined.Web, "blogs")); + TreeItems.Add(new TreeItemData("دسته بندی بلاگ ها", Icons.Material.Outlined.Web, "blog/categories")); TreeItems.Add(new TreeItemData("مشترکین", Icons.Material.Outlined.People, "products")); - TreeItems.Add(new TreeItemData("وبلاگ", Icons.Material.Outlined.Web, "products")); TreeItems.Add(new TreeItemData("تنظیمات", Icons.Material.Outlined.Settings, "products")); } } \ No newline at end of file diff --git a/NetinaShop.AdminPanel.PWA/Dialogs/BlogActionDialogBox.razor b/NetinaShop.AdminPanel.PWA/Dialogs/BlogActionDialogBox.razor new file mode 100644 index 0000000..f8ba8a8 --- /dev/null +++ b/NetinaShop.AdminPanel.PWA/Dialogs/BlogActionDialogBox.razor @@ -0,0 +1,196 @@ +@using Radzen.Blazor +@using NetinaShop.AdminPanel.PWA.Extensions + +@inject ISnackbar Snackbar +@inject IRestWrapper RestWrapper +@inject IUserUtility UserUtility +@inject IDialogService DialogService + + + + + + + + + + +
+ + + اطلاعات کلی + اطلاعات کلی محصول را به دقت وارد کنید + + + + + + + + + + + + +
+ +

منتظر بمانید

+
+
+
+
+ +

@e.Name

+
+
+
+ + + + + + + + + + + + + + +
+
+
+ + +
+ + + متن بلاگ + می توانید کتن کامل بلاگــــ خود را کامل وارد کنید + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ + + تصاویر محصول + می توانید برای محصول چند تصویر اپلود کنید + + + + + @foreach (var item in ViewModel.Files) + { +
+ + + + @if (item.IsHeader) + { +

هدر

+ } + @if (item.IsPrimary) + { +

اصلی

+ } +
+ } + +
+
+ +
+
+
+
+ + + + @if (ViewModel.IsEditing) + { + + } + else + { + + } + + بستن + + +
+@code +{ + + [CascadingParameter] + MudDialogInstance MudDialog { get; set; } + + [Parameter] + public BlogSDto? Blog { get; set; } + + public BlogActionDialogBoxViewModel ViewModel { get; set; } + + protected override async Task OnInitializedAsync() + { + if (Blog == null) + ViewModel = new BlogActionDialogBoxViewModel(Snackbar, RestWrapper, UserUtility, DialogService, MudDialog); + else + ViewModel = new BlogActionDialogBoxViewModel(Snackbar, RestWrapper, UserUtility, DialogService, MudDialog, Blog); + await ViewModel.InitializeAsync(); + await base.OnInitializedAsync(); + } + +} \ No newline at end of file diff --git a/NetinaShop.AdminPanel.PWA/Dialogs/BlogActionDialogBox.razor.cs b/NetinaShop.AdminPanel.PWA/Dialogs/BlogActionDialogBox.razor.cs new file mode 100644 index 0000000..9dd1ffe --- /dev/null +++ b/NetinaShop.AdminPanel.PWA/Dialogs/BlogActionDialogBox.razor.cs @@ -0,0 +1,236 @@ +using NetinaShop.Domain.Dtos.LargDtos; +using NetinaShop.Domain.Entities.Blogs; +using NetinaShop.Domain.Entities.Products; + +namespace NetinaShop.AdminPanel.PWA.Dialogs; + +public class BlogActionDialogBoxViewModel : BaseViewModel +{ + private readonly ISnackbar _snackbar; + private readonly IRestWrapper _restWrapper; + private readonly IUserUtility _userUtility; + private readonly IDialogService _dialogService; + private readonly MudDialogInstance _mudDialog; + + public BlogActionDialogBoxViewModel(ISnackbar snackbar, IRestWrapper restWrapper, IUserUtility userUtility, IDialogService dialogService, MudDialogInstance mudDialog) + { + _snackbar = snackbar; + _restWrapper = restWrapper; + _userUtility = userUtility; + _dialogService = dialogService; + _mudDialog = mudDialog; + } + public BlogActionDialogBoxViewModel(ISnackbar snackbar, + IRestWrapper restWrapper, + IUserUtility userUtility, + IDialogService dialogService, + MudDialogInstance mudDialog, + BlogSDto blog) + { + _snackbar = snackbar; + _restWrapper = restWrapper; + _userUtility = userUtility; + _dialogService = dialogService; + _mudDialog = mudDialog; + Blog = blog; + } + + private BlogSDto? _blog = null; + public BlogSDto? Blog + { + get => _blog; + set + { + _blog = value; + if (_blog != null) + { + IsEditing = true; + } + } + } + public bool IsEditing = false; + public string Title { get; set; } = string.Empty; + public string Content { get; set; } = string.Empty; + public int ReadingTime { get; set; } + public string Tags { get; set; } = string.Empty; + public string Summery { get; set; } = string.Empty; + public bool IsSuggested { get; set; } + public readonly ObservableCollection Files = new ObservableCollection(); + + + + public override async Task InitializeAsync() + { + if (IsEditing && _blog != null) + { + try + { + IsProcessing = true; + var blogLDto = await _restWrapper.CrudDtoApiRest(Address.BlogController).ReadOne(_blog.Id); + Title = blogLDto.Title; + Content = blogLDto.Content; + ReadingTime = blogLDto.ReadingTime; + Tags = blogLDto.Tags; + Summery = blogLDto.Summery; + Tags = blogLDto.Tags; + IsSuggested = blogLDto.IsSuggested; + blogLDto.Files.ForEach(f => Files.Add(f)); + SelectedCategory = new BlogCategorySDto() { Id = blogLDto.CategoryId, Name = blogLDto.CategoryName }; + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + _snackbar.Add(exe.Message, Severity.Error); + _snackbar.Add(ex.Content, Severity.Error); + _mudDialog.Cancel(); + } + catch (Exception e) + { + _snackbar.Add(e.Message, Severity.Error); + _mudDialog.Cancel(); + } + finally + { + + IsProcessing = false; + } + }; + } + + + + public void Cancel() => _mudDialog.Cancel(); + + public async Task SubmitEditAsync() + { + try + { + IsProcessing = true; + if (Blog == null || Blog.Id == default) + throw new Exception("بلاگ اشتباه است"); + if (SelectedCategory == null) + throw new Exception("لطفا یک دسته بندی انتخاب کنید"); + var token = await _userUtility.GetBearerTokenAsync(); + + var request = new BlogLDto + { + Id = Blog.Id, + Title = Title, + CategoryId = SelectedCategory.Id, + Content = Content, + Files = this.Files.ToList(), + IsSuggested = IsSuggested, + ReadingTime = ReadingTime, + Summery = Summery, + Tags = Tags + }; + await _restWrapper.CrudApiRest(Address.BlogController).Create(request, token); + _snackbar.Add($"ویرایش بلاگ {Title} با موفقیت انجام شد", Severity.Success); + _mudDialog.Close(); + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + _snackbar.Add(exe.Message, Severity.Error); + _snackbar.Add(ex.Content, Severity.Error); + _mudDialog.Cancel(); + } + catch (Exception e) + { + _snackbar.Add(e.Message, Severity.Error); + _mudDialog.Cancel(); + } + finally + { + + IsProcessing = false; + } + } + public async Task SubmitCreateAsync() + { + try + { + IsProcessing = true; + if (SelectedCategory == null) + throw new Exception("لطفا یک دسته بندی انتخاب کنید"); + var token = await _userUtility.GetBearerTokenAsync(); + var request = new BlogLDto + { + Title = Title, + CategoryId = SelectedCategory.Id, + Content = Content, + Files = this.Files.ToList(), + IsSuggested = IsSuggested, + ReadingTime = ReadingTime, + Summery = Summery, + Tags = Tags + }; + await _restWrapper.CrudApiRest(Address.BlogController).Create(request, token); + + _snackbar.Add($"ساخت بلاگ {Title} با موفقیت انجام شد", Severity.Success); + _mudDialog.Close(); + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + _snackbar.Add(exe.Message, Severity.Error); + _snackbar.Add(ex.Content, Severity.Error); + _mudDialog.Cancel(); + } + catch (Exception e) + { + _snackbar.Add(e.Message, Severity.Error); + _mudDialog.Cancel(); + } + finally + { + + IsProcessing = false; + } + } + + + private List _blogCategories = new List(); + public BlogCategorySDto? SelectedCategory; + public async Task> SearchBlogCategory(string category) + { + try + { + if (category.IsNullOrEmpty()) + _blogCategories = await _restWrapper.BlogCategoryRestApi.ReadAll(0); + else + _blogCategories = await _restWrapper.BlogCategoryRestApi.ReadAll(category); + return _blogCategories; + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + _snackbar.Add(exe.Message, Severity.Error); + _snackbar.Add(ex.Content, Severity.Error); + return _blogCategories; + } + catch (Exception e) + { + _snackbar.Add(e.Message, Severity.Error); + return _blogCategories; + } + } + public async Task SelectFileAsync() + { + DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, DisableBackdropClick = true }; + var dialog = await _dialogService.ShowAsync("انتخاب عکس", maxWidth); + var result = await dialog.Result; + var file = result.Data; + if (file is StorageFileSDto storageFile) + Files.Add(storageFile); + } + + public void RemoveFile(StorageFileSDto file) + { + Files.Remove(file); + } +} \ No newline at end of file diff --git a/NetinaShop.AdminPanel.PWA/Dialogs/BlogCategoryActionDialogBox.razor b/NetinaShop.AdminPanel.PWA/Dialogs/BlogCategoryActionDialogBox.razor new file mode 100644 index 0000000..b9701d9 --- /dev/null +++ b/NetinaShop.AdminPanel.PWA/Dialogs/BlogCategoryActionDialogBox.razor @@ -0,0 +1,136 @@ +@using NetinaShop.Common.Models.Exception +@using NetinaShop.Domain.Entities.Blogs + +@inject ISnackbar Snackbar +@inject IRestWrapper RestWrapper +@inject IUserUtility UserUtility + + + + + + + + اطلاعات کلی + اطلاعات کلی دسته بندی را به دقت وارد کنید + + + + + + + + + + + + + + + + @if (_isEditing) + { + + } + else + { + + } + + بستن + + + +@code { + [CascadingParameter] MudDialogInstance MudDialog { get; set; } + [Parameter] + public BlogCategorySDto? BlogCategory { get; set; } + + void Cancel() => MudDialog.Cancel(); + private bool _isProcessing = false; + private string _name = string.Empty; + private string _description = string.Empty; + private bool _isEditing; + + protected override Task OnParametersSetAsync() + { + if (BlogCategory != null) + { + _isEditing = true; + _description = BlogCategory.Description; + _name = BlogCategory.Name; + } + return base.OnParametersSetAsync(); + } + + private async Task SubmitCreateAsync() + { + try + { + if (_name.IsNullOrEmpty()) + throw new AppException("لطفا نام دسته بندی را وارد کنید"); + _isProcessing = true; + var token = await UserUtility.GetBearerTokenAsync(); + var request = new BlogCategorySDto{Name = _name,Description = _description}; + await RestWrapper.CrudApiRest(Address.BlogCategoryController).Create(request, token); + MudDialog.Close(DialogResult.Ok(true)); + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + Snackbar.Add(exe.Message, Severity.Error); + Snackbar.Add(ex.Content, Severity.Error); + } + catch (Exception e) + { + Snackbar.Add(e.Message, Severity.Error); + } + finally + { + _isProcessing = false; + } + } + + + private async Task SubmitEditAsync() + { + try + { + if (BlogCategory == null) + throw new AppException("دسته بندی به درستی ارسال نشده است"); + if (_name.IsNullOrEmpty()) + throw new AppException("لطفا نام دسته بندی را وارد کنید"); + _isProcessing = true; + await Task.Delay(1000); + var token = await UserUtility.GetBearerTokenAsync(); + var request = new BlogCategorySDto {Id = BlogCategory.Id , Name = _name, Description = _description }; + await RestWrapper.CrudApiRest(Address.BlogCategoryController).Update(request, token); + MudDialog.Close(); + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + Snackbar.Add(exe.Message, Severity.Error); + Snackbar.Add(ex.Content, Severity.Error); + MudDialog.Cancel(); + } + catch (Exception e) + { + Snackbar.Add(e.Message, Severity.Error); + MudDialog.Cancel(); + } + finally + { + + _isProcessing = false; + } + } +} \ No newline at end of file diff --git a/NetinaShop.AdminPanel.PWA/Dialogs/BrandActionDialogBox.razor b/NetinaShop.AdminPanel.PWA/Dialogs/BrandActionDialogBox.razor index 5061206..ccc492a 100644 --- a/NetinaShop.AdminPanel.PWA/Dialogs/BrandActionDialogBox.razor +++ b/NetinaShop.AdminPanel.PWA/Dialogs/BrandActionDialogBox.razor @@ -16,15 +16,18 @@ اطلاعات کلی دسته بندی محصول را به دقت وارد کنید - + - - + + + + + @@ -33,14 +36,22 @@ - @* - - بستن - - *@ - + @if (_isEditing) + { + + } + else + { + + } بستن @@ -48,12 +59,28 @@ @code { [CascadingParameter] MudDialogInstance MudDialog { get; set; } + [Parameter] + public BrandSDto? Brand { get; set; } void Cancel() => MudDialog.Cancel(); private bool _isProcessing = false; private string _name = string.Empty; private string _description = string.Empty; - private bool _isMain; + private bool _hasSpecialPage; + private bool _isEditing; + private string _pageUrl = string.Empty; + + protected override Task OnParametersSetAsync() + { + if (Brand != null) + { + _isEditing = true; + _hasSpecialPage = Brand.HasSpecialPage; + _description = Brand.Description; + _name = Brand.Name; + } + return base.OnParametersSetAsync(); + } private async Task SubmitCreateAsync() { @@ -63,7 +90,7 @@ throw new AppException("لطفا نام برند را وارد کنید"); _isProcessing = true; var token = await UserUtility.GetBearerTokenAsync(); - var request = new CreateBrandCommand(_name, _description, _isMain, string.Empty, new List()); + var request = new CreateBrandCommand(_name, _description, _hasSpecialPage, string.Empty, new List()); await RestWrapper.CrudApiRest(Address.BrandController).Create(request, token); MudDialog.Close(DialogResult.Ok(true)); } @@ -83,4 +110,40 @@ _isProcessing = false; } } + + + private async Task SubmitEditAsync() + { + try + { + if (Brand == null) + throw new AppException("برند به درستی ارسال نشده است"); + if (_name.IsNullOrEmpty()) + throw new AppException("لطفا نام برند را وارد کنید"); + _isProcessing = true; + await Task.Delay(1000); + var token = await UserUtility.GetBearerTokenAsync(); + var request = new UpdateBrandCommand(Brand.Id, _name, _description, _hasSpecialPage, string.Empty , new List()); + await RestWrapper.CrudApiRest(Address.BrandController).Update(request, token); + MudDialog.Close(); + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + Snackbar.Add(exe.Message, Severity.Error); + Snackbar.Add(ex.Content, Severity.Error); + MudDialog.Cancel(); + } + catch (Exception e) + { + Snackbar.Add(e.Message, Severity.Error); + MudDialog.Cancel(); + } + finally + { + + _isProcessing = false; + } + } } \ No newline at end of file diff --git a/NetinaShop.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor b/NetinaShop.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor index 236cfd6..10144a3 100644 --- a/NetinaShop.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor +++ b/NetinaShop.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor @@ -12,223 +12,239 @@ - + - - + +
+ - اطلاعات کلی - اطلاعات کلی محصول را به دقت وارد کنید - - + اطلاعات کلی + اطلاعات کلی محصول را به دقت وارد کنید + + - - - - - - - + + + + + + + - - - - -
- -

منتظر بمانید

-
-
-
-
- -

@e.Name

-
-
-
- + + + + +
+ +

منتظر بمانید

+
+
+
+
+ +

@e.Name

+
+
+
+ - - - - -
- -

منتظر بمانید

-
-
-
-
- -

@e.Name

-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ + + + +
+ +

منتظر بمانید

+
+
+
+
+ +

@e.Name

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
- + - ویژگی های کلی - می توانید ویگی های تکمیلی محصول را کامل وارد کنید - - + ویژگی های کلی + می توانید ویگی های تکمیلی محصول را کامل وارد کنید + + - - - + + + - - - + + + - - افزودن - + + افزودن + - - - - - - - - - حذف - - - - - - - + + + + + + + + + حذف + + + + + + + +
- +
+ - توضیحات تکمیلی - می توانید توضیحاتــ تکمیلی محصول را کامل وارد کنید - - - - - - - + توضیحات تکمیلی + می توانید توضیحاتــ تکمیلی محصول را کامل وارد کنید + + + + + + + - - + + - - - - - + + + + + - - - - - - - + + + + + + + - + - - + + +
- +
+ - تصاویر محصول - می توانید برای محصول چند تصویر اپلود کنید - - - - - @foreach (var item in ViewModel.Files) - { -
- + تصاویر محصول + می توانید برای محصول چند تصویر اپلود کنید + + + + + @foreach (var item in ViewModel.Files) + { +
+ - -
- } + + @if (item.IsHeader) + { +

هدر

+ } + @if (item.IsPrimary) + { +

اصلی

+ } +
+ } -
+ +
-
+
- + @if (ViewModel.IsEditing) { @@ -257,6 +273,7 @@ public ProductSDto? Product { get; set; } public ProductActionDialogBoxViewModel ViewModel { get; set; } + protected override async Task OnInitializedAsync() { if (Product == null) diff --git a/NetinaShop.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor.cs b/NetinaShop.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor.cs index 52ea191..d1955d1 100644 --- a/NetinaShop.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor.cs +++ b/NetinaShop.AdminPanel.PWA/Dialogs/ProductActionDialogBox.razor.cs @@ -66,6 +66,49 @@ public class ProductActionDialogBoxViewModel : BaseViewModel public readonly ObservableCollection Files = new ObservableCollection(); + public override async Task InitializeAsync() + { + if (IsEditing && _product != null) + { + try + { + IsProcessing = true; + var productLDto = await _restWrapper.CrudDtoApiRest(Address.ProductController).ReadOne(_product.Id); + ExpertCheck = productLDto.ExpertCheck; + Summery = productLDto.Summery; + BeDisplayed = productLDto.BeDisplayed; + HasExpressDelivery = productLDto.HasExpressDelivery; + PersianName = productLDto.PersianName; + EnglishName = productLDto.EnglishName; + Cost = productLDto.Cost; + PackingCost = productLDto.PackingCost; + MaxOrder = productLDto.MaxOrderCount; + Warranty = productLDto.Warranty; + productLDto.Specifications.ForEach(s => Specifications.Add(s)); + productLDto.Files.ForEach(f => Files.Add(f)); + SelectedCategory = new ProductCategorySDto { Id = productLDto.CategoryId, Name = productLDto.CategoryName }; + SelectedBrand = new BrandSDto { Id = productLDto.BrandId, Name = productLDto.BrandName }; + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + _snackbar.Add(exe.Message, Severity.Error); + _snackbar.Add(ex.Content, Severity.Error); + _mudDialog.Cancel(); + } + catch (Exception e) + { + _snackbar.Add(e.Message, Severity.Error); + _mudDialog.Cancel(); + } + finally + { + + IsProcessing = false; + } + }; + } public async Task SubmitEditAsync() { @@ -74,8 +117,12 @@ public class ProductActionDialogBoxViewModel : BaseViewModel IsProcessing = true; if (Product == null || Product.Id == default) throw new Exception("محصول اشتباه است"); + if (SelectedCategory == null) + throw new Exception("لطفا یک دسته بندی انتخاب کنید"); + if (SelectedBrand == null) + throw new Exception("لطفا یک برند انتخاب کنید"); var token = await _userUtility.GetBearerTokenAsync(); - var request = new UpdateProductCommand(Product.Id, PersianName, EnglishName, Summery, ExpertCheck, Tags, Warranty, BeDisplayed, Cost, PackingCost, HasExpressDelivery, MaxOrder, _selectedBrand?.Id ?? default, _selectedCategory?.Id ?? default, Specifications.ToList(), Files.ToList()); + var request = new UpdateProductCommand(Product.Id, PersianName, EnglishName, Summery, ExpertCheck, Tags, Warranty, BeDisplayed, Cost, PackingCost, HasExpressDelivery, MaxOrder, SelectedBrand?.Id ?? default, SelectedCategory?.Id ?? default, Specifications.ToList(), Files.ToList()); await _restWrapper.CrudApiRest(Address.ProductController).Update(request, token); _snackbar.Add($"ویرایش محصول {PersianName} با موفقیت انجام شد", Severity.Success); _mudDialog.Close(); @@ -99,6 +146,97 @@ public class ProductActionDialogBoxViewModel : BaseViewModel IsProcessing = false; } } + public async Task SubmitCreateAsync() + { + try + { + IsProcessing = true; + if (SelectedCategory == null) + throw new Exception("لطفا یک دسته بندی انتخاب کنید"); + if (SelectedBrand == null) + throw new Exception("لطفا یک برند انتخاب کنید"); + var token = await _userUtility.GetBearerTokenAsync(); + var request = new CreateProductCommand(PersianName, EnglishName, Summery, ExpertCheck, Tags, Warranty, BeDisplayed, Cost, PackingCost, HasExpressDelivery, MaxOrder, SelectedBrand?.Id ?? default, SelectedCategory?.Id ?? default, Specifications.ToList(), Files.ToList()); + await _restWrapper.CrudApiRest(Address.ProductController).Create(request, token); + + _snackbar.Add($"ساخت محصول {PersianName} با موفقیت انجام شد", Severity.Success); + _mudDialog.Close(); + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + _snackbar.Add(exe.Message, Severity.Error); + _snackbar.Add(ex.Content, Severity.Error); + _mudDialog.Cancel(); + } + catch (Exception e) + { + _snackbar.Add(e.Message, Severity.Error); + _mudDialog.Cancel(); + } + finally + { + + IsProcessing = false; + } + } + + + private List _productCategories = new List(); + public ProductCategorySDto? SelectedCategory; + public async Task> SearchProductCategory(string category) + { + try + { + if (category.IsNullOrEmpty()) + _productCategories = await _restWrapper.ProductCategoryRestApi.ReadAll(0); + else + _productCategories = await _restWrapper.ProductCategoryRestApi.ReadAll(category); + return _productCategories; + } + 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; + } + } + + private List _brands = new List(); + public BrandSDto? SelectedBrand; + public async Task> SearchBrand(string brand) + { + try + { + if (brand.IsNullOrEmpty()) + _brands = await _restWrapper.BrandRestApi.ReadAll(0); + else + _brands = await _restWrapper.BrandRestApi.ReadAll(brand); + return _brands; + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + _snackbar.Add(exe.Message, Severity.Error); + _snackbar.Add(ex.Content, Severity.Error); + return _brands; + } + catch (Exception e) + { + _snackbar.Add(e.Message, Severity.Error); + return _brands; + } + } + public void AddSpecification() { @@ -125,135 +263,6 @@ public class ProductActionDialogBoxViewModel : BaseViewModel if (spec != null) Specifications.Remove(spec); } - public async Task SubmitCreateAsync() - { - try - { - IsProcessing = true; - var token = await _userUtility.GetBearerTokenAsync(); - var request = new CreateProductCommand(PersianName, EnglishName, Summery, ExpertCheck, Tags, Warranty, BeDisplayed, Cost, PackingCost, HasExpressDelivery, MaxOrder, _selectedBrand?.Id ?? default, _selectedCategory?.Id ?? default, Specifications.ToList(), Files.ToList()); - await _restWrapper.CrudApiRest(Address.ProductController).Create(request, token); - - _snackbar.Add($"ساخت محصول {PersianName} با موفقیت انجام شد", Severity.Success); - _mudDialog.Close(); - } - catch (ApiException ex) - { - var exe = await ex.GetContentAsAsync(); - if (exe != null) - _snackbar.Add(exe.Message, Severity.Error); - _snackbar.Add(ex.Content, Severity.Error); - _mudDialog.Cancel(); - } - catch (Exception e) - { - _snackbar.Add(e.Message, Severity.Error); - _mudDialog.Cancel(); - } - finally - { - - IsProcessing = false; - } - } - public override async Task InitializeAsync() - { - if (IsEditing && _product != null) - { - try - { - IsProcessing = true; - var productLDto = await _restWrapper.CrudDtoApiRest(Address.ProductController).ReadOne(_product.Id); - ExpertCheck = productLDto.ExpertCheck; - Summery = productLDto.Summery; - BeDisplayed = productLDto.BeDisplayed; - HasExpressDelivery = productLDto.HasExpressDelivery; - PersianName = productLDto.PersianName; - EnglishName = productLDto.EnglishName; - Cost = productLDto.Cost; - PackingCost = productLDto.PackingCost; - MaxOrder = productLDto.MaxOrderCount; - Warranty = productLDto.Warranty; - productLDto.Specifications.ForEach(s => Specifications.Add(s)); - productLDto.Files.ForEach(f => Files.Add(f)); - _selectedCategory = new ProductCategorySDto { Id = productLDto.CategoryId, Name = productLDto.CategoryName }; - _selectedBrand = new BrandSDto { Id = productLDto.BrandId, Name = productLDto.BrandName }; - } - catch (ApiException ex) - { - var exe = await ex.GetContentAsAsync(); - if (exe != null) - _snackbar.Add(exe.Message, Severity.Error); - _snackbar.Add(ex.Content, Severity.Error); - _mudDialog.Cancel(); - } - catch (Exception e) - { - _snackbar.Add(e.Message, Severity.Error); - _mudDialog.Cancel(); - } - finally - { - - IsProcessing = false; - } - }; - } - - - public List _productCategories = new List(); - public ProductCategorySDto? _selectedCategory; - public async Task> SearchProductCategory(string category) - { - try - { - if (category.IsNullOrEmpty()) - _productCategories = await _restWrapper.ProductCategoryRestApi.ReadAll(0); - else - _productCategories = await _restWrapper.ProductCategoryRestApi.ReadAll(category); - return _productCategories; - } - 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; - } - } - - public List _brands = new List(); - public BrandSDto? _selectedBrand; - public async Task> SearchBrand(string brand) - { - try - { - if (brand.IsNullOrEmpty()) - _brands = await _restWrapper.BrandRestApi.ReadAll(0); - else - _brands = await _restWrapper.BrandRestApi.ReadAll(brand); - return _brands; - } - catch (ApiException ex) - { - var exe = await ex.GetContentAsAsync(); - if (exe != null) - _snackbar.Add(exe.Message, Severity.Error); - _snackbar.Add(ex.Content, Severity.Error); - return _brands; - } - catch (Exception e) - { - _snackbar.Add(e.Message, Severity.Error); - return _brands; - } - } public async Task SelectFileAsync() { diff --git a/NetinaShop.AdminPanel.PWA/Dialogs/ProductCategoryActionDialogBox.razor b/NetinaShop.AdminPanel.PWA/Dialogs/ProductCategoryActionDialogBox.razor index 0325e03..5715226 100644 --- a/NetinaShop.AdminPanel.PWA/Dialogs/ProductCategoryActionDialogBox.razor +++ b/NetinaShop.AdminPanel.PWA/Dialogs/ProductCategoryActionDialogBox.razor @@ -19,17 +19,17 @@ - + - + - @@ -54,7 +54,7 @@ توضیحات تکمیلی می توانید توضیحاتــ تکمیلی محصول را کامل وارد کنید - + @@ -86,148 +86,42 @@ - @if (_isEditing) + @if (ViewModel.IsEditing) { - + Content="ثبت ویرایش" OnClickCallback="ViewModel.SubmitEditAsync" /> } else { - + Content="تایید" OnClickCallback="ViewModel.SubmitCreateAsync" /> } - بستن + بستن
@code { + [CascadingParameter] MudDialogInstance MudDialog { get; set; } - void Cancel() => MudDialog.Cancel(); + public ProductCategoryActionDialogBoxViewModel ViewModel { get; set; } - private string _name = string.Empty; - private string _description = string.Empty; - private bool _isMain; - private bool _isEditing = false; - private bool _isProcessing = false; - - private ProductCategorySDto? _category = null; [Parameter] - public ProductCategorySDto? Category - { - get => _category; - set - { - _category = value; - if (_category != null) - { - _name = _category.Name; - _description = _category.Description; - _isEditing = true; - } - } - } - - private async Task SubmitCreateAsync() - { - try - { - if (_name.IsNullOrEmpty()) - throw new AppException("لطفا نام دسته را وارد کنید"); - _isProcessing = true; - var token = await UserUtility.GetBearerTokenAsync(); - var request = new CreateProductCategoryCommand(_name, _description,_isMain, _selectedCategory?.Id ?? default, new List()); - await RestWrapper.CrudApiRest(Address.ProductCategoryController).Create(request, token); - MudDialog.Close(); - } - catch (ApiException ex) - { - var exe = await ex.GetContentAsAsync(); - if (exe != null) - Snackbar.Add(exe.Message, Severity.Error); - Snackbar.Add(ex.Content, Severity.Error); - MudDialog.Cancel(); - } - catch (Exception e) - { - Snackbar.Add(e.Message, Severity.Error); - MudDialog.Cancel(); - } - finally - { + public ProductCategorySDto? Category { get; set; } - _isProcessing = false; - } - } - - private async Task SubmitEditAsync() + protected override Task OnInitializedAsync() { - try - { - if (Category == null) - throw new AppException("دسته به درستی ارسال نشده است"); - if (_name.IsNullOrEmpty()) - throw new AppException("لطفا نام دسته را وارد کنید"); - _isProcessing = true; - await Task.Delay(1000); - var token = await UserUtility.GetBearerTokenAsync(); - var request = new UpdateProductCategoryCommand(Category.Id, _name, _description,_isMain, _selectedCategory?.Id ?? default, new List()); - await RestWrapper.CrudApiRest(Address.ProductCategoryController).Update(request, token); - MudDialog.Close(); - } - catch (ApiException ex) - { - var exe = await ex.GetContentAsAsync(); - if (exe != null) - Snackbar.Add(exe.Message, Severity.Error); - Snackbar.Add(ex.Content, Severity.Error); - MudDialog.Cancel(); - } - catch (Exception e) - { - Snackbar.Add(e.Message, Severity.Error); - MudDialog.Cancel(); - } - finally - { - - _isProcessing = false; - } - } - - private List _productCategories = new List(); - private ProductCategorySDto? _selectedCategory; - private async Task> SearchCity(string city) - { - try - { - if (_productCategories.Count == 0) - { - _productCategories = await RestWrapper.ProductCategoryRestApi.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; - } + if (Category == null) + ViewModel = new ProductCategoryActionDialogBoxViewModel(Snackbar, RestWrapper, UserUtility, MudDialog); + else + ViewModel = new ProductCategoryActionDialogBoxViewModel(Snackbar, RestWrapper, UserUtility, MudDialog,Category); + return base.OnInitializedAsync(); } } \ No newline at end of file diff --git a/NetinaShop.AdminPanel.PWA/Dialogs/ProductCategoryActionDialogBox.razor.cs b/NetinaShop.AdminPanel.PWA/Dialogs/ProductCategoryActionDialogBox.razor.cs new file mode 100644 index 0000000..6d4cfe5 --- /dev/null +++ b/NetinaShop.AdminPanel.PWA/Dialogs/ProductCategoryActionDialogBox.razor.cs @@ -0,0 +1,145 @@ +namespace NetinaShop.AdminPanel.PWA.Dialogs; + +public class ProductCategoryActionDialogBoxViewModel:BaseViewModel +{ + private readonly ISnackbar _snackbar; + private readonly IRestWrapper _restWrapper; + private readonly IUserUtility _userUtility; + private readonly MudDialogInstance _mudDialog; + + public ProductCategoryActionDialogBoxViewModel(ISnackbar snackbar, IRestWrapper restWrapper, IUserUtility userUtility, MudDialogInstance mudDialog) + { + _snackbar = snackbar; + _restWrapper = restWrapper; + _userUtility = userUtility; + _mudDialog = mudDialog; + } + + public ProductCategoryActionDialogBoxViewModel(ISnackbar snackbar, IRestWrapper restWrapper, IUserUtility userUtility, MudDialogInstance mudDialog,ProductCategorySDto category) + { + _snackbar = snackbar; + _restWrapper = restWrapper; + _userUtility = userUtility; + _mudDialog = mudDialog; + Category = category; + } + public void Cancel() => _mudDialog.Cancel(); + + public string Name = string.Empty; + public string Description = string.Empty; + public bool IsMain; + public bool IsEditing = false; + + private ProductCategorySDto? _category = null; + [Parameter] + public ProductCategorySDto? Category + { + get => _category; + set + { + _category = value; + if (_category != null) + { + Name = _category.Name; + Description = _category.Description; + IsEditing = true; + } + } + } + + public async Task SubmitCreateAsync() + { + try + { + if (Name.IsNullOrEmpty()) + throw new AppException("لطفا نام دسته را وارد کنید"); + IsProcessing = true; + var token = await _userUtility.GetBearerTokenAsync(); + var request = new CreateProductCategoryCommand(Name, Description, IsMain, SelectedCategory?.Id ?? default, new List()); + await _restWrapper.CrudApiRest(Address.ProductCategoryController).Create(request, token); + _mudDialog.Close(); + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + _snackbar.Add(exe.Message, Severity.Error); + _snackbar.Add(ex.Content, Severity.Error); + _mudDialog.Cancel(); + } + catch (Exception e) + { + _snackbar.Add(e.Message, Severity.Error); + _mudDialog.Cancel(); + } + finally + { + + IsProcessing = false; + } + } + + public async Task SubmitEditAsync() + { + try + { + if (Category == null) + throw new AppException("دسته به درستی ارسال نشده است"); + if (Name.IsNullOrEmpty()) + throw new AppException("لطفا نام دسته را وارد کنید"); + IsProcessing = true; + await Task.Delay(1000); + var token = await _userUtility.GetBearerTokenAsync(); + var request = new UpdateProductCategoryCommand(Category.Id, Name, Description, IsMain, SelectedCategory?.Id ?? default, new List()); + await _restWrapper.CrudApiRest(Address.ProductCategoryController).Update(request, token); + _mudDialog.Close(); + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + _snackbar.Add(exe.Message, Severity.Error); + _snackbar.Add(ex.Content, Severity.Error); + _mudDialog.Cancel(); + } + catch (Exception e) + { + _snackbar.Add(e.Message, Severity.Error); + _mudDialog.Cancel(); + } + finally + { + + IsProcessing = false; + } + } + + private List _productCategories = new List(); + public ProductCategorySDto? SelectedCategory; + public async Task> SearchCity(string city) + { + try + { + if (_productCategories.Count == 0) + { + _productCategories = await _restWrapper.ProductCategoryRestApi.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/Dialogs/StorageDialogBox.razor b/NetinaShop.AdminPanel.PWA/Dialogs/StorageDialogBox.razor index 9ea5362..ae6b5eb 100644 --- a/NetinaShop.AdminPanel.PWA/Dialogs/StorageDialogBox.razor +++ b/NetinaShop.AdminPanel.PWA/Dialogs/StorageDialogBox.razor @@ -12,7 +12,7 @@ میتوانید از بین عکس های اپلود شده یکی را انتخاب کرده یا عکس جدیدی اپلود کنید - + + OnAdornmentClick="@ViewModel.SearchAsync">
- @foreach (var item in _files) + @foreach (var item in ViewModel.Files) { @if (item.Selected) { - + } else { - + } }
@@ -51,142 +51,33 @@ - + - + + - بستن + بستن @code { - - private void SelectFile(StorageFileSDto item) - { - var pastSelect = _files.FirstOrDefault(f => f.Selected); - if (pastSelect != null) - pastSelect.Selected = false; - item.Selected = true; - } - - private void UnSelectFile(StorageFileSDto item) => item.Selected = false; - - public void SearchChanged(string search) - { - if (search.IsNullOrEmpty() && !_search.IsNullOrEmpty()) - { - _files.Clear(); - _originalFiles.ForEach(f=>_files.Add(f)); - } - _search = search; - } - public void SearchAsync() - { - try - { - if (_search.IsNullOrEmpty()) - throw new AppException("دسته بندی برای جست جو وارد نشده است"); - _files.Clear(); - foreach (var storageFileSDto in _originalFiles.Where(f => f.FileName.ToLower().Trim().Contains(_search.ToLower().Trim()))) - _files.Add(storageFileSDto); - } - catch (Exception e) - { - Snackbar.Add(e.Message, Severity.Error); - } - } - - public void SelectFile() - { - var selected = _files.FirstOrDefault(f => f.Selected); - if (selected == null) - throw new Exception("یک فایل را انتخاب کنید"); - MudDialog.Close(selected); - } [CascadingParameter] MudDialogInstance MudDialog { get; set; } - void Cancel() => MudDialog.Cancel(); - private readonly ObservableCollection _files = new ObservableCollection(); - private List _originalFiles = new List(); - private bool _isProcessing = false; - private string _search = string.Empty; + + public StorageDialogBoxViewModel ViewModel { get; set; } + protected override async Task OnInitializedAsync() { - try - { - _isProcessing = true; - _files.Clear(); - var token = await UserUtility.GetBearerTokenAsync(); - var files = await RestWrapper.FileRestApi.GetFilesAsync(token); - files.ForEach(f => _files.Add(f)); - _originalFiles = files; - } - catch (ApiException ex) - { - var exe = await ex.GetContentAsAsync(); - if (exe != null) - Snackbar.Add(exe.Message, Severity.Error); - Snackbar.Add(ex.Content, Severity.Error); - } - catch (Exception e) - { - Snackbar.Add(e.Message, Severity.Error); - } - finally - { - _isProcessing = false; - } - + ViewModel = new StorageDialogBoxViewModel(RestWrapper,UserUtility,Snackbar,MudDialog); + await ViewModel.InitializeAsync(); await base.OnInitializedAsync(); } - - private async Task FileChangeForUpload(InputFileChangeEventArgs obj) - { - try - { - _isProcessing = true; - using var memoryStream = new MemoryStream(); - var file = obj.File; - var stream = file.OpenReadStream(); - await stream.CopyToAsync(memoryStream); - - var fileUpload = new FileUploadRequest - { - ContentType = file.ContentType, - FileName = file.Name, - FileUploadType = FileUploadType.Image, - StringBaseFile = Convert.ToBase64String(memoryStream.ToArray()) - }; - var token = await UserUtility.GetBearerTokenAsync(); - var rest = await RestWrapper.FileRestApi.UploadFileAsync(fileUpload, token); - _files.Insert(0, new StorageFileSDto - { - FileLocation = rest.FileLocation, - FileName = rest.FileName, - FileType = StorageFileType.Image - }); - } - catch (ApiException ex) - { - var exe = await ex.GetContentAsAsync(); - if (exe != null) - Snackbar.Add(exe.Message, Severity.Error); - Snackbar.Add(ex.Content, Severity.Error); - } - catch (Exception e) - { - Snackbar.Add(e.Message, Severity.Error); - } - finally - { - _isProcessing = false; - } - } } diff --git a/NetinaShop.AdminPanel.PWA/Dialogs/StorageDialogBox.razor.cs b/NetinaShop.AdminPanel.PWA/Dialogs/StorageDialogBox.razor.cs new file mode 100644 index 0000000..a839807 --- /dev/null +++ b/NetinaShop.AdminPanel.PWA/Dialogs/StorageDialogBox.razor.cs @@ -0,0 +1,152 @@ +using Microsoft.AspNetCore.Components.Forms; + +namespace NetinaShop.AdminPanel.PWA.Dialogs; + +public class StorageDialogBoxViewModel:BaseViewModel +{ + + private readonly IRestWrapper _restWrapper; + private readonly IUserUtility _userUtility; + private readonly ISnackbar _snackbar; + + private readonly MudDialogInstance _mudDialog; + + public readonly ObservableCollection Files = new ObservableCollection(); + public List OriginalFiles = new List(); + public string Search = string.Empty; + public bool IsPrimary { get; set; } + public bool IsHeader { get; set; } + + public StorageDialogBoxViewModel(IRestWrapper restWrapper, IUserUtility userUtility, ISnackbar snackbar, MudDialogInstance mudDialog) + { + _restWrapper = restWrapper; + _userUtility = userUtility; + _snackbar = snackbar; + _mudDialog = mudDialog; + } + + + public void Cancel() => _mudDialog.Cancel(); + public void SelectFile(StorageFileSDto item) + { + var pastSelect = Files.FirstOrDefault(f => f.Selected); + if (pastSelect != null) + pastSelect.Selected = false; + item.Selected = true; + } + + public void UnSelectFile(StorageFileSDto item) => item.Selected = false; + + public void SearchChanged(string search) + { + if (search.IsNullOrEmpty() && !Search.IsNullOrEmpty()) + { + Files.Clear(); + OriginalFiles.ForEach(f => Files.Add(f)); + } + Search = search; + } + public void SearchAsync() + { + try + { + if (Search.IsNullOrEmpty()) + throw new AppException("نام فایل برای جست جو وارد نشده است"); + Files.Clear(); + foreach (var storageFileSDto in OriginalFiles.Where(f => f.FileName.ToLower().Trim().Contains(Search.ToLower().Trim()))) + Files.Add(storageFileSDto); + } + catch (Exception e) + { + _snackbar.Add(e.Message, Severity.Error); + } + } + + public void SelectFile() + { + try + { + var selected = Files.FirstOrDefault(f => f.Selected); + if (selected == null) + throw new Exception("یک فایل را انتخاب کنید"); + selected.IsHeader = IsHeader; + selected.IsPrimary = IsPrimary; + _mudDialog.Close(selected); + } + catch (Exception e) + { + _snackbar.Add(e.Message, Severity.Error); + } + } + + public override async Task InitializeAsync() + { + try + { + IsProcessing = true; + Files.Clear(); + var token = await _userUtility.GetBearerTokenAsync(); + var files = await _restWrapper.FileRestApi.GetFilesAsync(token); + files.ForEach(f => Files.Add(f)); + OriginalFiles = files; + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + _snackbar.Add(exe.Message, Severity.Error); + _snackbar.Add(ex.Content, Severity.Error); + } + catch (Exception e) + { + _snackbar.Add(e.Message, Severity.Error); + } + finally + { + IsProcessing = false; + } + } + + public async Task FileChangeForUpload(InputFileChangeEventArgs obj) + { + try + { + IsProcessing = true; + using var memoryStream = new MemoryStream(); + var file = obj.File; + var stream = file.OpenReadStream(); + await stream.CopyToAsync(memoryStream); + + var fileUpload = new FileUploadRequest + { + ContentType = file.ContentType, + FileName = file.Name, + FileUploadType = FileUploadType.Image, + StringBaseFile = Convert.ToBase64String(memoryStream.ToArray()) + }; + var token = await _userUtility.GetBearerTokenAsync(); + var rest = await _restWrapper.FileRestApi.UploadFileAsync(fileUpload, token); + Files.Insert(0, new StorageFileSDto + { + FileLocation = rest.FileLocation, + FileName = rest.FileName, + FileType = StorageFileType.Image + }); + } + catch (ApiException ex) + { + var exe = await ex.GetContentAsAsync(); + if (exe != null) + _snackbar.Add(exe.Message, Severity.Error); + _snackbar.Add(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/Models/Address.cs b/NetinaShop.AdminPanel.PWA/Models/Address.cs index 5034ac0..918fc38 100644 --- a/NetinaShop.AdminPanel.PWA/Models/Address.cs +++ b/NetinaShop.AdminPanel.PWA/Models/Address.cs @@ -14,4 +14,6 @@ public static class Address public static string ProductController = $"{BaseAddress}/product"; public static string BrandController = $"{BaseAddress}/brand"; public static string FileController => $"{BaseAddress}/file"; + public static string BlogController => $"{BaseAddress}/blog"; + public static string BlogCategoryController => $"{BaseAddress}/blog/category"; } \ No newline at end of file diff --git a/NetinaShop.AdminPanel.PWA/Pages/BlogCategoriesPage.razor b/NetinaShop.AdminPanel.PWA/Pages/BlogCategoriesPage.razor new file mode 100644 index 0000000..9579cff --- /dev/null +++ b/NetinaShop.AdminPanel.PWA/Pages/BlogCategoriesPage.razor @@ -0,0 +1,80 @@ +@page "/blog/categories" +@attribute [Microsoft.AspNetCore.Authorization.Authorize] + +@inject IDialogService DialogService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@inject IUserUtility UserUtility +@inject IRestWrapper RestWrapper + + + + + + دسته بندی بلاگـــــ ها + 124 عدد + + افزودن دسته بندی + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@code +{ + public BlogCategoriesPageViewModel ViewModel { get; set; } + protected override async Task OnInitializedAsync() + { + ViewModel = new BlogCategoriesPageViewModel(NavigationManager, Snackbar, UserUtility, RestWrapper, DialogService); + await ViewModel.InitializeAsync(); + await base.OnInitializedAsync(); + } +} \ No newline at end of file diff --git a/NetinaShop.AdminPanel.PWA/Pages/BlogCategoriesPage.razor.cs b/NetinaShop.AdminPanel.PWA/Pages/BlogCategoriesPage.razor.cs new file mode 100644 index 0000000..3240577 --- /dev/null +++ b/NetinaShop.AdminPanel.PWA/Pages/BlogCategoriesPage.razor.cs @@ -0,0 +1,185 @@ +using NetinaShop.Domain.Entities.Blogs; + +namespace NetinaShop.AdminPanel.PWA.Pages; + +public class BlogCategoriesPageViewModel:BaseViewModel> +{ + private readonly NavigationManager _navigationManager; + private readonly ISnackbar _snackbar; + private readonly IUserUtility _userUtility; + private readonly IDialogService _dialogService; + private readonly IRestWrapper _restWrapper; + + public string Search = string.Empty; + public int CurrentPage = 0; + public int PageCount = 1; + + public BlogCategoriesPageViewModel(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; + PageDto.Clear(); + var dto = await _restWrapper.CrudDtoApiRest(Address.BlogCategoryController) + .ReadAll(CurrentPage); + dto.ForEach(d => PageDto.Add(d)); + if (PageDto.Count == 20) + PageCount = 2; + } + 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 ChangePageAsync(int page) + { + CurrentPage = page - 1; + if (CurrentPage > PageCount - 2) + { + + try + { + IsProcessing = true; + + List dto = new List(); + if (Search.IsNullOrEmpty()) + { + dto = await _restWrapper.CrudDtoApiRest(Address.BlogCategoryController) + .ReadAll(CurrentPage); + } + else + { + dto = await _restWrapper.BlogCategoryRestApi.ReadAll(CurrentPage, Search); + } + + dto.ForEach(d => PageDto.Add(d)); + if (PageDto.Count % 20 == 0) + PageCount = CurrentPage + 2; + + } + 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; + } + } + } + + public async Task AddBlogCategoryClicked() + { + DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, DisableBackdropClick = true }; + await _dialogService.ShowAsync("افزودن دسته بندی جدید", maxWidth); + } + + public async Task EditBlogCategoryClicked(BlogCategorySDto blogCategory) + { + DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, DisableBackdropClick = true }; + var parameters = new DialogParameters(); + parameters.Add(x => x.BlogCategory, blogCategory); + await _dialogService.ShowAsync($"ویرایش دسته بندی {blogCategory.Name}", parameters, maxWidth); + } + + public async Task DeleteBlogCategoryAsync(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.BlogCategoryController) + .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; + } + } + } + + public async Task SearchChanged(string search) + { + if (search.IsNullOrEmpty() && !Search.IsNullOrEmpty()) + await InitializeAsync(); + Search = search; + } + public async Task SearchAsync() + { + try + { + if (Search.IsNullOrEmpty()) + throw new AppException("نام دسته بندی برای جست جو وارد نشده است"); + IsProcessing = true; + CurrentPage = 0; + PageCount = 1; + PageDto.Clear(); + var dto = await _restWrapper.BlogCategoryRestApi.ReadAll(CurrentPage, Search); + dto.ForEach(d => PageDto.Add(d)); + if (PageDto.Count == 20) + PageCount = 2; + } + 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/BlogsPage.razor b/NetinaShop.AdminPanel.PWA/Pages/BlogsPage.razor new file mode 100644 index 0000000..00e2c76 --- /dev/null +++ b/NetinaShop.AdminPanel.PWA/Pages/BlogsPage.razor @@ -0,0 +1,81 @@ +@page "/blogs" +@attribute [Microsoft.AspNetCore.Authorization.Authorize] + +@inject IDialogService DialogService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@inject IUserUtility UserUtility +@inject IRestWrapper RestWrapper + + + + + + بلاگـــــ ها + 124 عدد + + افزودن بلاگ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@code +{ + public BlogsPageViewModel ViewModel { get; set; } + protected override async Task OnInitializedAsync() + { + ViewModel = new BlogsPageViewModel(NavigationManager, Snackbar, UserUtility, RestWrapper, DialogService); + await ViewModel.InitializeAsync(); + await base.OnInitializedAsync(); + } +} diff --git a/NetinaShop.AdminPanel.PWA/Pages/BlogsPage.razor.cs b/NetinaShop.AdminPanel.PWA/Pages/BlogsPage.razor.cs new file mode 100644 index 0000000..32d6122 --- /dev/null +++ b/NetinaShop.AdminPanel.PWA/Pages/BlogsPage.razor.cs @@ -0,0 +1,185 @@ +using NetinaShop.Domain.Entities.Blogs; + +namespace NetinaShop.AdminPanel.PWA.Pages; + +public class BlogsPageViewModel : BaseViewModel> +{ + private readonly NavigationManager _navigationManager; + private readonly ISnackbar _snackbar; + private readonly IUserUtility _userUtility; + private readonly IDialogService _dialogService; + private readonly IRestWrapper _restWrapper; + + public string Search = string.Empty; + public int CurrentPage = 0; + public int PageCount = 1; + + public BlogsPageViewModel(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; + PageDto.Clear(); + var dto = await _restWrapper.CrudDtoApiRest(Address.BlogController) + .ReadAll(CurrentPage); + dto.ForEach(d => PageDto.Add(d)); + if (PageDto.Count == 20) + PageCount = 2; + } + 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 ChangePageAsync(int page) + { + CurrentPage = page - 1; + if (CurrentPage > PageCount - 2) + { + + try + { + IsProcessing = true; + + List dto = new List(); + if (Search.IsNullOrEmpty()) + { + dto = await _restWrapper.CrudDtoApiRest(Address.BlogController) + .ReadAll(CurrentPage); + } + else + { + dto = await _restWrapper.BlogRestApi.ReadAll(CurrentPage, Search); + } + + dto.ForEach(d => PageDto.Add(d)); + if (PageDto.Count % 20 == 0) + PageCount = CurrentPage + 2; + + } + 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; + } + } + } + + public async Task AddBlogClicked() + { + DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, DisableBackdropClick = true }; + await _dialogService.ShowAsync("افزودن بلاگ جدید", maxWidth); + } + + public async Task EditBlogClicked(BlogSDto blog) + { + DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, DisableBackdropClick = true }; + var parameters = new DialogParameters(); + parameters.Add(x => x.Blog, blog); + await _dialogService.ShowAsync($"ویرایش بلاگ {blog.Title}", parameters, maxWidth); + } + + public async Task DeleteBlogAsync(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.BlogController) + .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; + } + } + } + + public async Task SearchChanged(string search) + { + if (search.IsNullOrEmpty() && !Search.IsNullOrEmpty()) + await InitializeAsync(); + Search = search; + } + public async Task SearchAsync() + { + try + { + if (Search.IsNullOrEmpty()) + throw new AppException("نام بلاگ برای جست جو وارد نشده است"); + IsProcessing = true; + CurrentPage = 0; + PageCount = 1; + PageDto.Clear(); + var dto = await _restWrapper.BlogRestApi.ReadAll(CurrentPage, Search); + dto.ForEach(d => PageDto.Add(d)); + if (PageDto.Count == 20) + PageCount = 2; + } + 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/BrandsPage.razor b/NetinaShop.AdminPanel.PWA/Pages/BrandsPage.razor index 11dbd40..10f7bf6 100644 --- a/NetinaShop.AdminPanel.PWA/Pages/BrandsPage.razor +++ b/NetinaShop.AdminPanel.PWA/Pages/BrandsPage.razor @@ -24,10 +24,15 @@ + AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" class="my-auto" + ValueChanged="@ViewModel.SearchChanged" + Clearable="true" + OnAdornmentClick="@ViewModel.SearchAsync"> @@ -48,7 +53,11 @@ - + + + + + + + +
diff --git a/NetinaShop.AdminPanel.PWA/Pages/BrandsPage.razor.cs b/NetinaShop.AdminPanel.PWA/Pages/BrandsPage.razor.cs index 8f4b45a..5ac0b55 100644 --- a/NetinaShop.AdminPanel.PWA/Pages/BrandsPage.razor.cs +++ b/NetinaShop.AdminPanel.PWA/Pages/BrandsPage.razor.cs @@ -9,6 +9,9 @@ public class BrandsPageViewModel : BaseViewModel> private readonly IUserUtility _userUtility; private readonly IDialogService _dialogService; private readonly IRestWrapper _restWrapper; + public string Search = string.Empty; + public int CurrentPage = 0; + public int PageCount = 1; public BrandsPageViewModel(NavigationManager navigationManager, ISnackbar snackbar, IUserUtility userUtility, IRestWrapper restWrapper, IDialogService dialogService) { @@ -51,6 +54,12 @@ public class BrandsPageViewModel : BaseViewModel> await _dialogService.ShowAsync("افزودن برند جدید", maxWidth); } + public async Task EditBrandAsync(BrandSDto brand) + { + DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, DisableBackdropClick = true }; + var parameters = new DialogParameters { { x => x.Brand, brand } }; + await _dialogService.ShowAsync($"ویرایش برند {brand.Name}", parameters, maxWidth); + } public async Task DeleteBrandAsync(Guid selectedCategoryId) { var options = new DialogOptions { CloseOnEscapeKey = true }; @@ -88,4 +97,85 @@ public class BrandsPageViewModel : BaseViewModel> } } } + + + public async Task SearchChanged(string search) + { + if (search.IsNullOrEmpty() && !Search.IsNullOrEmpty()) + await InitializeAsync(); + Search = search; + } + public async Task SearchAsync() + { + try + { + if (Search.IsNullOrEmpty()) + throw new AppException("نام برند برای جست جو وارد نشده است"); + IsProcessing = true; + CurrentPage = 0; + PageCount = 1; + PageDto.Clear(); + var dto = await _restWrapper.BrandRestApi.ReadAll(CurrentPage, Search); + dto.ForEach(d => PageDto.Add(d)); + if (PageDto.Count == 20) + PageCount = 2; + } + 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; + } + } + + public async Task ChangePageAsync(int page) + { + CurrentPage = page - 1; + if (CurrentPage > PageCount - 2) + { + + try + { + IsProcessing = true; + + List dto = new List(); + if (Search.IsNullOrEmpty()) + { + dto = await _restWrapper.CrudDtoApiRest(Address.BrandController) + .ReadAll(CurrentPage); + } + else + { + dto = await _restWrapper.BrandRestApi.ReadAll(CurrentPage, Search); + } + + dto.ForEach(d => PageDto.Add(d)); + if (PageDto.Count % 20 == 0) + PageCount = CurrentPage + 2; + + } + 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/Services/RestServices/IBlogCategoryRestApi.cs b/NetinaShop.AdminPanel.PWA/Services/RestServices/IBlogCategoryRestApi.cs new file mode 100644 index 0000000..3e44d71 --- /dev/null +++ b/NetinaShop.AdminPanel.PWA/Services/RestServices/IBlogCategoryRestApi.cs @@ -0,0 +1,13 @@ +namespace NetinaShop.AdminPanel.PWA.Services.RestServices; + +public interface IBlogCategoryRestApi +{ + [Get("")] + Task> ReadAll(); + [Get("")] + Task> ReadAll([Query] int page); + [Get("")] + Task> ReadAll([Query] int page, [Query] string productName); + [Get("")] + Task> ReadAll([Query] string blogCategoryName); +} \ No newline at end of file diff --git a/NetinaShop.AdminPanel.PWA/Services/RestServices/IBlogRestApi.cs b/NetinaShop.AdminPanel.PWA/Services/RestServices/IBlogRestApi.cs new file mode 100644 index 0000000..7e80b23 --- /dev/null +++ b/NetinaShop.AdminPanel.PWA/Services/RestServices/IBlogRestApi.cs @@ -0,0 +1,11 @@ +namespace NetinaShop.AdminPanel.PWA.Services.RestServices; + +public interface IBlogRestApi +{ + [Get("")] + Task> ReadAll(); + [Get("")] + Task> ReadAll([Query] int page); + [Get("")] + Task> ReadAll([Query] int page, [Query] string blogName); +} \ No newline at end of file diff --git a/NetinaShop.AdminPanel.PWA/Services/RestServices/IRestWrapper.cs b/NetinaShop.AdminPanel.PWA/Services/RestServices/IRestWrapper.cs index 2cb0bbd..ea6d982 100644 --- a/NetinaShop.AdminPanel.PWA/Services/RestServices/IRestWrapper.cs +++ b/NetinaShop.AdminPanel.PWA/Services/RestServices/IRestWrapper.cs @@ -12,4 +12,6 @@ public interface IRestWrapper public IProductRestApi ProductRestApi { get; } public IBrandRestApi BrandRestApi { get; } public IFileRestApi FileRestApi { get; } + public IBlogRestApi BlogRestApi { get; } + public IBlogCategoryRestApi BlogCategoryRestApi { get; } } \ No newline at end of file diff --git a/NetinaShop.AdminPanel.PWA/Services/RestServices/RestWrapper.cs b/NetinaShop.AdminPanel.PWA/Services/RestServices/RestWrapper.cs index 6c8ac7c..ddc5e5c 100644 --- a/NetinaShop.AdminPanel.PWA/Services/RestServices/RestWrapper.cs +++ b/NetinaShop.AdminPanel.PWA/Services/RestServices/RestWrapper.cs @@ -24,4 +24,6 @@ public class RestWrapper : IRestWrapper public IProductRestApi ProductRestApi => RestService.For(Address.ProductController, setting); public IBrandRestApi BrandRestApi => RestService.For(Address.BrandController, setting); public IFileRestApi FileRestApi => RestService.For(Address.FileController, setting); + public IBlogRestApi BlogRestApi => RestService.For(Address.BlogController, setting); + public IBlogCategoryRestApi BlogCategoryRestApi => RestService.For(Address.BlogCategoryController, setting); } \ No newline at end of file diff --git a/NetinaShop.AdminPanel.PWA/wwwroot/css/app.min.css b/NetinaShop.AdminPanel.PWA/wwwroot/css/app.min.css index c8c40aa..bd67f72 100644 --- a/NetinaShop.AdminPanel.PWA/wwwroot/css/app.min.css +++ b/NetinaShop.AdminPanel.PWA/wwwroot/css/app.min.css @@ -504,6 +504,9 @@ video { .absolute { position: absolute; } +.bottom-0 { + bottom: 0px; +} .m-1 { margin: 0.25rem; } @@ -610,6 +613,10 @@ video { .h-7 { height: 1.75rem; } +.h-fit { + height: -moz-fit-content; + height: fit-content; +} .h-full { height: 100%; } @@ -622,6 +629,12 @@ video { .min-h-\[10rem\] { min-height: 10rem; } +.min-h-\[28rem\] { + min-height: 28rem; +} +.min-h-\[33rem\] { + min-height: 33rem; +} .w-10 { width: 2.5rem; } @@ -712,6 +725,14 @@ video { .bg-\[--color-background\] { background-color: var(--color-background); } +.bg-blue-500 { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); +} +.bg-pink-500 { + --tw-bg-opacity: 1; + background-color: rgb(236 72 153 / var(--tw-bg-opacity)); +} .bg-transparent { background-color: transparent; } @@ -728,10 +749,26 @@ video { .p-8 { padding: 2rem; } +.px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; +} +.px-1\.5 { + padding-left: 0.375rem; + padding-right: 0.375rem; +} .px-4 { padding-left: 1rem; padding-right: 1rem; } +.py-0 { + padding-top: 0px; + padding-bottom: 0px; +} +.py-0\.5 { + padding-top: 0.125rem; + padding-bottom: 0.125rem; +} .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; @@ -772,6 +809,10 @@ video { --tw-text-opacity: 1; color: rgb(59 130 246 / var(--tw-text-opacity)); } +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} .shadow-sm { --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); diff --git a/NetinaShop.AdminPanel.PWA/wwwroot/css/app.output.css b/NetinaShop.AdminPanel.PWA/wwwroot/css/app.output.css index 8ba49f1..cbfc75e 100644 --- a/NetinaShop.AdminPanel.PWA/wwwroot/css/app.output.css +++ b/NetinaShop.AdminPanel.PWA/wwwroot/css/app.output.css @@ -558,6 +558,10 @@ video { position: absolute; } +.bottom-0 { + bottom: 0px; +} + .m-1 { margin: 0.25rem; } @@ -696,6 +700,11 @@ video { height: 1.75rem; } +.h-fit { + height: -moz-fit-content; + height: fit-content; +} + .h-full { height: 100%; } @@ -712,6 +721,14 @@ video { min-height: 10rem; } +.min-h-\[28rem\] { + min-height: 28rem; +} + +.min-h-\[33rem\] { + min-height: 33rem; +} + .w-10 { width: 2.5rem; } @@ -831,6 +848,16 @@ video { background-color: var(--color-background); } +.bg-blue-500 { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); +} + +.bg-pink-500 { + --tw-bg-opacity: 1; + background-color: rgb(236 72 153 / var(--tw-bg-opacity)); +} + .bg-transparent { background-color: transparent; } @@ -852,11 +879,31 @@ video { padding: 2rem; } +.px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; +} + +.px-1\.5 { + padding-left: 0.375rem; + padding-right: 0.375rem; +} + .px-4 { padding-left: 1rem; padding-right: 1rem; } +.py-0 { + padding-top: 0px; + padding-bottom: 0px; +} + +.py-0\.5 { + padding-top: 0.125rem; + padding-bottom: 0.125rem; +} + .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; @@ -909,6 +956,11 @@ video { color: rgb(59 130 246 / var(--tw-text-opacity)); } +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + .shadow-sm { --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);