Add product and category management pages
This commit is contained in:
@@ -28,3 +28,9 @@
|
|||||||
</NotFound>
|
</NotFound>
|
||||||
</Router>
|
</Router>
|
||||||
</CascadingAuthenticationState>
|
</CascadingAuthenticationState>
|
||||||
|
|
||||||
|
@code{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -21,10 +21,15 @@
|
|||||||
|
|
||||||
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
|
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
|
||||||
|
|
||||||
|
<PackageReference Include="Foursat.BackOffice.BFF.Category.Protobuf" Version="0.0.1" />
|
||||||
|
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.Otp.Protobuf" Version="0.0.111" />
|
<PackageReference Include="Foursat.BackOffice.BFF.Otp.Protobuf" Version="0.0.111" />
|
||||||
|
|
||||||
<PackageReference Include="FourSat.BackOffice.BFF.Package.Protobuf" Version="0.0.111" />
|
<PackageReference Include="FourSat.BackOffice.BFF.Package.Protobuf" Version="0.0.111" />
|
||||||
|
|
||||||
|
<PackageReference Include="Foursat.BackOffice.BFF.Products.Protobuf" Version="0.0.3" />
|
||||||
|
|
||||||
|
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.Role.Protobuf" Version="0.0.111" />
|
<PackageReference Include="Foursat.BackOffice.BFF.Role.Protobuf" Version="0.0.111" />
|
||||||
|
|
||||||
<PackageReference Include="Foursat.BackOffice.BFF.User.Protobuf" Version="0.0.111" />
|
<PackageReference Include="Foursat.BackOffice.BFF.User.Protobuf" Version="0.0.111" />
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
<MudStack Row="true" AlignItems="AlignItems.Center">
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
<MudText Typo="Typo.subtitle2">فیلتر ها</MudText>
|
<MudText Typo="Typo.subtitle2">فیلتر ها</MudText>
|
||||||
<MudSpacer />
|
<MudSpacer />
|
||||||
@if (IsFilterd)
|
@if (IsFiltered)
|
||||||
{
|
{
|
||||||
<MudButton Color="Color.Error" Variant="Variant.Text" Size="Size.Small" OnClick="@(async () => await OnClearFilterClick.InvokeAsync())">خذف فیلتر</MudButton>
|
<MudButton Color="Color.Error" Variant="Variant.Text" Size="Size.Small" OnClick="@(async () => await OnClearFilterClick.InvokeAsync())">حذف فیلتر</MudButton>
|
||||||
}
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
<MudItem Style="height:100%;">
|
<MudItem Style="height:100%;">
|
||||||
@@ -32,4 +32,3 @@
|
|||||||
</MudItem>
|
</MudItem>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,9 @@ using Microsoft.AspNetCore.Components;
|
|||||||
namespace BackOffice.Common.BaseComponents;
|
namespace BackOffice.Common.BaseComponents;
|
||||||
public partial class BasePageComponent
|
public partial class BasePageComponent
|
||||||
{
|
{
|
||||||
[Parameter] public RenderFragment Filters { get; set; }
|
[Parameter] public RenderFragment Filters { get; set; } = default!;
|
||||||
[Parameter] public RenderFragment Content { get; set; }
|
[Parameter] public RenderFragment Content { get; set; } = default!;
|
||||||
[Parameter] public bool IsFilterd { get; set; }
|
[Parameter] public bool IsFiltered { get; set; }
|
||||||
[Parameter] public EventCallback OnSubmitClick { get; set; }
|
[Parameter] public EventCallback OnSubmitClick { get; set; }
|
||||||
[Parameter] public EventCallback OnClearFilterClick { get; set; }
|
[Parameter] public EventCallback OnClearFilterClick { get; set; }
|
||||||
private bool _open = true;
|
|
||||||
|
|
||||||
private void ToggleDrawer()
|
|
||||||
{
|
|
||||||
_open = !_open;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -55,12 +55,17 @@ public partial class DateRangePicker
|
|||||||
}
|
}
|
||||||
private async Task OnClickOK()
|
private async Task OnClickOK()
|
||||||
{
|
{
|
||||||
_picker.CloseAsync();
|
if (_picker.DateRange is not null)
|
||||||
|
{
|
||||||
await OnChanged.InvokeAsync(_picker.DateRange);
|
await OnChanged.InvokeAsync(_picker.DateRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await _picker.CloseAsync();
|
||||||
|
}
|
||||||
private async Task OnClickClear()
|
private async Task OnClickClear()
|
||||||
{
|
{
|
||||||
_picker.CloseAsync();
|
_dateRange = new DateRange();
|
||||||
await OnChanged.InvokeAsync(_picker.DateRange);
|
await OnChanged.InvokeAsync(_dateRange);
|
||||||
|
await _picker.CloseAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,23 +2,22 @@
|
|||||||
using BackOffice.BFF.Otp.Protobuf.Protos.Otp;
|
using BackOffice.BFF.Otp.Protobuf.Protos.Otp;
|
||||||
using BackOffice.BFF.Package.Protobuf.Protos.Package;
|
using BackOffice.BFF.Package.Protobuf.Protos.Package;
|
||||||
using BackOffice.BFF.Role.Protobuf.Protos.Role;
|
using BackOffice.BFF.Role.Protobuf.Protos.Role;
|
||||||
|
using BackOffice.BFF.Products.Protobuf.Protos.Products;
|
||||||
using BackOffice.BFF.User.Protobuf.Protos.User;
|
using BackOffice.BFF.User.Protobuf.Protos.User;
|
||||||
using BackOffice.BFF.UserAddress.Protobuf.Protos.UserAddress;
|
using BackOffice.BFF.UserAddress.Protobuf.Protos.UserAddress;
|
||||||
using BackOffice.BFF.UserOrder.Protobuf.Protos.UserOrder;
|
using BackOffice.BFF.UserOrder.Protobuf.Protos.UserOrder;
|
||||||
using BackOffice.BFF.UserRole.Protobuf.Protos.UserRole;
|
using BackOffice.BFF.UserRole.Protobuf.Protos.UserRole;
|
||||||
|
using BackOffice.BFF.Category.Protobuf.Protos.Category;
|
||||||
using BackOffice.Common.Utilities;
|
using BackOffice.Common.Utilities;
|
||||||
using Blazored.LocalStorage;
|
using Blazored.LocalStorage;
|
||||||
using Google.Protobuf.Reflection;
|
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using Grpc.Core.Interceptors;
|
using Grpc.Core.Interceptors;
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
using Grpc.Net.Client.Web;
|
using Grpc.Net.Client.Web;
|
||||||
using Microsoft.AspNetCore.Components.Authorization;
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
|
|
||||||
using MudBlazor.Services;
|
using MudBlazor.Services;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using static MudBlazor.Colors;
|
|
||||||
|
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection;
|
namespace Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -48,25 +47,35 @@ public static class ConfigureServices
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static IServiceCollection AddGrpcServices(this IServiceCollection services, IConfiguration configuration) //
|
public static IServiceCollection AddGrpcServices(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
var baseUri = configuration["GwUrl"];
|
services.AddSingleton<CallInvoker>(sp =>
|
||||||
Console.WriteLine();
|
{
|
||||||
Console.WriteLine(baseUri);
|
var config = sp.GetRequiredService<IConfiguration>();
|
||||||
var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()));
|
var baseUri = config["GwUrl"];
|
||||||
httpClient.Timeout = TimeSpan.FromMinutes(10); // TODO Check Timeout
|
|
||||||
var serviceProvider = services.BuildServiceProvider();
|
|
||||||
var channel = CreateAuthenticatedChannel(baseUri, httpClient, serviceProvider);
|
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(baseUri))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Configuration value 'GwUrl' is missing or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()))
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromMinutes(10)
|
||||||
|
};
|
||||||
|
|
||||||
services.AddTransient(sp => new OtpContract.OtpContractClient(channel));
|
return CreateAuthenticatedChannel(baseUri, httpClient, sp);
|
||||||
services.AddTransient(sp => new PackageContract.PackageContractClient(channel));
|
});
|
||||||
services.AddTransient(sp => new RoleContract.RoleContractClient(channel));
|
|
||||||
services.AddTransient(sp => new UserContract.UserContractClient(channel));
|
services.AddTransient(sp => new OtpContract.OtpContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
services.AddTransient(sp => new UserAddressContract.UserAddressContractClient(channel));
|
services.AddTransient(sp => new PackageContract.PackageContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
services.AddTransient(sp => new UserOrderContract.UserOrderContractClient(channel));
|
services.AddTransient(sp => new ProductsContract.ProductsContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
services.AddTransient(sp => new UserRoleContract.UserRoleContractClient(channel));
|
services.AddTransient(sp => new RoleContract.RoleContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
|
services.AddTransient(sp => new UserContract.UserContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
|
services.AddTransient(sp => new UserAddressContract.UserAddressContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
|
services.AddTransient(sp => new UserOrderContract.UserOrderContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
|
services.AddTransient(sp => new UserRoleContract.UserRoleContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
|
services.AddTransient(sp => new CategoryContract.CategoryContractClient(sp.GetRequiredService<CallInvoker>()));
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@@ -98,7 +107,6 @@ public static class ConfigureServices
|
|||||||
MaxReceiveMessageSize = 1000 * 1024 * 1024, // 1 GB
|
MaxReceiveMessageSize = 1000 * 1024 * 1024, // 1 GB
|
||||||
MaxSendMessageSize = 1000 * 1024 * 1024 // 1 GB
|
MaxSendMessageSize = 1000 * 1024 * 1024 // 1 GB
|
||||||
});
|
});
|
||||||
var invoker = channel.Intercept(new ErrorHandlerInterceptor());
|
return channel.Intercept(new ErrorHandlerInterceptor());
|
||||||
return invoker;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Grpc.Core.Interceptors;
|
using Grpc.Core;
|
||||||
using Grpc.Core;
|
using Grpc.Core.Interceptors;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
namespace BackOffice.Common.Utilities;
|
namespace BackOffice.Common.Utilities;
|
||||||
|
|
||||||
@@ -28,7 +29,8 @@ public class ErrorHandlerInterceptor : Interceptor
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
GlobalConstants.ConstSnackbar.Add(ex.Message, severity: MudBlazor.Severity.Error);
|
var message = ex.Message.ExtractUserFriendlyMessage();
|
||||||
|
GlobalConstants.ConstSnackbar?.Add(message, Severity.Error);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,5 @@ public static class GlobalConstants
|
|||||||
public const string DateTimeFormat = "MMM dd, yyyy - HH:mm";
|
public const string DateTimeFormat = "MMM dd, yyyy - HH:mm";
|
||||||
public static string JwtTokenKey = "AuthToken";
|
public static string JwtTokenKey = "AuthToken";
|
||||||
public const string SuccessMsg = "با موفقیت انجام شد";
|
public const string SuccessMsg = "با موفقیت انجام شد";
|
||||||
public static ISnackbar ConstSnackbar;
|
public static ISnackbar? ConstSnackbar { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -11,4 +11,6 @@ public static class RouteConstance
|
|||||||
public const string UserOrder = "/UserOrderPage/";
|
public const string UserOrder = "/UserOrderPage/";
|
||||||
public const string UserRole = "/UserRolePage/";
|
public const string UserRole = "/UserRolePage/";
|
||||||
public const string UserAddress = "/UserAddressPage/";
|
public const string UserAddress = "/UserAddressPage/";
|
||||||
|
public const string Products = "/ProductsPage/";
|
||||||
|
public const string Category = "/CategoryPage/";
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/BackOffice/Pages/AutoComplete/ProductsAutoComplete.razor
Normal file
15
src/BackOffice/Pages/AutoComplete/ProductsAutoComplete.razor
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
@using BackOffice.BFF.Products.Protobuf.Protos.Products
|
||||||
|
|
||||||
|
<MudAutocomplete T="GetAllProductsByFilterResponseModel"
|
||||||
|
Label="@Label"
|
||||||
|
Value="@_item"
|
||||||
|
DebounceInterval="700"
|
||||||
|
ValueChanged="@((e) => OnSelected(e))"
|
||||||
|
ToStringFunc="@(e => e == null ? null : e.Title)"
|
||||||
|
SearchFunc="@Search"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Clearable="true"
|
||||||
|
ShowProgressIndicator="true"
|
||||||
|
CoerceText="false"
|
||||||
|
OnClearButtonClick="()=> OnSelected(null)" />
|
||||||
|
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
using BackOffice.BFF.Products.Protobuf.Protos.Products;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.AutoComplete;
|
||||||
|
|
||||||
|
public partial class ProductsAutoComplete
|
||||||
|
{
|
||||||
|
[Inject] public ProductsContract.ProductsContractClient ProductsService { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter] public long? Value { get; set; }
|
||||||
|
[Parameter] public string? Label { get; set; } = "محصول";
|
||||||
|
[Parameter] public EventCallback<long?> ValueChanged { get; set; }
|
||||||
|
|
||||||
|
private List<GetAllProductsByFilterResponseModel> _items = new();
|
||||||
|
private GetAllProductsByFilterResponseModel? _item;
|
||||||
|
|
||||||
|
protected override async void OnParametersSet()
|
||||||
|
{
|
||||||
|
await base.OnParametersSetAsync();
|
||||||
|
|
||||||
|
if (Value.HasValue && Value.Value > 0)
|
||||||
|
{
|
||||||
|
var getAll = await ProductsService.GetAllProductsByFilterAsync(new GetAllProductsByFilterRequest
|
||||||
|
{
|
||||||
|
Filter = new GetAllProductsByFilterFilter
|
||||||
|
{
|
||||||
|
Id = Value.Value
|
||||||
|
},
|
||||||
|
PaginationState = new PaginationState
|
||||||
|
{
|
||||||
|
PageNumber = 1,
|
||||||
|
PageSize = 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (getAll?.Models != null && getAll.Models.Any())
|
||||||
|
{
|
||||||
|
_item = getAll.Models.First();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnSelected(GetAllProductsByFilterResponseModel? model)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
_item = null;
|
||||||
|
await ValueChanged.InvokeAsync(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_item = model;
|
||||||
|
await ValueChanged.InvokeAsync(model.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IEnumerable<GetAllProductsByFilterResponseModel>> Search(string value, CancellationToken token)
|
||||||
|
{
|
||||||
|
var request = new GetAllProductsByFilterRequest
|
||||||
|
{
|
||||||
|
Filter = new GetAllProductsByFilterFilter
|
||||||
|
{
|
||||||
|
Title = string.IsNullOrWhiteSpace(value) ? null : value
|
||||||
|
},
|
||||||
|
PaginationState = new PaginationState
|
||||||
|
{
|
||||||
|
PageNumber = 1,
|
||||||
|
PageSize = 9
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var getAll = await ProductsService.GetAllProductsByFilterAsync(request);
|
||||||
|
_items = getAll?.Models?.ToList() ?? new List<GetAllProductsByFilterResponseModel>();
|
||||||
|
|
||||||
|
return _items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
83
src/BackOffice/Pages/Category/CategoryMainPage.razor
Normal file
83
src/BackOffice/Pages/Category/CategoryMainPage.razor
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
@attribute [Route(RouteConstance.Category)]
|
||||||
|
|
||||||
|
@using BackOffice.BFF.Category.Protobuf.Protos.Category
|
||||||
|
@using BackOffice.Common.BaseComponents
|
||||||
|
@using DataModel = BackOffice.BFF.Category.Protobuf.Protos.Category.GetAllCategoryByFilterResponseModel
|
||||||
|
|
||||||
|
<BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleared" OnSubmitClick="OnFilterSubmit">
|
||||||
|
<Filters>
|
||||||
|
<MudTextField T="string" Clearable="true" Label="عنوان" @bind-Value="@_request.Filter.Title" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
|
</Filters>
|
||||||
|
<Content>
|
||||||
|
<MudDataGrid T="DataModel"
|
||||||
|
ServerData="@(new Func<GridState<DataModel>, Task<GridData<DataModel>>>(ServerReload))"
|
||||||
|
Hover="true"
|
||||||
|
@ref="_gridData"
|
||||||
|
Height="72vh">
|
||||||
|
<ColGroup>
|
||||||
|
<col />
|
||||||
|
<col />
|
||||||
|
<col />
|
||||||
|
<col />
|
||||||
|
<col />
|
||||||
|
<col style="width: 80px;" />
|
||||||
|
</ColGroup>
|
||||||
|
<ToolBarContent>
|
||||||
|
<MudText>مدیریت دستهبندیها</MudText>
|
||||||
|
<MudSpacer />
|
||||||
|
<MudStack Spacing="2" Row="true" Justify="Justify.Center" AlignItems="AlignItems.Center">
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
Size="Size.Large"
|
||||||
|
ButtonType="ButtonType.Button"
|
||||||
|
OnClick="CreateNew"
|
||||||
|
Style="cursor:pointer;">
|
||||||
|
افزودن
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
</ToolBarContent>
|
||||||
|
<Columns>
|
||||||
|
<PropertyColumn Property="x => x.Id" Title="شناسه" />
|
||||||
|
<PropertyColumn Property="x => x.Name" Title="نام لاتین" />
|
||||||
|
<PropertyColumn Property="x => x.Title" Title="عنوان" />
|
||||||
|
<PropertyColumn Property="x => x.ParentId" Title="شناسه والد" />
|
||||||
|
<TemplateColumn Title="فعال؟">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudChip Color="@(context.Item.IsActive ? Color.Success : Color.Error)"
|
||||||
|
Variant="Variant.Filled"
|
||||||
|
Size="Size.Small">
|
||||||
|
@(context.Item.IsActive ? "فعال" : "غیرفعال")
|
||||||
|
</MudChip>
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn StickyLeft="true" Title="عملیات" CellStyle="text-wrap: nowrap;" HeaderStyle="text-wrap: nowrap;">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
|
<MudTooltip Text="ویرایش">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.EditNote"
|
||||||
|
Size="Size.Small"
|
||||||
|
ButtonType="ButtonType.Button"
|
||||||
|
OnClick="@(() => Update(context.Item))"
|
||||||
|
Style="cursor:pointer;" />
|
||||||
|
</MudTooltip>
|
||||||
|
|
||||||
|
<MudTooltip Text="حذف">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.DeleteOutline"
|
||||||
|
Size="Size.Small"
|
||||||
|
ButtonType="ButtonType.Button"
|
||||||
|
OnClick="@(() => OnDelete(context.Item))"
|
||||||
|
Style="cursor:pointer;" />
|
||||||
|
</MudTooltip>
|
||||||
|
</MudStack>
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
</Columns>
|
||||||
|
<PagerContent>
|
||||||
|
<MudDataGridPager T="DataModel"
|
||||||
|
PageSizeOptions="@(new int[] { 30, 60, 90 })"
|
||||||
|
InfoFormat="سطر {first_item} تا {last_item} از {all_items}"
|
||||||
|
RowsPerPageString="تعداد سطرهای صفحه" />
|
||||||
|
</PagerContent>
|
||||||
|
</MudDataGrid>
|
||||||
|
</Content>
|
||||||
|
</BasePageComponent>
|
||||||
120
src/BackOffice/Pages/Category/CategoryMainPage.razor.cs
Normal file
120
src/BackOffice/Pages/Category/CategoryMainPage.razor.cs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
using BackOffice.BFF.Category.Protobuf.Protos.Category;
|
||||||
|
using BackOffice.Common.BaseComponents;
|
||||||
|
using Mapster;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
using DataModel = BackOffice.BFF.Category.Protobuf.Protos.Category.GetAllCategoryByFilterResponseModel;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Category;
|
||||||
|
|
||||||
|
public partial class CategoryMainPage
|
||||||
|
{
|
||||||
|
[Inject] public CategoryContract.CategoryContractClient CategoryContract { get; set; } = default!;
|
||||||
|
|
||||||
|
private BasePageComponent _basePage = default!;
|
||||||
|
private MudDataGrid<DataModel> _gridData = default!;
|
||||||
|
private GetAllCategoryByFilterRequest _request = new() { Filter = new() };
|
||||||
|
|
||||||
|
private async Task<GridData<DataModel>> ServerReload(GridState<DataModel> state)
|
||||||
|
{
|
||||||
|
_request.Filter ??= new();
|
||||||
|
_request.PaginationState ??= new();
|
||||||
|
_request.PaginationState.PageNumber = state.Page + 1;
|
||||||
|
_request.PaginationState.PageSize = state.PageSize;
|
||||||
|
|
||||||
|
var result = await CategoryContract.GetAllCategoryByFilterAsync(_request);
|
||||||
|
if (result != null && result.Models != null && result.Models.Any())
|
||||||
|
{
|
||||||
|
return new GridData<DataModel>
|
||||||
|
{
|
||||||
|
Items = result.Models.ToList(),
|
||||||
|
TotalItems = (int)result.MetaData.TotalCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GridData<DataModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CreateNew()
|
||||||
|
{
|
||||||
|
var dialog = await DialogService.ShowAsync<CreateOrUpdateCategoryDialog>(
|
||||||
|
"افزودن دستهبندی",
|
||||||
|
new DialogParameters<CreateOrUpdateCategoryDialog>
|
||||||
|
{
|
||||||
|
{ x => x.Model, new CreateNewCategoryRequest { IsActive = true } }
|
||||||
|
},
|
||||||
|
new DialogOptions { CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Small });
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Canceled)
|
||||||
|
{
|
||||||
|
await ReloadData();
|
||||||
|
Snackbar.Add("دستهبندی با موفقیت ایجاد شد.", Severity.Success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Update(DataModel model)
|
||||||
|
{
|
||||||
|
var getResponse = await CategoryContract.GetCategoryAsync(new GetCategoryRequest { Id = model.Id });
|
||||||
|
|
||||||
|
var editModel = getResponse.Adapt<UpdateCategoryRequest>();
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<CreateOrUpdateCategoryDialog>(
|
||||||
|
"ویرایش دستهبندی",
|
||||||
|
new DialogParameters<CreateOrUpdateCategoryDialog>
|
||||||
|
{
|
||||||
|
{ x => x.EditModel, editModel }
|
||||||
|
},
|
||||||
|
new DialogOptions { CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Small });
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Canceled)
|
||||||
|
{
|
||||||
|
await ReloadData();
|
||||||
|
Snackbar.Add("دستهبندی با موفقیت ویرایش شد.", Severity.Success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnDelete(DataModel model)
|
||||||
|
{
|
||||||
|
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Small };
|
||||||
|
bool? result = await DialogService.ShowMessageBox(
|
||||||
|
"اخطار",
|
||||||
|
"آیا از حذف این دستهبندی مطمئن هستید؟",
|
||||||
|
yesText: "حذف",
|
||||||
|
cancelText: "لغو",
|
||||||
|
options: options);
|
||||||
|
|
||||||
|
if (result == true)
|
||||||
|
{
|
||||||
|
await CategoryContract.DeleteCategoryAsync(new DeleteCategoryRequest { Id = model.Id });
|
||||||
|
await ReloadData();
|
||||||
|
Snackbar.Add("دستهبندی با موفقیت حذف شد.", Severity.Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ReloadData()
|
||||||
|
{
|
||||||
|
if (_gridData != null)
|
||||||
|
{
|
||||||
|
await _gridData.ReloadServerData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnFilterSubmit()
|
||||||
|
{
|
||||||
|
_basePage.IsFiltered = true;
|
||||||
|
StateHasChanged();
|
||||||
|
await ReloadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnFilterCleared()
|
||||||
|
{
|
||||||
|
_basePage.IsFiltered = false;
|
||||||
|
StateHasChanged();
|
||||||
|
_request = new GetAllCategoryByFilterRequest { Filter = new GetAllCategoryByFilterFilter() };
|
||||||
|
await ReloadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
@using BackOffice.BFF.Category.Protobuf.Protos.Category
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudTextField T="string"
|
||||||
|
@bind-Value="Name"
|
||||||
|
Label="نام لاتین"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense" />
|
||||||
|
|
||||||
|
<MudTextField T="string"
|
||||||
|
@bind-Value="Title"
|
||||||
|
Label="عنوان"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense" />
|
||||||
|
|
||||||
|
<MudTextField T="string"
|
||||||
|
@bind-Value="Description"
|
||||||
|
Label="توضیحات"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
Lines="3"
|
||||||
|
TextArea="true" />
|
||||||
|
|
||||||
|
<MudNumericField T="long?"
|
||||||
|
@bind-Value="ParentId"
|
||||||
|
HideSpinButtons="true"
|
||||||
|
Label="شناسه والد (اختیاری)"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense" />
|
||||||
|
|
||||||
|
<MudNumericField T="int"
|
||||||
|
@bind-Value="SortOrder"
|
||||||
|
HideSpinButtons="true"
|
||||||
|
Label="ترتیب نمایش"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense" />
|
||||||
|
|
||||||
|
<MudCheckBox T="bool" @bind-Checked="IsActive" Label="فعال" />
|
||||||
|
</MudStack>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Cancel">لغو</MudButton>
|
||||||
|
<MudButton Color="Color.Primary" OnClick="Save">ثبت</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
using BackOffice.BFF.Category.Protobuf.Protos.Category;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Category;
|
||||||
|
|
||||||
|
public partial class CreateOrUpdateCategoryDialog
|
||||||
|
{
|
||||||
|
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
[Inject] public CategoryContract.CategoryContractClient CategoryContract { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter] public CreateNewCategoryRequest? Model { get; set; }
|
||||||
|
[Parameter] public UpdateCategoryRequest? EditModel { get; set; }
|
||||||
|
|
||||||
|
private bool IsEdit => EditModel != null;
|
||||||
|
|
||||||
|
private string Name { get; set; } = string.Empty;
|
||||||
|
private string Title { get; set; } = string.Empty;
|
||||||
|
private string? Description { get; set; }
|
||||||
|
private long? ParentId { get; set; }
|
||||||
|
private bool IsActive { get; set; } = true;
|
||||||
|
private int SortOrder { get; set; }
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (IsEdit && EditModel != null)
|
||||||
|
{
|
||||||
|
Name = EditModel.Name;
|
||||||
|
Title = EditModel.Title;
|
||||||
|
Description = EditModel.Description;
|
||||||
|
ParentId = EditModel.ParentId;
|
||||||
|
IsActive = EditModel.IsActive;
|
||||||
|
SortOrder = EditModel.SortOrder;
|
||||||
|
}
|
||||||
|
else if (Model != null)
|
||||||
|
{
|
||||||
|
Name = Model.Name;
|
||||||
|
Title = Model.Title;
|
||||||
|
Description = Model.Description;
|
||||||
|
ParentId = Model.ParentId;
|
||||||
|
IsActive = Model.IsActive;
|
||||||
|
SortOrder = Model.SortOrder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Save()
|
||||||
|
{
|
||||||
|
if (IsEdit && EditModel != null)
|
||||||
|
{
|
||||||
|
var request = new UpdateCategoryRequest
|
||||||
|
{
|
||||||
|
Id = EditModel.Id,
|
||||||
|
Name = Name,
|
||||||
|
Title = Title,
|
||||||
|
Description = Description,
|
||||||
|
ParentId = ParentId,
|
||||||
|
IsActive = IsActive,
|
||||||
|
SortOrder = SortOrder
|
||||||
|
};
|
||||||
|
|
||||||
|
await CategoryContract.UpdateCategoryAsync(request);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var request = new CreateNewCategoryRequest
|
||||||
|
{
|
||||||
|
Name = Name,
|
||||||
|
Title = Title,
|
||||||
|
Description = Description,
|
||||||
|
ParentId = ParentId,
|
||||||
|
IsActive = IsActive,
|
||||||
|
SortOrder = SortOrder
|
||||||
|
};
|
||||||
|
|
||||||
|
await CategoryContract.CreateNewCategoryAsync(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
MudDialog.Close(DialogResult.Ok(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel()
|
||||||
|
{
|
||||||
|
MudDialog.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -15,7 +15,6 @@ public partial class LoginPage
|
|||||||
[Inject] public OtpContract.OtpContractClient OtpContract { get; set; }
|
[Inject] public OtpContract.OtpContractClient OtpContract { get; set; }
|
||||||
private async Task OnSubmitClick()
|
private async Task OnSubmitClick()
|
||||||
{
|
{
|
||||||
Console.WriteLine(OtpContract == null);
|
|
||||||
await _form.Validate();
|
await _form.Validate();
|
||||||
if (!_form.IsValid)
|
if (!_form.IsValid)
|
||||||
return;
|
return;
|
||||||
@@ -31,7 +30,8 @@ public partial class LoginPage
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Snackbar.Add(message: ex.Message, severity: Severity.Error, null);
|
var message = ex.Message.ExtractUserFriendlyMessage();
|
||||||
|
Snackbar.Add(message: message, severity: Severity.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ public partial class VerifyCodePage
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Snackbar.Add(message: ex.Message, severity: Severity.Error, null);
|
var message = ex.Message.ExtractUserFriendlyMessage();
|
||||||
|
Snackbar.Add(message: message, severity: Severity.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
@@ -68,7 +69,8 @@ public partial class VerifyCodePage
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Snackbar.Add(message: ex.Message, severity: Severity.Error, null);
|
var message = ex.Message.ExtractUserFriendlyMessage();
|
||||||
|
Snackbar.Add(message: message, severity: Severity.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@
|
|||||||
@using BackOffice.Common.BaseComponents
|
@using BackOffice.Common.BaseComponents
|
||||||
@using DataModel = BackOffice.BFF.Package.Protobuf.Protos.Package.GetAllPackageByFilterResponseModel
|
@using DataModel = BackOffice.BFF.Package.Protobuf.Protos.Package.GetAllPackageByFilterResponseModel
|
||||||
|
|
||||||
<BackOffice.Common.BaseComponents.BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleard" OnSubmitClick="OnFilterSubmit">
|
<BackOffice.Common.BaseComponents.BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleared" OnSubmitClick="OnFilterSubmit">
|
||||||
<Filters>
|
<Filters>
|
||||||
<MudTextField T="string" Clearable="true" Label="عنوان" @bind-Value="@_request.Filter.Title" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
<MudTextField T="string" Clearable="true" Label="عنوان" @bind-Value="@_request.Filter.Title" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
</Filters>
|
</Filters>
|
||||||
@@ -93,4 +93,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -82,14 +82,14 @@ public partial class PackageMainPage
|
|||||||
|
|
||||||
public async Task OnFilterSubmit()
|
public async Task OnFilterSubmit()
|
||||||
{
|
{
|
||||||
_basePage.IsFilterd = true;
|
_basePage.IsFiltered = true;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnFilterCleard()
|
public async Task OnFilterCleared()
|
||||||
{
|
{
|
||||||
_basePage.IsFilterd = false;
|
_basePage.IsFiltered = false;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
_request = new() { Filter = new() { } };
|
_request = new() { Filter = new() { } };
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
|
|||||||
87
src/BackOffice/Pages/Products/Components/CreateDialog.razor
Normal file
87
src/BackOffice/Pages/Products/Components/CreateDialog.razor
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
@using BackOffice.BFF.Products.Protobuf.Protos.Products
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
@using Tizzani.MudBlazor.HtmlEditor
|
||||||
|
@using BackOffice.Common.BaseComponents
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudStack Spacing="3">
|
||||||
|
<MudStack Justify="Justify.Center" AlignItems="AlignItems.Center">
|
||||||
|
@if (!string.IsNullOrWhiteSpace(_mainImagePreview))
|
||||||
|
{
|
||||||
|
<Image Src="@(_mainImagePreview)" Width="220" Height="140" ObjectPosition="ObjectPosition.Center" ObjectFit="ObjectFit.Cover" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudPaper Class="d-flex align-center justify-center" Style="width:220px;height:140px;">
|
||||||
|
<MudText Typo="Typo.caption">تصویری انتخاب نشده است</MudText>
|
||||||
|
</MudPaper>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudFileUpload T="IBrowserFile" Accept="image/*" FilesChanged="OnMainImageSelected">
|
||||||
|
<ActivatorContent>
|
||||||
|
<MudButton HtmlTag="label"
|
||||||
|
Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
ButtonType="ButtonType.Button"
|
||||||
|
StartIcon="@Icons.Material.Filled.Image"
|
||||||
|
Style="cursor:pointer;">
|
||||||
|
انتخاب تصویر اصلی
|
||||||
|
</MudButton>
|
||||||
|
</ActivatorContent>
|
||||||
|
<SelectedTemplate>
|
||||||
|
@if (context != null)
|
||||||
|
{
|
||||||
|
<MudText Class="mt-2" Typo="Typo.subtitle2">
|
||||||
|
<MudTooltip Text="@context.Name" Inline="true">
|
||||||
|
@context.Name
|
||||||
|
</MudTooltip>
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudText Class="mt-2" Typo="Typo.subtitle2">فایلی انتخاب نشده</MudText>
|
||||||
|
}
|
||||||
|
</SelectedTemplate>
|
||||||
|
</MudFileUpload>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
|
<MudItem xs="6">
|
||||||
|
<MudTextField T="string" @bind-Value="Model.Title" Disabled="_isLoading" Label="عنوان" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="6">
|
||||||
|
<MudNumericField T="long" HideSpinButtons="true" @bind-Value="Model.Price" Format="N0" Immediate="true" Disabled="_isLoading" Label="قیمت" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
|
</MudItem>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudNumericField T="int" HideSpinButtons="true" @bind-Value="Model.Discount" Disabled="_isLoading" Label="تخفیف (%)" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
|
|
||||||
|
<MudTextField T="string" @bind-Value="Model.ShortInfomation" Disabled="_isLoading" Label="خلاصه" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
|
|
||||||
|
<MudHtmlEditor @bind-Html="Model.FullInformation">
|
||||||
|
<MudHtmlToolbarOptions InsertImage="false" />
|
||||||
|
</MudHtmlEditor>
|
||||||
|
|
||||||
|
<MudStack Spacing="1">
|
||||||
|
<MudText Typo="Typo.subtitle2">تصویر بندانگشتی</MudText>
|
||||||
|
<MudFileUpload T="IBrowserFile" Accept="image/*" FilesChanged="OnThumbnailSelected">
|
||||||
|
<ActivatorContent>
|
||||||
|
<MudButton HtmlTag="label"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Color="Color.Secondary"
|
||||||
|
ButtonType="ButtonType.Button"
|
||||||
|
StartIcon="@Icons.Material.Filled.Image"
|
||||||
|
Style="cursor:pointer;">
|
||||||
|
انتخاب تصویر بندانگشتی (اختیاری)
|
||||||
|
</MudButton>
|
||||||
|
</ActivatorContent>
|
||||||
|
</MudFileUpload>
|
||||||
|
</MudStack>
|
||||||
|
</MudStack>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Cancel" Disabled="_isLoading">لغو</MudButton>
|
||||||
|
<MudButton Color="Color.Primary" OnClick="CallCreateMethod" Disabled="_isLoading">ثبت</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
using BackOffice.BFF.Products.Protobuf.Protos.Products;
|
||||||
|
using Google.Protobuf;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Products.Components;
|
||||||
|
|
||||||
|
public partial class CreateDialog
|
||||||
|
{
|
||||||
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
[Inject] public ProductsContract.ProductsContractClient ProductsContract { get; set; } = default!;
|
||||||
|
[Parameter] public CreateNewProductsRequest Model { get; set; } = default!;
|
||||||
|
|
||||||
|
private IBrowserFile? _mainImageFile;
|
||||||
|
private IBrowserFile? _thumbnailImageFile;
|
||||||
|
private readonly long _maxAllowedSize = (1024 * 1024) * 5;
|
||||||
|
private bool _isLoading;
|
||||||
|
private string? _mainImagePreview;
|
||||||
|
|
||||||
|
private async Task OnMainImageSelected(IBrowserFile? file)
|
||||||
|
{
|
||||||
|
_mainImageFile = file;
|
||||||
|
if (file != null)
|
||||||
|
{
|
||||||
|
var buffer = new byte[file.Size];
|
||||||
|
await file.OpenReadStream(_maxAllowedSize).ReadAsync(buffer);
|
||||||
|
_mainImagePreview = $"data:{file.ContentType};base64," + Convert.ToBase64String(buffer);
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnThumbnailSelected(IBrowserFile? file)
|
||||||
|
{
|
||||||
|
_thumbnailImageFile = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void CallCreateMethod()
|
||||||
|
{
|
||||||
|
_isLoading = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(Model.Title))
|
||||||
|
{
|
||||||
|
Snackbar.Add("لطفا عنوان را وارد کنید!", Severity.Warning);
|
||||||
|
_isLoading = false;
|
||||||
|
StateHasChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_mainImageFile != null)
|
||||||
|
{
|
||||||
|
var buffer = new byte[_mainImageFile.Size];
|
||||||
|
await _mainImageFile.OpenReadStream(_maxAllowedSize).ReadAsync(buffer);
|
||||||
|
Model.ImageFile = new ImageFileModel
|
||||||
|
{
|
||||||
|
File = ByteString.CopyFrom(buffer),
|
||||||
|
Mime = _mainImageFile.ContentType,
|
||||||
|
FileName = Path.GetFileNameWithoutExtension(_mainImageFile.Name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_thumbnailImageFile != null)
|
||||||
|
{
|
||||||
|
var buffer = new byte[_thumbnailImageFile.Size];
|
||||||
|
await _thumbnailImageFile.OpenReadStream(_maxAllowedSize).ReadAsync(buffer);
|
||||||
|
Model.ThumbnailFile = new ImageFileModel
|
||||||
|
{
|
||||||
|
File = ByteString.CopyFrom(buffer),
|
||||||
|
Mime = _thumbnailImageFile.ContentType,
|
||||||
|
FileName = Path.GetFileNameWithoutExtension(_thumbnailImageFile.Name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ProductsContract.CreateNewProductsAsync(Model);
|
||||||
|
Submit();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Snackbar.Add("خطای سرور", Severity.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
_isLoading = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Submit() => MudDialog.Close(DialogResult.Ok(true));
|
||||||
|
void Cancel() => MudDialog.Cancel();
|
||||||
|
}
|
||||||
83
src/BackOffice/Pages/Products/Components/GalleryDialog.razor
Normal file
83
src/BackOffice/Pages/Products/Components/GalleryDialog.razor
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
@using BackOffice.BFF.Products.Protobuf.Protos.Products
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudStack Spacing="3">
|
||||||
|
<MudText Typo="Typo.h6">گالری تصاویر - @ProductTitle</MudText>
|
||||||
|
|
||||||
|
<MudGrid>
|
||||||
|
<MudItem xs="12" md="4">
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudTextField @bind-Value="_title"
|
||||||
|
Label="عنوان تصویر"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense" />
|
||||||
|
|
||||||
|
<MudFileUpload T="IBrowserFile" Accept="image/*" FilesChanged="OnImageSelected">
|
||||||
|
<ActivatorContent>
|
||||||
|
<MudButton HtmlTag="label"
|
||||||
|
Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
ButtonType="ButtonType.Button"
|
||||||
|
StartIcon="@Icons.Material.Filled.Image"
|
||||||
|
Style="cursor:pointer;">
|
||||||
|
انتخاب تصویر
|
||||||
|
</MudButton>
|
||||||
|
</ActivatorContent>
|
||||||
|
<SelectedTemplate>
|
||||||
|
@if (context != null)
|
||||||
|
{
|
||||||
|
<MudText Class="mt-1" Typo="Typo.caption">@context.Name</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudText Class="mt-1" Typo="Typo.caption">فایلی انتخاب نشده</MudText>
|
||||||
|
}
|
||||||
|
</SelectedTemplate>
|
||||||
|
</MudFileUpload>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(_previewImage))
|
||||||
|
{
|
||||||
|
<MudPaper Class="pa-1">
|
||||||
|
<img src="@_previewImage" alt="پیشنمایش" style="width:100%; height:160px; object-fit:cover;" />
|
||||||
|
</MudPaper>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudButton Color="Color.Primary" OnClick="AddImage" Disabled="_isUploading">افزودن به گالری</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
</MudItem>
|
||||||
|
|
||||||
|
<MudItem xs="12" md="8">
|
||||||
|
@if (Items?.Count > 0)
|
||||||
|
{
|
||||||
|
<MudGrid>
|
||||||
|
@foreach (var item in Items)
|
||||||
|
{
|
||||||
|
<MudItem xs="6" sm="4" md="3">
|
||||||
|
<MudPaper Class="pa-2">
|
||||||
|
<img src="@item.ImageThumbnailPath" alt="@item.Title" style="width:100%; height:120px; object-fit:cover;" />
|
||||||
|
<MudText Typo="Typo.caption" Class="mt-1">@item.Title</MudText>
|
||||||
|
<MudStack Row="true" Justify="Justify.FlexEnd">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.DeleteOutline"
|
||||||
|
Color="Color.Error"
|
||||||
|
Size="Size.Small"
|
||||||
|
OnClick="@(() => RemoveImage(item))" />
|
||||||
|
</MudStack>
|
||||||
|
</MudPaper>
|
||||||
|
</MudItem>
|
||||||
|
}
|
||||||
|
</MudGrid>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption">هنوز تصویری برای این محصول ثبت نشده است.</MudText>
|
||||||
|
}
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
</MudStack>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Close">بستن</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
129
src/BackOffice/Pages/Products/Components/GalleryDialog.razor.cs
Normal file
129
src/BackOffice/Pages/Products/Components/GalleryDialog.razor.cs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
using BackOffice.BFF.Products.Protobuf.Protos.Products;
|
||||||
|
using Google.Protobuf;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Products.Components;
|
||||||
|
|
||||||
|
public partial class GalleryDialog
|
||||||
|
{
|
||||||
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
[Inject] public ProductsContract.ProductsContractClient ProductsContract { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter] public long ProductId { get; set; }
|
||||||
|
[Parameter] public string ProductTitle { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
private List<GalleryItemViewModel> Items { get; set; } = new List<GalleryItemViewModel>();
|
||||||
|
private IBrowserFile? _file;
|
||||||
|
private string? _title;
|
||||||
|
private readonly long _maxAllowedSize = (1024 * 1024) * 5;
|
||||||
|
private bool _isUploading;
|
||||||
|
private string? _previewImage;
|
||||||
|
private byte[]? _imageBuffer;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_title))
|
||||||
|
_title = ProductTitle;
|
||||||
|
|
||||||
|
await LoadGallery();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadGallery()
|
||||||
|
{
|
||||||
|
var response = await ProductsContract.GetProductGalleryAsync(new GetProductGalleryRequest
|
||||||
|
{
|
||||||
|
ProductId = ProductId
|
||||||
|
});
|
||||||
|
|
||||||
|
Items = response?.Items?
|
||||||
|
.Select(x => new GalleryItemViewModel
|
||||||
|
{
|
||||||
|
ProductGalleryId = x.ProductGalleryId,
|
||||||
|
ProductImageId = x.ProductImageId,
|
||||||
|
Title = x.Title,
|
||||||
|
ImagePath = x.ImagePath,
|
||||||
|
ImageThumbnailPath = x.ImageThumbnailPath
|
||||||
|
})
|
||||||
|
.ToList() ?? new List<GalleryItemViewModel>();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnImageSelected(IBrowserFile? file)
|
||||||
|
{
|
||||||
|
_file = file;
|
||||||
|
_previewImage = null;
|
||||||
|
_imageBuffer = null;
|
||||||
|
|
||||||
|
if (file != null)
|
||||||
|
{
|
||||||
|
var buffer = new byte[file.Size];
|
||||||
|
await file.OpenReadStream(_maxAllowedSize).ReadAsync(buffer);
|
||||||
|
_imageBuffer = buffer;
|
||||||
|
_previewImage = $"data:{file.ContentType};base64," + Convert.ToBase64String(buffer);
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddImage()
|
||||||
|
{
|
||||||
|
if (_file == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isUploading = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
if (_imageBuffer == null)
|
||||||
|
{
|
||||||
|
var buffer = new byte[_file.Size];
|
||||||
|
await _file.OpenReadStream(_maxAllowedSize).ReadAsync(buffer);
|
||||||
|
_imageBuffer = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new AddProductImageRequest
|
||||||
|
{
|
||||||
|
ProductId = ProductId,
|
||||||
|
Title = _title ?? string.Empty,
|
||||||
|
ImageFile = new ImageFileModel
|
||||||
|
{
|
||||||
|
File = ByteString.CopyFrom(_imageBuffer),
|
||||||
|
Mime = _file.ContentType,
|
||||||
|
FileName = Path.GetFileNameWithoutExtension(_file.Name)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await ProductsContract.AddProductImageAsync(request);
|
||||||
|
|
||||||
|
_file = null;
|
||||||
|
_title = string.Empty;
|
||||||
|
_previewImage = null;
|
||||||
|
_imageBuffer = null;
|
||||||
|
|
||||||
|
await LoadGallery();
|
||||||
|
|
||||||
|
_isUploading = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RemoveImage(GalleryItemViewModel item)
|
||||||
|
{
|
||||||
|
await ProductsContract.RemoveProductImageAsync(new RemoveProductImageRequest
|
||||||
|
{
|
||||||
|
ProductGalleryId = item.ProductGalleryId
|
||||||
|
});
|
||||||
|
|
||||||
|
await LoadGallery();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Close() => MudDialog.Close();
|
||||||
|
|
||||||
|
private class GalleryItemViewModel
|
||||||
|
{
|
||||||
|
public long ProductGalleryId { get; set; }
|
||||||
|
public long ProductImageId { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string ImagePath { get; set; }
|
||||||
|
public string ImageThumbnailPath { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
@using BackOffice.Common.BaseComponents
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudStack Spacing="2" AlignItems="AlignItems.Center">
|
||||||
|
<MudText Typo="Typo.h6">@Title</MudText>
|
||||||
|
@if (!string.IsNullOrWhiteSpace(ImageUrl))
|
||||||
|
{
|
||||||
|
<Image Src="@ImageUrl"
|
||||||
|
Width="400"
|
||||||
|
Height="260"
|
||||||
|
ObjectPosition="ObjectPosition.Center"
|
||||||
|
ObjectFit="ObjectFit.Cover" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption">تصویری برای نمایش وجود ندارد.</MudText>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Close">بستن</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Products.Components;
|
||||||
|
|
||||||
|
public partial class ImagePreviewDialog
|
||||||
|
{
|
||||||
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter] public string ImageUrl { get; set; } = string.Empty;
|
||||||
|
[Parameter] public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
private void Close() => MudDialog.Close();
|
||||||
|
}
|
||||||
|
|
||||||
73
src/BackOffice/Pages/Products/Components/UpdateDialog.razor
Normal file
73
src/BackOffice/Pages/Products/Components/UpdateDialog.razor
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
@using BackOffice.BFF.Products.Protobuf.Protos.Products
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
@using Tizzani.MudBlazor.HtmlEditor
|
||||||
|
@using BackOffice.Common.BaseComponents
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudStack Spacing="3">
|
||||||
|
<MudStack Justify="Justify.Center" AlignItems="AlignItems.Center">
|
||||||
|
@if (!string.IsNullOrWhiteSpace(_mainImagePreview))
|
||||||
|
{
|
||||||
|
<Image Src="@(_mainImagePreview)" Width="220" Height="140" ObjectPosition="ObjectPosition.Center" ObjectFit="ObjectFit.Cover" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudPaper Class="d-flex align-center justify-center" Style="width:220px;height:140px;">
|
||||||
|
<MudText Typo="Typo.caption">تصویری انتخاب نشده است</MudText>
|
||||||
|
</MudPaper>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudFileUpload T="IBrowserFile" Accept="image/*" FilesChanged="OnMainImageSelected">
|
||||||
|
<ActivatorContent>
|
||||||
|
<MudButton HtmlTag="label"
|
||||||
|
Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
ButtonType="ButtonType.Button"
|
||||||
|
StartIcon="@Icons.Material.Filled.Image"
|
||||||
|
Style="cursor:pointer;">
|
||||||
|
تغییر تصویر اصلی
|
||||||
|
</MudButton>
|
||||||
|
</ActivatorContent>
|
||||||
|
</MudFileUpload>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
|
<MudItem xs="6">
|
||||||
|
<MudTextField T="string" @bind-Value="Model.Title" Disabled="_isLoading" Label="عنوان" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="6">
|
||||||
|
<MudNumericField T="long" HideSpinButtons="true" @bind-Value="Model.Price" Format="N0" Immediate="true" Disabled="_isLoading" Label="قیمت" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
|
</MudItem>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudNumericField T="int" HideSpinButtons="true" @bind-Value="Model.Discount" Disabled="_isLoading" Label="تخفیف (%)" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
|
|
||||||
|
<MudTextField T="string" @bind-Value="Model.ShortInfomation" Disabled="_isLoading" Label="خلاصه" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
|
|
||||||
|
<MudHtmlEditor @bind-Html="Model.FullInformation">
|
||||||
|
<MudHtmlToolbarOptions InsertImage="false" />
|
||||||
|
</MudHtmlEditor>
|
||||||
|
|
||||||
|
<MudStack Spacing="1">
|
||||||
|
<MudText Typo="Typo.subtitle2">تصویر بندانگشتی</MudText>
|
||||||
|
<MudFileUpload T="IBrowserFile" Accept="image/*" FilesChanged="OnThumbnailSelected">
|
||||||
|
<ActivatorContent>
|
||||||
|
<MudButton HtmlTag="label"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Color="Color.Secondary"
|
||||||
|
ButtonType="ButtonType.Button"
|
||||||
|
StartIcon="@Icons.Material.Filled.Image"
|
||||||
|
Style="cursor:pointer;">
|
||||||
|
تغییر تصویر بندانگشتی (اختیاری)
|
||||||
|
</MudButton>
|
||||||
|
</ActivatorContent>
|
||||||
|
</MudFileUpload>
|
||||||
|
</MudStack>
|
||||||
|
</MudStack>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Cancel" Disabled="_isLoading">لغو</MudButton>
|
||||||
|
<MudButton Color="Color.Primary" OnClick="@CallUpdateMethod" Disabled="_isLoading">ویرایش</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
using BackOffice.BFF.Products.Protobuf.Protos.Products;
|
||||||
|
using Google.Protobuf;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Products.Components;
|
||||||
|
|
||||||
|
public partial class UpdateDialog
|
||||||
|
{
|
||||||
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
[Inject] public ProductsContract.ProductsContractClient ProductsContract { get; set; } = default!;
|
||||||
|
[Parameter] public UpdateProductsRequest Model { get; set; } = default!;
|
||||||
|
|
||||||
|
private IBrowserFile? _mainImageFile;
|
||||||
|
private IBrowserFile? _thumbnailImageFile;
|
||||||
|
private readonly long _maxAllowedSize = (1024 * 1024) * 5;
|
||||||
|
private bool _isLoading;
|
||||||
|
private string? _mainImagePreview;
|
||||||
|
|
||||||
|
private async Task OnMainImageSelected(IBrowserFile? file)
|
||||||
|
{
|
||||||
|
_mainImageFile = file;
|
||||||
|
if (file != null)
|
||||||
|
{
|
||||||
|
var buffer = new byte[file.Size];
|
||||||
|
await file.OpenReadStream(_maxAllowedSize).ReadAsync(buffer);
|
||||||
|
_mainImagePreview = $"data:{file.ContentType};base64," + Convert.ToBase64String(buffer);
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnThumbnailSelected(IBrowserFile? file)
|
||||||
|
{
|
||||||
|
_thumbnailImageFile = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
_mainImagePreview = Model.ImagePath;
|
||||||
|
return base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void CallUpdateMethod()
|
||||||
|
{
|
||||||
|
_isLoading = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(Model.Title))
|
||||||
|
{
|
||||||
|
Snackbar.Add("لطفا عنوان را وارد کنید!", Severity.Warning);
|
||||||
|
_isLoading = false;
|
||||||
|
StateHasChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_mainImageFile != null)
|
||||||
|
{
|
||||||
|
var buffer = new byte[_mainImageFile.Size];
|
||||||
|
await _mainImageFile.OpenReadStream(_maxAllowedSize).ReadAsync(buffer);
|
||||||
|
Model.ImageFile = new ImageFileModel
|
||||||
|
{
|
||||||
|
File = ByteString.CopyFrom(buffer),
|
||||||
|
Mime = _mainImageFile.ContentType,
|
||||||
|
FileName = Path.GetFileNameWithoutExtension(_mainImageFile.Name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_thumbnailImageFile != null)
|
||||||
|
{
|
||||||
|
var buffer = new byte[_thumbnailImageFile.Size];
|
||||||
|
await _thumbnailImageFile.OpenReadStream(_maxAllowedSize).ReadAsync(buffer);
|
||||||
|
Model.ThumbnailFile = new ImageFileModel
|
||||||
|
{
|
||||||
|
File = ByteString.CopyFrom(buffer),
|
||||||
|
Mime = _thumbnailImageFile.ContentType,
|
||||||
|
FileName = Path.GetFileNameWithoutExtension(_thumbnailImageFile.Name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ProductsContract.UpdateProductsAsync(Model);
|
||||||
|
Submit();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Snackbar.Add("خطای سرور", Severity.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
_isLoading = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Submit() => MudDialog.Close(DialogResult.Ok(true));
|
||||||
|
void Cancel() => MudDialog.Cancel();
|
||||||
|
}
|
||||||
85
src/BackOffice/Pages/Products/ProductsMainPage.razor
Normal file
85
src/BackOffice/Pages/Products/ProductsMainPage.razor
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
@attribute [Route(RouteConstance.Products)]
|
||||||
|
|
||||||
|
@using BackOffice.BFF.Products.Protobuf.Protos.Products
|
||||||
|
@using BackOffice.Common.BaseComponents
|
||||||
|
@using BackOffice.Pages.Products.Components
|
||||||
|
@using DataModel = BackOffice.BFF.Products.Protobuf.Protos.Products.GetAllProductsByFilterResponseModel
|
||||||
|
|
||||||
|
<BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleared" OnSubmitClick="OnFilterSubmit">
|
||||||
|
<Filters>
|
||||||
|
<MudTextField T="string" Clearable="true" Label="عنوان" @bind-Value="@_request.Filter.Title" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
|
</Filters>
|
||||||
|
<Content>
|
||||||
|
<MudDataGrid T="DataModel" ServerData="@(new Func<GridState<DataModel>, Task<GridData<DataModel>>>(ServerReload))"
|
||||||
|
Hover="true" @ref="_gridData" Height="72vh">
|
||||||
|
<ColGroup>
|
||||||
|
<col />
|
||||||
|
<col />
|
||||||
|
<col />
|
||||||
|
<col style="width: 58px;" />
|
||||||
|
</ColGroup>
|
||||||
|
<ToolBarContent>
|
||||||
|
<MudText>مدیریت محصولات</MudText>
|
||||||
|
<MudSpacer />
|
||||||
|
<MudStack Spacing="2" Row="true" Justify="Justify.Center" AlignItems="AlignItems.Center">
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" Size="Size.Large" ButtonType="ButtonType.Button" OnClick="CreateNew" Style="cursor:pointer;">افزودن</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
</ToolBarContent>
|
||||||
|
<Columns>
|
||||||
|
<PropertyColumn Property="x => x.Id" Title="شناسه" />
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.Title" Title="عنوان" CellStyle="text-wrap: nowrap;" HeaderStyle="text-wrap: nowrap;">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
|
<MudTooltip Text="پیشنمایش تصویر">
|
||||||
|
<div style="cursor:pointer"
|
||||||
|
@onclick="@(() => OpenImagePreview(context.Item.ImagePath, context.Item.Title))">
|
||||||
|
<Image Src="@context.Item.ImagePath"
|
||||||
|
Width="25"
|
||||||
|
Height="25"
|
||||||
|
ObjectPosition="ObjectPosition.Center"
|
||||||
|
ObjectFit="ObjectFit.Fill" />
|
||||||
|
</div>
|
||||||
|
</MudTooltip>
|
||||||
|
@if (string.IsNullOrWhiteSpace(context.Item.Title))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.inherit">-</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudTooltip Text="@(context.Item.Title)" Arrow="true">
|
||||||
|
<MudText Typo="Typo.inherit" Style="text-wrap: nowrap;">
|
||||||
|
@(context.Item.Title.Truncate(20, true))
|
||||||
|
</MudText>
|
||||||
|
</MudTooltip>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
|
||||||
|
<PropertyColumn Property="x => x.Price" Title="قیمت" />
|
||||||
|
|
||||||
|
<TemplateColumn StickyLeft="true" Title="عملیات" CellStyle="text-wrap: nowrap;" HeaderStyle="text-wrap: nowrap;">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
|
<MudTooltip Text="ویرایش">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.EditNote" Size="Size.Small" ButtonType="ButtonType.Button" OnClick="@(() => Update(context.Item))" Style="cursor:pointer;" />
|
||||||
|
</MudTooltip>
|
||||||
|
|
||||||
|
<MudTooltip Text="حذف">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.DeleteOutline" Size="Size.Small" ButtonType="ButtonType.Button" OnClick="@(() => OnDelete(context.Item))" Style="cursor:pointer;" />
|
||||||
|
</MudTooltip>
|
||||||
|
|
||||||
|
<MudTooltip Text="گالری تصاویر">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Collections" Size="Size.Small" ButtonType="ButtonType.Button" OnClick="@(() => OpenGallery(context.Item))" Style="cursor:pointer;" />
|
||||||
|
</MudTooltip>
|
||||||
|
</MudStack>
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
</Columns>
|
||||||
|
<PagerContent>
|
||||||
|
<MudDataGridPager T="DataModel" PageSizeOptions=@(new int[] { 30, 60, 90 }) />
|
||||||
|
</PagerContent>
|
||||||
|
</MudDataGrid>
|
||||||
|
</Content>
|
||||||
|
</BasePageComponent>
|
||||||
123
src/BackOffice/Pages/Products/ProductsMainPage.razor.cs
Normal file
123
src/BackOffice/Pages/Products/ProductsMainPage.razor.cs
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
using BackOffice.BFF.Products.Protobuf.Protos.Products;
|
||||||
|
using BackOffice.Common.BaseComponents;
|
||||||
|
using BackOffice.Pages.Products.Components;
|
||||||
|
using Mapster;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
using DataModel = BackOffice.BFF.Products.Protobuf.Protos.Products.GetAllProductsByFilterResponseModel;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Products;
|
||||||
|
|
||||||
|
public partial class ProductsMainPage
|
||||||
|
{
|
||||||
|
[Inject] public ProductsContract.ProductsContractClient ProductsContract { get; set; } = default!;
|
||||||
|
|
||||||
|
private bool _isLoading = true;
|
||||||
|
private MudDataGrid<DataModel> _gridData;
|
||||||
|
private BasePageComponent _basePage;
|
||||||
|
private GetAllProductsByFilterRequest _request = new() { Filter = new() };
|
||||||
|
|
||||||
|
private async Task<GridData<DataModel>> ServerReload(GridState<DataModel> state)
|
||||||
|
{
|
||||||
|
_request.Filter ??= new();
|
||||||
|
_request.PaginationState ??= new();
|
||||||
|
_request.PaginationState.PageNumber = state.Page + 1;
|
||||||
|
_request.PaginationState.PageSize = state.PageSize;
|
||||||
|
|
||||||
|
var result = await ProductsContract.GetAllProductsByFilterAsync(_request);
|
||||||
|
if (result != null && result.Models != null && result.Models.Any())
|
||||||
|
{
|
||||||
|
return new GridData<DataModel> { Items = result.Models.ToList(), TotalItems = (int)result.MetaData.TotalCount };
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GridData<DataModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Update(DataModel model)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters<UpdateDialog> { { x => x.Model, model.Adapt<UpdateProductsRequest>() } };
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<UpdateDialog>("ویرایش محصول", parameters, new DialogOptions { CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Small });
|
||||||
|
var result = await dialog.Result;
|
||||||
|
|
||||||
|
if (!result.Canceled)
|
||||||
|
{
|
||||||
|
ReLoadData();
|
||||||
|
Snackbar.Add("عملیات با موفقیت انجام شد", Severity.Success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnDelete(DataModel model)
|
||||||
|
{
|
||||||
|
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Small };
|
||||||
|
bool? result = await DialogService.ShowMessageBox(
|
||||||
|
"اخطار",
|
||||||
|
"آیا از حذف این مورد مطمئن هستید؟",
|
||||||
|
yesText: "حذف", cancelText: "لغو",
|
||||||
|
options: options);
|
||||||
|
if (result == true)
|
||||||
|
{
|
||||||
|
await ProductsContract.DeleteProductsAsync(new DeleteProductsRequest { Id = model.Id });
|
||||||
|
ReLoadData();
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void ReLoadData()
|
||||||
|
{
|
||||||
|
if (_gridData != null)
|
||||||
|
await _gridData.ReloadServerData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CreateNew()
|
||||||
|
{
|
||||||
|
var dialog = await DialogService.ShowAsync<CreateDialog>("افزودن محصول", new DialogParameters<CreateDialog> { { x => x.Model, new CreateNewProductsRequest() } }, new DialogOptions { CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Small });
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Canceled)
|
||||||
|
{
|
||||||
|
ReLoadData();
|
||||||
|
Snackbar.Add("عملیات با موفقیت انجام شد", Severity.Success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnFilterSubmit()
|
||||||
|
{
|
||||||
|
_basePage.IsFiltered = true;
|
||||||
|
StateHasChanged();
|
||||||
|
ReLoadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnFilterCleared()
|
||||||
|
{
|
||||||
|
_basePage.IsFiltered = false;
|
||||||
|
StateHasChanged();
|
||||||
|
_request = new GetAllProductsByFilterRequest { Filter = new() };
|
||||||
|
ReLoadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OpenGallery(DataModel model)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters<GalleryDialog>
|
||||||
|
{
|
||||||
|
{ x => x.ProductId, model.Id },
|
||||||
|
{ x => x.ProductTitle, model.Title }
|
||||||
|
};
|
||||||
|
await DialogService.ShowAsync<GalleryDialog>("گالری تصاویر", parameters,
|
||||||
|
new DialogOptions { CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Medium });
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OpenImagePreview(string imagePath, string title)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(imagePath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var parameters = new DialogParameters<ImagePreviewDialog>
|
||||||
|
{
|
||||||
|
{ x => x.ImageUrl, imagePath },
|
||||||
|
{ x => x.Title, title }
|
||||||
|
};
|
||||||
|
|
||||||
|
await DialogService.ShowAsync<ImagePreviewDialog>("پیشنمایش تصویر محصول", parameters,
|
||||||
|
new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Medium, FullWidth = true });
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -15,7 +15,7 @@ namespace BackOffice.Pages.Role.Components
|
|||||||
public UpdateRoleRequest Model { get; set; } = new();
|
public UpdateRoleRequest Model { get; set; } = new();
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
MudDialogInstance MudDialog { get; set; }
|
IMudDialogInstance MudDialog { get; set; }
|
||||||
|
|
||||||
public async void CallUpdateMethod()
|
public async void CallUpdateMethod()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
@using BackOffice.Common.BaseComponents
|
@using BackOffice.Common.BaseComponents
|
||||||
@using DataModel = BackOffice.BFF.Role.Protobuf.Protos.Role.GetAllRoleByFilterResponseModel
|
@using DataModel = BackOffice.BFF.Role.Protobuf.Protos.Role.GetAllRoleByFilterResponseModel
|
||||||
|
|
||||||
<BackOffice.Common.BaseComponents.BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleard" OnSubmitClick="OnFilterSubmit">
|
<BackOffice.Common.BaseComponents.BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleared" OnSubmitClick="OnFilterSubmit">
|
||||||
<Filters>
|
<Filters>
|
||||||
<MudTextField T="string" Clearable="true" Label="عنوان" @bind-Value="@_request.Filter.Title" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
<MudTextField T="string" Clearable="true" Label="عنوان" @bind-Value="@_request.Filter.Title" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
</Filters>
|
</Filters>
|
||||||
@@ -57,4 +57,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -81,14 +81,14 @@ public partial class RoleMainPage
|
|||||||
}
|
}
|
||||||
public async Task OnFilterSubmit()
|
public async Task OnFilterSubmit()
|
||||||
{
|
{
|
||||||
_basePage.IsFilterd = true;
|
_basePage.IsFiltered = true;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnFilterCleard()
|
public async Task OnFilterCleared()
|
||||||
{
|
{
|
||||||
_basePage.IsFilterd = false;
|
_basePage.IsFiltered = false;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
_request = new() { Filter = new() { } };
|
_request = new() { Filter = new() { } };
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace BackOffice.Pages.User.Components;
|
|||||||
public partial class UserRoleDialog
|
public partial class UserRoleDialog
|
||||||
{
|
{
|
||||||
[Parameter] public long UserId { get; set; }
|
[Parameter] public long UserId { get; set; }
|
||||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; }
|
||||||
MudForm _form;
|
MudForm _form;
|
||||||
private bool _isLoading;
|
private bool _isLoading;
|
||||||
private bool _isFirstLoad = true;
|
private bool _isFirstLoad = true;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
@using BackOffice.Common.BaseComponents
|
@using BackOffice.Common.BaseComponents
|
||||||
@using DataModel = BackOffice.BFF.User.Protobuf.Protos.User.GetAllUserByFilterResponseModel
|
@using DataModel = BackOffice.BFF.User.Protobuf.Protos.User.GetAllUserByFilterResponseModel
|
||||||
|
|
||||||
<BackOffice.Common.BaseComponents.BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleard" OnSubmitClick="OnFilterSubmit">
|
<BackOffice.Common.BaseComponents.BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleared" OnSubmitClick="OnFilterSubmit">
|
||||||
<Filters>
|
<Filters>
|
||||||
<MudTextField T="string" Clearable="true" Label="نام" @bind-Value="@_request.Filter.FirstName" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
<MudTextField T="string" Clearable="true" Label="نام" @bind-Value="@_request.Filter.FirstName" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
<MudTextField T="string" Clearable="true" Label="نام خانوادگی" @bind-Value="@_request.Filter.LastName" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
<MudTextField T="string" Clearable="true" Label="نام خانوادگی" @bind-Value="@_request.Filter.LastName" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
@@ -64,4 +64,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -51,30 +51,6 @@ public partial class UserMainPage
|
|||||||
if (_gridData != null)
|
if (_gridData != null)
|
||||||
await _gridData.ReloadServerData();
|
await _gridData.ReloadServerData();
|
||||||
}
|
}
|
||||||
private async Task Searchfirstname(string? firstname)
|
|
||||||
{
|
|
||||||
_request.Filter ??= new();
|
|
||||||
_request.Filter.FirstName = string.IsNullOrWhiteSpace(firstname) ? default : firstname;
|
|
||||||
ReLoadData();
|
|
||||||
}
|
|
||||||
private async Task Searchlastname(string? lastname)
|
|
||||||
{
|
|
||||||
_request.Filter ??= new();
|
|
||||||
_request.Filter.LastName = string.IsNullOrWhiteSpace(lastname) ? default : lastname;
|
|
||||||
ReLoadData();
|
|
||||||
}
|
|
||||||
private async Task SearchNationalCode(string? nationalcode)
|
|
||||||
{
|
|
||||||
_request.Filter ??= new();
|
|
||||||
_request.Filter.NationalCode = string.IsNullOrWhiteSpace(nationalcode) ? default : nationalcode;
|
|
||||||
ReLoadData();
|
|
||||||
}
|
|
||||||
private async Task SearchMobile(string? mobile)
|
|
||||||
{
|
|
||||||
_request.Filter ??= new();
|
|
||||||
_request.Filter.Mobile = string.IsNullOrWhiteSpace(mobile) ? default : mobile;
|
|
||||||
ReLoadData();
|
|
||||||
}
|
|
||||||
public async Task OnclickUserRoleDialog(long userid)
|
public async Task OnclickUserRoleDialog(long userid)
|
||||||
{
|
{
|
||||||
var dialog = await DialogService.ShowAsync<UserRoleDialog>($"نقش های کاربر", new DialogParameters<UserRoleDialog>() { { x => x.UserId, userid } }, new DialogOptions() { CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Small });
|
var dialog = await DialogService.ShowAsync<UserRoleDialog>($"نقش های کاربر", new DialogParameters<UserRoleDialog>() { { x => x.UserId, userid } }, new DialogOptions() { CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Small });
|
||||||
@@ -87,14 +63,14 @@ public partial class UserMainPage
|
|||||||
}
|
}
|
||||||
public async Task OnFilterSubmit()
|
public async Task OnFilterSubmit()
|
||||||
{
|
{
|
||||||
_basePage.IsFilterd = true;
|
_basePage.IsFiltered = true;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnFilterCleard()
|
public async Task OnFilterCleared()
|
||||||
{
|
{
|
||||||
_basePage.IsFilterd = false;
|
_basePage.IsFiltered = false;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
_request = new() { Filter = new() { } };
|
_request = new() { Filter = new() { } };
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public partial class CreateDialog
|
|||||||
{
|
{
|
||||||
[Inject] public UserAddressContract.UserAddressContractClient UserAddressContract { get; set; }
|
[Inject] public UserAddressContract.UserAddressContractClient UserAddressContract { get; set; }
|
||||||
[Parameter] public CreateNewUserAddressRequest Model { get; set; } = new();
|
[Parameter] public CreateNewUserAddressRequest Model { get; set; } = new();
|
||||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; }
|
||||||
|
|
||||||
private bool _isLoading = false;
|
private bool _isLoading = false;
|
||||||
|
|
||||||
@@ -49,9 +49,8 @@ public partial class CreateDialog
|
|||||||
Submit();
|
Submit();
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (RpcException ex)
|
catch (RpcException)
|
||||||
{
|
{
|
||||||
Console.WriteLine(ex);
|
|
||||||
Snackbar.Add("خطای سرور", Severity.Error);
|
Snackbar.Add("خطای سرور", Severity.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace BackOffice.Pages.UserAddress.Components
|
|||||||
public UpdateUserAddressRequest Model { get; set; } = new();
|
public UpdateUserAddressRequest Model { get; set; } = new();
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
MudDialogInstance MudDialog { get; set; }
|
IMudDialogInstance MudDialog { get; set; }
|
||||||
public async void CallUpdateMethod()
|
public async void CallUpdateMethod()
|
||||||
{
|
{
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
@using BackOffice.Common.BaseComponents
|
@using BackOffice.Common.BaseComponents
|
||||||
@using DataModel = BackOffice.BFF.UserAddress.Protobuf.Protos.UserAddress.GetAllUserAddressByFilterResponseModel
|
@using DataModel = BackOffice.BFF.UserAddress.Protobuf.Protos.UserAddress.GetAllUserAddressByFilterResponseModel
|
||||||
|
|
||||||
<BackOffice.Common.BaseComponents.BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleard" OnSubmitClick="OnFilterSubmit">
|
<BackOffice.Common.BaseComponents.BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleared" OnSubmitClick="OnFilterSubmit">
|
||||||
<Filters>
|
<Filters>
|
||||||
<MudTextField T="string" Clearable="true" Label="عنوان" @bind-Value="@_request.Filter.Title" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
<MudTextField T="string" Clearable="true" Label="عنوان" @bind-Value="@_request.Filter.Title" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
</Filters>
|
</Filters>
|
||||||
@@ -54,4 +54,3 @@
|
|||||||
</BackOffice.Common.BaseComponents.BasePageComponent>
|
</BackOffice.Common.BaseComponents.BasePageComponent>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -83,14 +83,14 @@ public partial class UserAddressMainPage
|
|||||||
}
|
}
|
||||||
public async Task OnFilterSubmit()
|
public async Task OnFilterSubmit()
|
||||||
{
|
{
|
||||||
_basePage.IsFilterd = true;
|
_basePage.IsFiltered = true;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnFilterCleard()
|
public async Task OnFilterCleared()
|
||||||
{
|
{
|
||||||
_basePage.IsFilterd = false;
|
_basePage.IsFiltered = false;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
_request = new() { Filter = new() { } };
|
_request = new() { Filter = new() { } };
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
@using BackOffice.Common.BaseComponents
|
@using BackOffice.Common.BaseComponents
|
||||||
@using DataModel = BackOffice.BFF.UserOrder.Protobuf.Protos.UserOrder.GetAllUserOrderByFilterResponseModel
|
@using DataModel = BackOffice.BFF.UserOrder.Protobuf.Protos.UserOrder.GetAllUserOrderByFilterResponseModel
|
||||||
|
|
||||||
<BackOffice.Common.BaseComponents.BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleard" OnSubmitClick="OnFilterSubmit">
|
<BackOffice.Common.BaseComponents.BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleared" OnSubmitClick="OnFilterSubmit">
|
||||||
<Filters>
|
<Filters>
|
||||||
<MudNumericField HideSpinButtons="true" T="long?" Clearable="true" Label="قیمت" @bind-Value="@_request.Filter.Price" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
<MudNumericField HideSpinButtons="true" T="long?" Clearable="true" Label="قیمت" @bind-Value="@_request.Filter.Price" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
</Filters>
|
</Filters>
|
||||||
@@ -65,4 +65,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,14 +56,14 @@ public partial class UserOrderMainPage
|
|||||||
}
|
}
|
||||||
public async Task OnFilterSubmit()
|
public async Task OnFilterSubmit()
|
||||||
{
|
{
|
||||||
_basePage.IsFilterd = true;
|
_basePage.IsFiltered = true;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnFilterCleard()
|
public async Task OnFilterCleared()
|
||||||
{
|
{
|
||||||
_basePage.IsFilterd = false;
|
_basePage.IsFiltered = false;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
_request = new() { Filter = new() { } };
|
_request = new() { Filter = new() { } };
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public partial class CreateDialog
|
|||||||
{
|
{
|
||||||
[Inject] public UserRoleContract.UserRoleContractClient UserRoleContract { get; set; }
|
[Inject] public UserRoleContract.UserRoleContractClient UserRoleContract { get; set; }
|
||||||
[Parameter] public CreateNewUserRoleRequest Model { get; set; } = new();
|
[Parameter] public CreateNewUserRoleRequest Model { get; set; } = new();
|
||||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; }
|
||||||
|
|
||||||
private bool _isLoading = false;
|
private bool _isLoading = false;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace BackOffice.Pages.UserRole.Components
|
|||||||
public UpdateUserRoleRequest Model { get; set; } = new();
|
public UpdateUserRoleRequest Model { get; set; } = new();
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
MudDialogInstance MudDialog { get; set; }
|
IMudDialogInstance MudDialog { get; set; }
|
||||||
|
|
||||||
public async void CallUpdateMethod()
|
public async void CallUpdateMethod()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
@using BackOffice.Common.BaseComponents
|
@using BackOffice.Common.BaseComponents
|
||||||
@using DataModel = BackOffice.BFF.UserRole.Protobuf.Protos.UserRole.GetAllUserRoleByFilterResponseModel
|
@using DataModel = BackOffice.BFF.UserRole.Protobuf.Protos.UserRole.GetAllUserRoleByFilterResponseModel
|
||||||
|
|
||||||
<BackOffice.Common.BaseComponents.BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleard" OnSubmitClick="OnFilterSubmit">
|
<BackOffice.Common.BaseComponents.BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleared" OnSubmitClick="OnFilterSubmit">
|
||||||
<Filters>
|
<Filters>
|
||||||
<MudNumericField T="long?" HideSpinButtons="true" Clearable="true" Label="شناسه" @bind-Value="@_request.Filter.Id" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
<MudNumericField T="long?" HideSpinButtons="true" Clearable="true" Label="شناسه" @bind-Value="@_request.Filter.Id" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
</Filters>
|
</Filters>
|
||||||
@@ -53,4 +53,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -83,14 +83,14 @@ public partial class UserRoleMainPage
|
|||||||
}
|
}
|
||||||
public async Task OnFilterSubmit()
|
public async Task OnFilterSubmit()
|
||||||
{
|
{
|
||||||
_basePage.IsFilterd = true;
|
_basePage.IsFiltered = true;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnFilterCleard()
|
public async Task OnFilterCleared()
|
||||||
{
|
{
|
||||||
_basePage.IsFilterd = false;
|
_basePage.IsFiltered = false;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
_request = new() { Filter = new() { } };
|
_request = new() { Filter = new() { } };
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
|
|||||||
104
src/BackOffice/Shared/CustomMudTheme.cs
Normal file
104
src/BackOffice/Shared/CustomMudTheme.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace BackOffice.Shared;
|
||||||
|
|
||||||
|
public static class CustomMudTheme
|
||||||
|
{
|
||||||
|
public static MudTheme CustomMudBlazorTheme { get; set; } = new()
|
||||||
|
{
|
||||||
|
PaletteLight = new PaletteLight()
|
||||||
|
{
|
||||||
|
Primary = "#0380C0",
|
||||||
|
Secondary = "#5E4DF9",
|
||||||
|
PrimaryContrastText = "#FFFFFF",
|
||||||
|
AppbarText = Colors.Gray.Darken4,
|
||||||
|
Background = "#F5F5F5",
|
||||||
|
AppbarBackground = "#F5F5F5",
|
||||||
|
TextPrimary = Colors.Gray.Darken3,
|
||||||
|
Surface = "#FFFFFF",
|
||||||
|
Divider = "#B2BFCB",
|
||||||
|
DrawerBackground = "#FFFFFF",
|
||||||
|
DrawerText = Colors.Gray.Darken3,
|
||||||
|
DrawerIcon = Colors.Gray.Darken2,
|
||||||
|
},
|
||||||
|
LayoutProperties = new LayoutProperties
|
||||||
|
{
|
||||||
|
DefaultBorderRadius = "12px",
|
||||||
|
AppbarHeight = "56px",
|
||||||
|
DrawerWidthLeft = "260px",
|
||||||
|
DrawerWidthRight = "260px"
|
||||||
|
},
|
||||||
|
Typography = new Typography
|
||||||
|
{
|
||||||
|
Default = new DefaultTypography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir", "Tahoma", "Segoe UI", "Arial", "sans-serif" }
|
||||||
|
},
|
||||||
|
H1 = new H1Typography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "2rem", LineHeight = "1.70", FontWeight = "800",
|
||||||
|
LetterSpacing = "normal"
|
||||||
|
},
|
||||||
|
H2 = new H2Typography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "1.875rem", LineHeight = "1.65", FontWeight = "800",
|
||||||
|
LetterSpacing = "normal"
|
||||||
|
},
|
||||||
|
H3 = new H3Typography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "1.5rem", LineHeight = "1.60", FontWeight = "800",
|
||||||
|
LetterSpacing = "normal"
|
||||||
|
},
|
||||||
|
H4 = new H4Typography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "1.25rem", LineHeight = "1.55", FontWeight = "800",
|
||||||
|
LetterSpacing = "normal"
|
||||||
|
},
|
||||||
|
H5 = new H5Typography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "1.125rem", LineHeight = "1.50", FontWeight = "800",
|
||||||
|
LetterSpacing = "normal"
|
||||||
|
},
|
||||||
|
H6 = new H6Typography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "1rem", LineHeight = "1.45", FontWeight = "800",
|
||||||
|
LetterSpacing = "normal"
|
||||||
|
},
|
||||||
|
Subtitle1 = new Subtitle1Typography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "1rem", LineHeight = "1.62", FontWeight = "500",
|
||||||
|
LetterSpacing = "normal"
|
||||||
|
},
|
||||||
|
Subtitle2 = new Subtitle2Typography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "0.875rem", LineHeight = "1.60", FontWeight = "500",
|
||||||
|
LetterSpacing = "normal"
|
||||||
|
},
|
||||||
|
Body1 = new Body1Typography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "1rem", LineHeight = "1.85", FontWeight = "400",
|
||||||
|
LetterSpacing = "normal"
|
||||||
|
},
|
||||||
|
Body2 = new Body2Typography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "0.875rem", LineHeight = "1.80", FontWeight = "400",
|
||||||
|
LetterSpacing = "normal"
|
||||||
|
},
|
||||||
|
Caption = new CaptionTypography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "0.75rem", LineHeight = "1.60", FontWeight = "400",
|
||||||
|
LetterSpacing = "normal"
|
||||||
|
},
|
||||||
|
Overline = new OverlineTypography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "0.75rem", LineHeight = "1.60", FontWeight = "500",
|
||||||
|
LetterSpacing = "normal"
|
||||||
|
},
|
||||||
|
Button = new ButtonTypography()
|
||||||
|
{
|
||||||
|
FontFamily = new[] { "Vazir" }, FontSize = "0.875rem", LineHeight = "1.60", FontWeight = "600",
|
||||||
|
LetterSpacing = "normal", TextTransform = "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
@inherits LayoutComponentBase
|
@inherits LayoutComponentBase
|
||||||
|
|
||||||
<MudRTLProvider RightToLeft="true">
|
<MudRTLProvider RightToLeft="true">
|
||||||
<MudThemeProvider Theme="CustomTheme" />
|
<MudThemeProvider Theme="CustomMudTheme.CustomMudBlazorTheme" />
|
||||||
<MudDialogProvider />
|
<MudDialogProvider />
|
||||||
<MudSnackbarProvider />
|
<MudSnackbarProvider />
|
||||||
|
|
||||||
@@ -13,24 +13,3 @@
|
|||||||
</MudMainContent>
|
</MudMainContent>
|
||||||
</MudLayout>
|
</MudLayout>
|
||||||
</MudRTLProvider>
|
</MudRTLProvider>
|
||||||
|
|
||||||
|
|
||||||
@code {
|
|
||||||
|
|
||||||
|
|
||||||
MudTheme CustomTheme = new MudTheme()
|
|
||||||
{
|
|
||||||
Typography = new Typography()
|
|
||||||
{
|
|
||||||
Default = new Default()
|
|
||||||
{
|
|
||||||
FontFamily = new[] { "IRANSans" }
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
LayoutProperties = new()
|
|
||||||
{
|
|
||||||
DrawerWidthRight = "250px"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -4,14 +4,14 @@
|
|||||||
@attribute [AllowAnonymous]
|
@attribute [AllowAnonymous]
|
||||||
|
|
||||||
<MudRTLProvider RightToLeft="true">
|
<MudRTLProvider RightToLeft="true">
|
||||||
<MudThemeProvider Theme="CustomTheme" />
|
<MudThemeProvider Theme="CustomMudTheme.CustomMudBlazorTheme" />
|
||||||
<MudDialogProvider />
|
<MudDialogProvider />
|
||||||
<MudSnackbarProvider />
|
<MudSnackbarProvider />
|
||||||
<MudPopoverProvider />
|
<MudPopoverProvider />
|
||||||
<MudLayout>
|
<MudLayout>
|
||||||
<MudAppBar>
|
<MudAppBar Elevation="1" Color="Color.Surface">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@((e) => DrawerToggle())" />
|
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@((e) => DrawerToggle())" />
|
||||||
مدیریت
|
<MudText Class="mx-2" Typo="Typo.h6">پنل مدیریت فرصت</MudText>
|
||||||
<MudSpacer />
|
<MudSpacer />
|
||||||
<AuthorizeView>
|
<AuthorizeView>
|
||||||
<Authorized>
|
<Authorized>
|
||||||
@@ -33,4 +33,3 @@
|
|||||||
</MudLayout>
|
</MudLayout>
|
||||||
</MudRTLProvider>
|
</MudRTLProvider>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,24 @@
|
|||||||
|
using BackOffice.Common.Utilities;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.AspNetCore.Components.Authorization;
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
using MudBlazor;
|
using MudBlazor;
|
||||||
|
|
||||||
namespace BackOffice.Shared;
|
namespace BackOffice.Shared;
|
||||||
|
|
||||||
public partial class MainLayout
|
public partial class MainLayout
|
||||||
{
|
{
|
||||||
bool _drawerOpen = false;
|
private bool _drawerOpen;
|
||||||
private string Details { get; set; }
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
base.OnInitialized();
|
||||||
|
GlobalConstants.ConstSnackbar = Snackbar;
|
||||||
|
}
|
||||||
|
|
||||||
void DrawerToggle()
|
void DrawerToggle()
|
||||||
{
|
{
|
||||||
_drawerOpen = !_drawerOpen;
|
_drawerOpen = !_drawerOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
MudTheme CustomTheme = new MudTheme()
|
|
||||||
{
|
|
||||||
Typography = new Typography()
|
|
||||||
{
|
|
||||||
Default = new Default()
|
|
||||||
{
|
|
||||||
FontFamily = new[] { "IRANSans" }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,57 @@
|
|||||||
@using BackOffice.BFF.Package.Protobuf.Protos.Package
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
@using Microsoft.AspNetCore.Components.Authorization
|
|
||||||
|
<MudNavMenu Bordered="false" Class="nav-menu">
|
||||||
|
<MudText Class="nav-menu__title" Typo="Typo.subtitle2">منوی اصلی</MudText>
|
||||||
|
<MudDivider Class="mb-2" />
|
||||||
|
|
||||||
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
|
Href="/"
|
||||||
|
Icon="@Icons.Material.Filled.Dashboard">
|
||||||
|
داشبورد
|
||||||
|
</MudNavLink>
|
||||||
|
|
||||||
<MudNavMenu Bordered="true">
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix" Href="/">داشبورد</MudNavLink>
|
|
||||||
<AuthorizeView Roles="Administrator">
|
<AuthorizeView Roles="Administrator">
|
||||||
<Authorized>
|
<Authorized>
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix" Href="@(RouteConstance.Package)">مدیریت پکیج</MudNavLink>
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix" Href="@(RouteConstance.UserPage)">مدیریت کاربر</MudNavLink>
|
Href="@(RouteConstance.Package)"
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix" Href="@(RouteConstance.Role)">مدیریت نقش</MudNavLink>
|
Icon="@Icons.Material.Filled.Inventory2">
|
||||||
|
مدیریت پکیج
|
||||||
|
</MudNavLink>
|
||||||
|
|
||||||
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
|
Href="@(RouteConstance.Products)"
|
||||||
|
Icon="@Icons.Material.Filled.ShoppingBag">
|
||||||
|
مدیریت محصول
|
||||||
|
</MudNavLink>
|
||||||
|
|
||||||
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
|
Href="@(RouteConstance.Category)"
|
||||||
|
Icon="@Icons.Material.Filled.Category">
|
||||||
|
مدیریت دستهبندی
|
||||||
|
</MudNavLink>
|
||||||
|
|
||||||
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
|
Href="@(RouteConstance.UserPage)"
|
||||||
|
Icon="@Icons.Material.Filled.People">
|
||||||
|
مدیریت کاربر
|
||||||
|
</MudNavLink>
|
||||||
|
|
||||||
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
|
Href="@(RouteConstance.Role)"
|
||||||
|
Icon="@Icons.Material.Filled.AdminPanelSettings">
|
||||||
|
مدیریت نقش
|
||||||
|
</MudNavLink>
|
||||||
</Authorized>
|
</Authorized>
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
|
|
||||||
|
<MudDivider Class="my-2" />
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix" OnClick="Signout">خروج از حساب</MudNavLink>
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
|
Color="Color.Error"
|
||||||
|
Icon="@Icons.Material.Filled.Logout"
|
||||||
|
OnClick="Signout">
|
||||||
|
خروج از حساب
|
||||||
|
</MudNavLink>
|
||||||
</MudNavMenu>
|
</MudNavMenu>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
.nav-menu {
|
||||||
|
padding: 1.5rem 0.5rem 1.5rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu__title {
|
||||||
|
padding: 0 0.75rem 0.25rem 0.75rem;
|
||||||
|
color: rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu .mud-divider {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu .mud-nav-link {
|
||||||
|
margin: 0.15rem 0.4rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
padding-inline: 0.75rem 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu .mud-nav-link .mud-nav-link-text {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu .mud-nav-link.mud-nav-link-active {
|
||||||
|
background-color: rgba(3, 128, 192, 0.12);
|
||||||
|
color: #0380C0;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu .mud-nav-link.mud-nav-link-active .mud-nav-link-icon {
|
||||||
|
color: #0380C0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
@using MudBlazor
|
@using MudBlazor
|
||||||
@using Mapster
|
@using Mapster
|
||||||
@using DateTimeConverterCL
|
@using DateTimeConverterCL
|
||||||
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
|
|
||||||
|
|
||||||
@attribute [Authorize(Roles = "Administrator, Admin, Author")]
|
@attribute [Authorize(Roles = "Administrator, Admin, Author")]
|
||||||
@@ -20,3 +21,4 @@
|
|||||||
@inject IJSRuntime jsRuntime
|
@inject IJSRuntime jsRuntime
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@inject ILocalStorageService LocalStorageService
|
@inject ILocalStorageService LocalStorageService
|
||||||
|
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||||
|
|||||||
@@ -9,6 +9,17 @@
|
|||||||
url('../fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf') format('truetype');
|
url('../fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Vazir", "IRANSans", Tahoma, "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
.loading-progress {
|
.loading-progress {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
Reference in New Issue
Block a user