Refactor and enhance product and order handling
- Updated `DiscountActionDialogBoxViewModel` and `FastProductCreateDialogBoxViewModel` to create command objects directly from properties and improved error handling. - Added meta tag management UI and logic in `ProductActionDialogBox.razor` and `ProductActionDialogBoxViewModel`. - Increased max file read stream size to 8 MB in `StorageDialogBoxViewModel`. - Incremented `AssemblyVersion` and `FileVersion` to `1.7.20.34` in `Netina.AdminPanel.PWA.csproj`. - Updated `BrandsPage.razor` and `BrandsPageViewModel` for pagination and service injection. - Updated `CategoriesPageViewModel` to create command objects directly from properties. - Updated `ProductsPage.razor` for service injection and added a button for product details. - Updated `ICrudApiRest` and `ICrudDtoApiRest` interfaces to use generic `Create` methods. - Updated `appsettings.Development.json` for `StorageBaseUrl` and commented out `IsShop`. - Added new project `AppHost.csproj` targeting .NET 8.0 with Aspire hosting. - Added new `appsettings.Development.json` and `appsettings.json` for logging. - Added new `Program.cs` to create and run a distributed application. - Added new `launchSettings.json` for application launch settings. - Added `Extensions.cs` for common .NET Aspire services. - Added new project `ServiceDefaults.csproj` for shared service defaults. - Introduced `ProductMetaTag` class and related migration for meta tag handling. - Updated `OrderController.cs` for additional authorization requirements. - Updated target frameworks to `net8.0` in various projects. - Enhanced `SiteMapService.cs` to include brand site maps. - Added new properties to DTOs for customer and meta tag handling. - Enhanced `Product` class with meta tag management methods. - Refactored `OrderMapper.g.cs` and `ProductMapper.g.cs` for improved mapping logic. - Enhanced command handlers to manage meta tags. - Added `ICurrentUserService` for user permissions in query handlers. - Refactored `StorageService.cs` for paginated storage file fetching.subProduct
parent
91d9b602f8
commit
0e7c1d7f81
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsAspireHost>true</IsAspireHost>
|
||||
<UserSecretsId>045801e7-58a1-4ee6-9d28-93c23b5dcd6b</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.2.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Netina.AdminPanel.PWA\Netina.AdminPanel.PWA.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,3 @@
|
|||
var builder = DistributedApplication.CreateBuilder(args);
|
||||
|
||||
builder.Build().Run();
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:17057;http://localhost:15006",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"DOTNET_ENVIRONMENT": "Development",
|
||||
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21274",
|
||||
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22232"
|
||||
}
|
||||
},
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:15006",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"DOTNET_ENVIRONMENT": "Development",
|
||||
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19034",
|
||||
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20044"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Aspire.Hosting.Dcp": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -194,16 +194,42 @@ public class DiscountActionDialogBoxViewModel : BaseViewModel<DiscountLDto>
|
|||
if(StartDate != null)
|
||||
PageDto.StartDate = StartDate.Value;
|
||||
|
||||
var request = PageDto.Adapt<CreateDiscountCommand>();
|
||||
await _restWrapper.CrudApiRest<Discount, Guid>(Address.DiscountController).Create<CreateDiscountCommand>(request, token);
|
||||
var request = new CreateDiscountCommand(PageDto.Code,
|
||||
PageDto.Description,
|
||||
PageDto.DiscountPercent,
|
||||
PageDto.DiscountAmount,
|
||||
PageDto.HasCode,
|
||||
PageDto.AmountType,
|
||||
PageDto.Type,
|
||||
PageDto.Count,
|
||||
PageDto.StartDate,
|
||||
PageDto.ExpireDate,
|
||||
PageDto.Immortal,
|
||||
PageDto.PriceFloor,
|
||||
PageDto.HasPriceFloor,
|
||||
PageDto.PriceCeiling,
|
||||
PageDto.HasPriceCeiling,
|
||||
PageDto.IsInfinity,
|
||||
PageDto.UseCount,
|
||||
PageDto.IsForInvitation,
|
||||
PageDto.IsSpecialOffer,
|
||||
PageDto.IsForFirstPurchase,
|
||||
PageDto.ProductId,
|
||||
PageDto.CategoryId);
|
||||
await _restWrapper.CrudDtoApiRest<Discount,DiscountLDto, Guid>(Address.DiscountController).Create<CreateDiscountCommand>(request, token);
|
||||
|
||||
_snackbar.Add($"ساخت تخفیف با موفقیت انجام شد", Severity.Success);
|
||||
_mudDialog.Close(true);
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
if (ex.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
var exe = await ex.GetContentAsAsync<ApiResult>();
|
||||
_snackbar.Add(exe != null ? exe.Message : ex.Content, Severity.Error);
|
||||
}
|
||||
else
|
||||
_snackbar.Add(ex.Content, Severity.Error);
|
||||
_mudDialog.Cancel();
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -248,7 +274,30 @@ public class DiscountActionDialogBoxViewModel : BaseViewModel<DiscountLDto>
|
|||
if (token == null)
|
||||
throw new Exception("Token is null");
|
||||
PageDto.Id = _discountId;
|
||||
var request = PageDto.Adapt<UpdateDiscountCommand>();
|
||||
var request = new UpdateDiscountCommand(
|
||||
PageDto.Id,
|
||||
PageDto.Code,
|
||||
PageDto.Description,
|
||||
PageDto.DiscountPercent,
|
||||
PageDto.DiscountAmount,
|
||||
PageDto.HasCode,
|
||||
PageDto.AmountType,
|
||||
PageDto.Type,
|
||||
PageDto.Count,
|
||||
PageDto.StartDate,
|
||||
PageDto.ExpireDate,
|
||||
PageDto.Immortal,
|
||||
PageDto.PriceFloor,
|
||||
PageDto.HasPriceFloor,
|
||||
PageDto.PriceCeiling,
|
||||
PageDto.HasPriceCeiling,
|
||||
PageDto.IsInfinity,
|
||||
PageDto.UseCount,
|
||||
PageDto.IsForInvitation,
|
||||
PageDto.IsSpecialOffer,
|
||||
PageDto.IsForFirstPurchase,
|
||||
PageDto.ProductId,
|
||||
PageDto.CategoryId);
|
||||
await _restWrapper.CrudApiRest<Discount, Guid>(Address.DiscountController).Update<UpdateDiscountCommand>(request, token);
|
||||
_snackbar.Add($"ویرایش تخفیف با موفقیت انجام شد", Severity.Success);
|
||||
_mudDialog.Close(true);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Netina.Domain.Entities.Brands;
|
||||
|
||||
namespace Netina.AdminPanel.PWA.Dialogs;
|
||||
|
@ -141,19 +142,29 @@ public class FastProductCreateDialogBoxViewModel(ISnackbar snackbar, IRestWrappe
|
|||
}
|
||||
|
||||
product.Cost *= 10;
|
||||
var command = product.Adapt<CreateProductCommand>() with
|
||||
{
|
||||
BeDisplayed = true,
|
||||
BrandId = brand.Id,
|
||||
CategoryId = SelectedCategory.Id,
|
||||
Files = files,
|
||||
Specifications = specifications
|
||||
};
|
||||
var id = await restWrapper.CrudApiRest<Product, Guid>(Address.ProductController)
|
||||
.Create(command, token);
|
||||
var command = new CreateProductCommand(product.PersianName, product.EnglishName, product.Summery,
|
||||
product.ExpertCheck,
|
||||
product.Tags,
|
||||
product.Warranty,
|
||||
true,
|
||||
product.Cost,
|
||||
product.PackingCost,
|
||||
product.Stock,
|
||||
product.HasExpressDelivery,
|
||||
product.MaxOrderCount,
|
||||
product.IsSpecialOffer,
|
||||
brand.Id,
|
||||
SelectedCategory.Id,
|
||||
null,
|
||||
specifications,
|
||||
files,
|
||||
new Dictionary<string, string>(),
|
||||
new Dictionary<string, string>());
|
||||
await restWrapper.CrudApiRest<Product, Guid>(Address.ProductController).Create<CreateProductCommand>(command, token);
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
if (ex.StatusCode != HttpStatusCode.OK)
|
||||
snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
@ -182,6 +182,59 @@
|
|||
<MudGrid>
|
||||
<MudItem xs="12">
|
||||
|
||||
<MudText Typo="Typo.h6">اطلاعات متا تگ</MudText>
|
||||
<MudText Typo="Typo.caption">می توانید متا تگ های سئو برای صفحه مورد نظر را وارد کنید</MudText>
|
||||
<MudGrid>
|
||||
|
||||
<MudItem lg="5" md="6">
|
||||
<MudTextField @bind-Value="@ViewModel.MetaTagType" T="string" Label="نوع" Variant="Variant.Outlined"></MudTextField>
|
||||
</MudItem>
|
||||
|
||||
<MudItem lg="5" md="6">
|
||||
<MudTextField @bind-Value="@ViewModel.MetaTagValue" T="string" Label="مقدار" Variant="Variant.Outlined"></MudTextField>
|
||||
</MudItem>
|
||||
|
||||
<MudItem lg="2" md="12">
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Size="Size.Large"
|
||||
Color="Color.Info"
|
||||
class="mt-2 w-full py-3"
|
||||
OnClick="ViewModel.AddMetaTag"
|
||||
StartIcon="@Icons.Material.Outlined.Add">افزودن</MudButton>
|
||||
</MudItem>
|
||||
|
||||
<MudItem sm="12">
|
||||
|
||||
<MudDataGrid Items="@ViewModel.MetaTags"
|
||||
T="MetaTagSDto"
|
||||
Elevation="0"
|
||||
Outlined="true"
|
||||
Bordered="true"
|
||||
Striped="true"
|
||||
Filterable="false"
|
||||
SortMode="@SortMode.None"
|
||||
Groupable="false">
|
||||
<Columns>
|
||||
<PropertyColumn T="MetaTagSDto" TProperty="string" Property="x => x.Type" Title="نوع"/>
|
||||
<PropertyColumn T="MetaTagSDto" TProperty="string" Property="x => x.Value" Title="مقدار"/>
|
||||
<TemplateColumn T="MetaTagSDto" CellClass="d-flex justify-end">
|
||||
<CellTemplate>
|
||||
<MudStack Row>
|
||||
<MudButton DisableElevation="true"
|
||||
Size="@Size.Small"
|
||||
Variant="@Variant.Filled"
|
||||
OnClick="() => ViewModel.MetaTags.Remove(context.Item)"
|
||||
Color="@Color.Error"
|
||||
StartIcon="@Icons.Material.Outlined.Delete">حذف</MudButton>
|
||||
</MudStack>
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
</Columns>
|
||||
</MudDataGrid>
|
||||
</MudItem>
|
||||
|
||||
</MudGrid>
|
||||
|
||||
<MudText Typo="Typo.h6">سوالات متداول</MudText>
|
||||
<MudText Typo="Typo.caption">می توانید سوالات متداول شهر موردنظر را وارد کنید</MudText>
|
||||
<MudGrid class="mt-1">
|
||||
|
@ -215,7 +268,7 @@
|
|||
Size="@Size.Small"
|
||||
Variant="@Variant.Outlined"
|
||||
Color="@Color.Error"
|
||||
OnClick="()=>ViewModel.Faqs.Remove(item.Key)" />
|
||||
OnClick="() => ViewModel.Faqs.Remove(item.Key)"/>
|
||||
<MudText>@item.Key</MudText>
|
||||
</MudStack>
|
||||
</TitleContent>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
namespace Netina.AdminPanel.PWA.Dialogs;
|
||||
using Netina.Domain.Entities.Seo;
|
||||
|
||||
namespace Netina.AdminPanel.PWA.Dialogs;
|
||||
|
||||
public class ProductActionDialogBoxViewModel : BaseViewModel<ProductLDto>
|
||||
{
|
||||
|
@ -89,6 +91,7 @@ public class ProductActionDialogBoxViewModel : BaseViewModel<ProductLDto>
|
|||
PageDto = productLDto;
|
||||
productLDto.Specifications.ForEach(s => Specifications.Add(s));
|
||||
productLDto.Files.ForEach(f => Files.Add(f));
|
||||
productLDto.MetaTags.ForEach(m => MetaTags.Add(m));
|
||||
SelectedCategory = new ProductCategorySDto { Id = productLDto.CategoryId, Name = productLDto.CategoryName };
|
||||
SelectedBrand = new BrandSDto { Id = productLDto.BrandId, PersianName = productLDto.BrandName };
|
||||
PageDto.IsSpecialOffer = productLDto.IsSpecialOffer;
|
||||
|
@ -193,7 +196,7 @@ public class ProductActionDialogBoxViewModel : BaseViewModel<ProductLDto>
|
|||
PageDto.Specifications,
|
||||
PageDto.Files,
|
||||
Faqs,
|
||||
new Dictionary<string, string>());
|
||||
MetaTags.ToDictionary(x => x.Type, x => x.Value));
|
||||
|
||||
await _restWrapper.CrudApiRest<Product, Guid>(Address.ProductController).Update<UpdateProductCommand>(request, token);
|
||||
_snackbar.Add($"ویرایش محصول {PageDto.PersianName} با موفقیت انجام شد", Severity.Success);
|
||||
|
@ -258,8 +261,8 @@ public class ProductActionDialogBoxViewModel : BaseViewModel<ProductLDto>
|
|||
PageDto.Specifications,
|
||||
PageDto.Files,
|
||||
Faqs,
|
||||
new Dictionary<string, string>());
|
||||
await _restWrapper.CrudApiRest<Product, Guid>(Address.ProductController).Create<CreateProductCommand>(request, token);
|
||||
MetaTags.ToDictionary(x => x.Type, x => x.Value));
|
||||
await _restWrapper.CrudDtoApiRest<Product, ProductLDto, Guid>(Address.ProductController).Create<CreateProductCommand>(request, token);
|
||||
|
||||
_snackbar.Add($"ساخت محصول {PageDto.PersianName} با موفقیت انجام شد", Severity.Success);
|
||||
_mudDialog.Close(DialogResult.Ok(true));
|
||||
|
@ -376,6 +379,25 @@ public class ProductActionDialogBoxViewModel : BaseViewModel<ProductLDto>
|
|||
}
|
||||
}
|
||||
|
||||
public readonly ObservableCollection<MetaTagSDto> MetaTags = new();
|
||||
public string MetaTagType { get; set; } = string.Empty;
|
||||
public string MetaTagValue { get; set; } = string.Empty;
|
||||
public void AddMetaTag()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MetaTagType.IsNullOrEmpty())
|
||||
throw new Exception("لطفا نوع متا مورد نظر را وارد کنید");
|
||||
if (MetaTagValue.IsNullOrEmpty())
|
||||
throw new Exception("لطفا مقدار متا مورد نظر را وارد کنید");
|
||||
|
||||
MetaTags.Add(new MetaTagSDto() { Type = MetaTagType, Value = MetaTagValue });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_snackbar.Add(e.Message, Severity.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddSpecification()
|
||||
{
|
||||
|
|
|
@ -115,7 +115,7 @@ public class StorageDialogBoxViewModel : BaseViewModel
|
|||
IsProcessing = true;
|
||||
using var memoryStream = new MemoryStream();
|
||||
var file = obj.File;
|
||||
var stream = file.OpenReadStream();
|
||||
var stream = file.OpenReadStream(8000000);
|
||||
await stream.CopyToAsync(memoryStream);
|
||||
|
||||
var fileUpload = new FileUploadRequest
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
|
||||
<AssemblyVersion>1.6.18.28</AssemblyVersion>
|
||||
<FileVersion>1.6.18.28</FileVersion>
|
||||
<AssemblyVersion>1.7.20.34</AssemblyVersion>
|
||||
<FileVersion>1.7.20.34</FileVersion>
|
||||
<AssemblyName>$(MSBuildProjectName)</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
@page "/product/brands"
|
||||
@using StringExtensions = Netina.Common.Extensions.StringExtensions
|
||||
|
||||
@inject IDialogService DialogService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IRestWrapper RestWrapper
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IUserUtility UserUtility
|
||||
@inject IConfiguration Configuration
|
||||
@inject IJSRuntime JsRuntime
|
||||
|
||||
|
||||
<MudStack class="h-full w-full p-8">
|
||||
|
@ -25,7 +28,7 @@
|
|||
<MudPaper>
|
||||
<MudDataGrid Striped="true" T="BrandSDto" Items="@ViewModel.PageDto" Filterable="false" Loading="@ViewModel.IsProcessing"
|
||||
CurrentPage="@ViewModel.CurrentPage"
|
||||
RowsPerPage="15"
|
||||
RowsPerPage="10"
|
||||
SortMode="@SortMode.None" Groupable="false">
|
||||
<ToolBarContent>
|
||||
<MudTextField T="string" Placeholder="جست جو بر اساس نام" Adornment="Adornment.Start" Immediate="true"
|
||||
|
@ -37,7 +40,6 @@
|
|||
<Columns>
|
||||
<PropertyColumn Title="نام برند" Property="arg => arg.PersianName" />
|
||||
<PropertyColumn Title="نام انگلیسی برند" Property="arg => arg.EnglishName" />
|
||||
<PropertyColumn Title="توضیحاتــ" Property="arg => arg.Description" />
|
||||
<TemplateColumn Title="صفحه اختصاصی دارد" T="BrandSDto">
|
||||
<CellTemplate>
|
||||
@if (@context.Item.HasSpecialPage)
|
||||
|
@ -54,15 +56,22 @@
|
|||
<TemplateColumn CellClass="d-flex justify-end">
|
||||
<CellTemplate>
|
||||
<MudStack Row="true">
|
||||
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Link"
|
||||
Size="@Size.Small"
|
||||
Variant="@Variant.Outlined"
|
||||
Color="@Color.Surface"
|
||||
OnClick="async()=>await ShowBrand(context.Item)" />
|
||||
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Edit"
|
||||
Size="@Size.Small"
|
||||
Variant="@Variant.Outlined"
|
||||
Color="@Color.Info"
|
||||
OnClick="async()=>await ViewModel.EditBrandAsync(context.Item)"></MudIconButton>
|
||||
OnClick="async () => await ViewModel.EditBrandAsync(context.Item)"></MudIconButton>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||
Size="@Size.Small"
|
||||
Variant="@Variant.Outlined"
|
||||
OnClick="async() => await ViewModel.DeleteBrandAsync(context.Item.Id)"
|
||||
OnClick="async () => await ViewModel.DeleteBrandAsync(context.Item.Id)"
|
||||
Color="@Color.Error"></MudIconButton>
|
||||
</MudStack>
|
||||
</CellTemplate>
|
||||
|
@ -92,8 +101,13 @@
|
|||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@code {
|
||||
private async Task ShowBrand(BrandSDto item)
|
||||
{
|
||||
var webUrl = Configuration.GetValue<string>("WebSiteUrl") ?? string.Empty;
|
||||
var slug = WebUtility.UrlEncode(item.Slug.Replace(' ', '-'));
|
||||
var url = $"{webUrl}/brands/{item.Id}/{slug}";
|
||||
await JsRuntime.InvokeVoidAsync("open", url, "_blank");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ public class BrandsPageViewModel(
|
|||
var dto = await restWrapper.CrudDtoApiRest<Brand, BrandSDto, Guid>(Address.BrandController)
|
||||
.ReadAll(0);
|
||||
PageDto = dto;
|
||||
if (PageDto.Count == 10)
|
||||
PageCount = 2;
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
|
@ -108,7 +110,7 @@ public class BrandsPageViewModel(
|
|||
PageDto.Clear();
|
||||
var dto = await restWrapper.BrandRestApi.ReadAll(CurrentPage, Search);
|
||||
dto.ForEach(d => PageDto.Add(d));
|
||||
if (PageDto.Count == 20)
|
||||
if (PageDto.Count == 10)
|
||||
PageCount = 2;
|
||||
}
|
||||
catch (ApiException ex)
|
||||
|
@ -149,7 +151,7 @@ public class BrandsPageViewModel(
|
|||
}
|
||||
|
||||
dto.ForEach(d => PageDto.Add(d));
|
||||
if (PageDto.Count % 20 == 0)
|
||||
if (PageDto.Count % 10 == 0)
|
||||
PageCount = CurrentPage + 2;
|
||||
|
||||
}
|
||||
|
|
|
@ -152,15 +152,17 @@ public class CategoriesPageViewModel(
|
|||
public bool ChangeOriginalCategoryVisibility() => OriginalCategoryVisibility = !OriginalCategoryVisibility;
|
||||
public void AddFastProductCategory(string categoryName, Guid? parentId = null)
|
||||
{
|
||||
if(categoryName.IsNullOrEmpty())
|
||||
if (categoryName.IsNullOrEmpty())
|
||||
return;
|
||||
ProductCategorySDto category = new ProductCategorySDto { Name = categoryName, IsMain = true};
|
||||
ProductCategorySDto category = new ProductCategorySDto { Name = categoryName, IsMain = true };
|
||||
if (parentId != null)
|
||||
{
|
||||
category.IsMain = false;
|
||||
category.ParentId = parentId.Value;
|
||||
|
||||
}
|
||||
var command = category.Adapt<CreateProductCategoryCommand>() with{Files = new()};
|
||||
|
||||
var command = new CreateProductCategoryCommand(category.Name, category.Description, category.IsMain, category.ParentId, new(), new Dictionary<string, string>(), new Dictionary<string, string>());
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
@inject IUserUtility UserUtility
|
||||
@inject IRestWrapper RestWrapper
|
||||
@inject IBrowserViewportService BrowserViewportService
|
||||
@inject IConfiguration Configuration
|
||||
@inject IJSRuntime JsRuntime
|
||||
|
||||
<MudStack class="h-full w-full p-8">
|
||||
<MudGrid>
|
||||
|
@ -180,6 +182,12 @@
|
|||
<TemplateColumn CellClass="d-flex justify-end">
|
||||
<CellTemplate>
|
||||
<MudStack Row="true">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Link"
|
||||
Size="@Size.Small"
|
||||
Variant="@Variant.Outlined"
|
||||
Color="@Color.Surface"
|
||||
OnClick="async()=>await ShowProduct(context.Item)" />
|
||||
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Edit"
|
||||
Size="@Size.Small"
|
||||
Variant="@Variant.Outlined"
|
||||
|
@ -217,4 +225,12 @@
|
|||
await ViewModel.InitializeAsync();
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
private async Task ShowProduct(ProductSDto item)
|
||||
{
|
||||
var webUrl = Configuration.GetValue<string>("WebSiteUrl") ?? string.Empty;
|
||||
var slug = WebUtility.UrlEncode(item.Slug.Replace(' ', '-'));
|
||||
var url = $"{webUrl}/products/{item.Id}/{slug}";
|
||||
await JsRuntime.InvokeVoidAsync("open", url, "_blank");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public interface ICrudApiRest<T, TKey> where T : class
|
|||
public interface ICrudDtoApiRest<T, TDto, in TKey> where T : class where TDto : class
|
||||
{
|
||||
[Post("")]
|
||||
Task Create([Body] T payload, [Header("Authorization")] string authorization);
|
||||
Task Create<TCreateCommand>([Body] TCreateCommand payload, [Header("Authorization")] string authorization);
|
||||
[Post("")]
|
||||
Task Create([Body] TDto payload, [Header("Authorization")] string authorization);
|
||||
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
//"WebSiteUrl": "https://bonsaigallery.shop",
|
||||
//"AdminPanelBaseUrl": "https://admin.bonsaigallery.shop",
|
||||
//"StorageBaseUrl": "https://storage.bonsaigallery.shop",
|
||||
//"StorageBaseUrl": "https://storage.bonsaigallery.shop/",
|
||||
//"ApiUrl": "https://api.bonsaigallery.shop/api",
|
||||
"IsShop": true
|
||||
//"IsShop": true
|
||||
|
||||
//"WebSiteUrl": "https://hamyanedalat.com",
|
||||
//"AdminPanelBaseUrl": "https://admin.hamyanedalat.com",
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.ServiceDiscovery;
|
||||
using OpenTelemetry;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace Microsoft.Extensions.Hosting;
|
||||
|
||||
// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
|
||||
// This project should be referenced by each service project in your solution.
|
||||
// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
|
||||
public static class Extensions
|
||||
{
|
||||
public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
|
||||
{
|
||||
builder.ConfigureOpenTelemetry();
|
||||
|
||||
builder.AddDefaultHealthChecks();
|
||||
|
||||
builder.Services.AddServiceDiscovery();
|
||||
|
||||
builder.Services.ConfigureHttpClientDefaults(http =>
|
||||
{
|
||||
// Turn on resilience by default
|
||||
http.AddStandardResilienceHandler();
|
||||
|
||||
// Turn on service discovery by default
|
||||
http.AddServiceDiscovery();
|
||||
});
|
||||
|
||||
// Uncomment the following to restrict the allowed schemes for service discovery.
|
||||
// builder.Services.Configure<ServiceDiscoveryOptions>(options =>
|
||||
// {
|
||||
// options.AllowedSchemes = ["https"];
|
||||
// });
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
|
||||
{
|
||||
builder.Logging.AddOpenTelemetry(logging =>
|
||||
{
|
||||
logging.IncludeFormattedMessage = true;
|
||||
logging.IncludeScopes = true;
|
||||
});
|
||||
|
||||
builder.Services.AddOpenTelemetry()
|
||||
.WithMetrics(metrics =>
|
||||
{
|
||||
metrics.AddAspNetCoreInstrumentation()
|
||||
.AddHttpClientInstrumentation()
|
||||
.AddRuntimeInstrumentation();
|
||||
})
|
||||
.WithTracing(tracing =>
|
||||
{
|
||||
tracing.AddAspNetCoreInstrumentation()
|
||||
// Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
|
||||
//.AddGrpcClientInstrumentation()
|
||||
.AddHttpClientInstrumentation();
|
||||
});
|
||||
|
||||
builder.AddOpenTelemetryExporters();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
|
||||
{
|
||||
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
|
||||
|
||||
if (useOtlpExporter)
|
||||
{
|
||||
builder.Services.AddOpenTelemetry().UseOtlpExporter();
|
||||
}
|
||||
|
||||
// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
|
||||
//if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
|
||||
//{
|
||||
// builder.Services.AddOpenTelemetry()
|
||||
// .UseAzureMonitor();
|
||||
//}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddHealthChecks()
|
||||
// Add a default liveness check to ensure app is responsive
|
||||
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static WebApplication MapDefaultEndpoints(this WebApplication app)
|
||||
{
|
||||
// Adding health checks endpoints to applications in non-development environments has security implications.
|
||||
// See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
// All health checks must pass for app to be considered ready to accept traffic after starting
|
||||
app.MapHealthChecks("/health");
|
||||
|
||||
// Only health checks tagged with the "live" tag must pass for app to be considered alive
|
||||
app.MapHealthChecks("/alive", new HealthCheckOptions
|
||||
{
|
||||
Predicate = r => r.Tags.Contains("live")
|
||||
});
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsAspireSharedProject>true</IsAspireSharedProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.10.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.2.2" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Loading…
Reference in New Issue