feat : add version 0.19.22.38 , fix zarinpall issue

release
Amir Hossein Khademi 2024-04-03 10:40:42 +03:30
parent e86aa9966e
commit 08c116aece
18 changed files with 404 additions and 57 deletions

View File

@ -65,13 +65,12 @@
<MudNavGroup Title="فروشگاه من" Expanded="false"
Icon="@Icons.Material.Outlined.Settings">
<MudNavLink Href="faqs" Icon="@Icons.Material.Filled.ManageAccounts">سوالات متداول</MudNavLink>
<MudNavLink Href="management/faqs" Icon="@Icons.Material.Filled.ManageAccounts">سوالات متداول</MudNavLink>
</MudNavGroup>
<MudNavGroup Title="تنظیماتـــ" Expanded="false"
Icon="@Icons.Material.Outlined.Settings">
<MudNavLink Href="management/shop" Icon="@Icons.Material.Filled.Shop2">فروشگاه</MudNavLink>
<MudNavLink Href="users" Icon="@Icons.Material.Filled.ManageAccounts">نقش ها و کاربران</MudNavLink>
</MudNavGroup>
</MudNavMenu>

View File

@ -6,7 +6,7 @@ public static class StorageFileExtension
{
public static string GetLink(this StorageFileSDto file)
{
var link = $"https://storage.vesmook.com/{file.FileLocation}";
var link = $"https://storage.vesmeh.com/{file.FileLocation}";
return link;
}
}

View File

@ -3,8 +3,8 @@
public static class Address
{
#if DEBUG
//public static string BaseAddress = "http://localhost:32770/api";
public static string BaseAddress = "https://api.vesmeh.com/api";
public static string BaseAddress = "http://localhost:32770/api";
//public static string BaseAddress = "https://api.vesmeh.com/api";
#else
public static string BaseAddress = "https://api.vesmeh.com/api";
#endif
@ -25,4 +25,6 @@ public static class Address
public static string ScraperController => $"{BaseAddress}/scraper";
public static string NewsletterMemberController => $"{BaseAddress}/newsletter/member";
public static string DashboardController => $"{BaseAddress}/dashboard";
public static string SettingController => $"{BaseAddress}/setting";
public static string DistrictController => $"{BaseAddress}/district";
}

View File

@ -5,8 +5,8 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
<AssemblyVersion>0.17.20.30</AssemblyVersion>
<FileVersion>0.17.20.30</FileVersion>
<AssemblyVersion>0.17.21.31</AssemblyVersion>
<FileVersion>0.17.21.31</FileVersion>
<AssemblyName>$(MSBuildProjectName)</AssemblyName>
</PropertyGroup>
@ -85,6 +85,8 @@
<Using Include="NetinaShop.Domain.Entities.ProductCategories" />
<Using Include="NetinaShop.Domain.Entities.Products" />
<Using Include="NetinaShop.Domain.Enums" />
<Using Include="NetinaShop.Domain.MartenEntities.Pages" />
<Using Include="NetinaShop.Domain.Models.Districts" />
<Using Include="Newtonsoft.Json" />
<Using Include="Refit" />
<Using Include="System.Collections.ObjectModel" />

View File

@ -1,4 +1,4 @@
@page "/faqs"
@page "/management/faqs"
@attribute [Microsoft.AspNetCore.Authorization.Authorize]
@inject IDialogService DialogService

View File

@ -1,6 +1,4 @@
using NetinaShop.Domain.Entities.Pages;
namespace NetinaShop.AdminPanel.PWA.Pages;
namespace NetinaShop.AdminPanel.PWA.Pages;
public class FaqManagementPageViewModel : BaseViewModel<FAQPage>
{

View File

@ -0,0 +1,148 @@
@page "/management/shop"
@using NetinaShop.Domain.Models.Districts
@attribute [Microsoft.AspNetCore.Authorization.Authorize]
@inject IDialogService DialogService
@inject NavigationManager NavigationManager
@inject ISnackbar Snackbar
@inject IUserUtility UserUtility
@inject IRestWrapper RestWrapper
<MudStack class="w-full p-8 h-screen bg-[--mud-palette-background-grey]">
<MudGrid>
<MudItem xs="12">
<MudPaper class="px-5 py-5">
<MudStack Row="true">
<MudStack class="mb-5 mx-2">
<MudText Typo="Typo.h4">فروشـــــگاه من</MudText>
<MudText Typo="Typo.caption">شما می توانید اطلاعات فروشگاه خود را ویرایش نمایید</MudText>
</MudStack>
<MudSpacer/>
<BaseButtonUi Size="Size.Large"
OnClickCallback="ViewModel.SubmitShopSettingAsync"
class="mt-2 mb-8 w-64 rounded-md"
IsProcessing="@ViewModel.IsProcessing"
Icon="@Icons.Material.Outlined.Check"
Content="ثبتـــ اطلاعات" Variant="Variant.Filled" Color="Color.Success" />
</MudStack>
<MudGrid>
<MudItem xs="12" sm="4">
<MudTextField T="string" @bind-Value="@ViewModel.ShopSetting.Name" Variant="Variant.Outlined" Label="نام فروشاه"></MudTextField>
</MudItem>
<MudItem xs="12" sm="4">
<MudTextField T="string" @bind-Value="@ViewModel.ShopSetting.Slogan" Variant="Variant.Outlined" Label="شعار فروشگاه"></MudTextField>
</MudItem>
<MudItem xs="12" sm="4">
<MudTextField T="string" @bind-Value="@ViewModel.ShopSetting.Description" Variant="Variant.Outlined" Label="توضیحات"></MudTextField>
</MudItem>
<MudItem xs="12" sm="5">
<MudTextField T="double" @bind-Value="@ViewModel.ShopSetting.TaxesFee" Variant="Variant.Outlined" Label="مقدار مالیات"></MudTextField>
</MudItem>
<MudItem xs="12" sm="5">
<MudTextField T="double" @bind-Value="@ViewModel.ShopSetting.ServiceFee" Variant="Variant.Outlined" Label="مقدار سرویس"></MudTextField>
</MudItem>
<MudItem xs="12" sm="2">
<MudSelect T="bool" Variant="Variant.Outlined" Label="سرویس درصدی می باشد ؟">
<MudSelectItem T="bool" Value="true">بلی</MudSelectItem>
<MudSelectItem T="bool" Value="true">خیر</MudSelectItem>
</MudSelect>
</MudItem>
<MudItem xs="12" sm="3">
<MudAutocomplete Required="true" ToStringFunc="dto => dto.Name" @bind-Value="@ViewModel.SelectedProvince"
SearchFunc="@ViewModel.SearchProvinceAsync"
T="Province"
Label="استان"
Variant="Variant.Outlined">
<ProgressIndicatorInPopoverTemplate>
<MudList Clickable="false">
<MudListItem>
<div class="flex flex-row w-full mx-auto">
<MudProgressCircular class="my-auto mr-1 -ml-4" Size="Size.Small" Indeterminate="true" />
<p class="font-bold my-1 mx-auto text-md">منتظر بمانید</p>
</div>
</MudListItem>
</MudList>
</ProgressIndicatorInPopoverTemplate>
<ItemTemplate Context="e">
<p>@e.Name</p>
</ItemTemplate>
</MudAutocomplete>
</MudItem>
<MudItem xs="12" sm="3">
<MudAutocomplete Required="true" ToStringFunc="dto => dto.Name" @bind-Value="@ViewModel.SelectedCity"
SearchFunc="@ViewModel.SearchCityAsync"
T="City"
Label="شهر"
Variant="Variant.Outlined">
<ProgressIndicatorInPopoverTemplate>
<MudList Clickable="false">
<MudListItem>
<div class="flex flex-row w-full mx-auto">
<MudProgressCircular class="my-auto mr-1 -ml-4" Size="Size.Small" Indeterminate="true" />
<p class="font-bold my-1 mx-auto text-md">منتظر بمانید</p>
</div>
</MudListItem>
</MudList>
</ProgressIndicatorInPopoverTemplate>
<ItemTemplate Context="e">
<p>@e.Name</p>
</ItemTemplate>
</MudAutocomplete>
</MudItem>
<MudItem xs="12" sm="6">
<MudTextField T="string" @bind-Value="@ViewModel.ShopSetting.Address" Variant="Variant.Outlined" Label="آدرس فروشگاه"></MudTextField>
</MudItem>
<MudItem xs="12" sm="6">
<MudTextField T="string" @bind-Value="@ViewModel.ShopSetting.ENamad" Variant="Variant.Outlined" Label="آدرس نماد اعتماد"></MudTextField>
</MudItem>
</MudGrid>
</MudPaper>
<MudPaper class="px-5 mt-8 py-5">
<MudStack Row="true">
<MudStack class="mb-5 mx-2">
<MudText Typo="Typo.h4">تنظیمات درگاه پرداخت</MudText>
<MudText Typo="Typo.caption">شما می توانید اطلاعات درگاه پرداخت خود را ویرایش نمایید</MudText>
</MudStack>
<MudSpacer />
<BaseButtonUi Size="Size.Large" class="mt-2 mb-8 w-64 rounded-md"
OnClickCallback="ViewModel.SubmitPaymentSettingAsync"
IsProcessing="@ViewModel.IsProcessing"
Icon="@Icons.Material.Outlined.Check" Content="ثبتـــ اطلاعات"
Variant="Variant.Filled" Color="Color.Success" />
</MudStack>
<MudGrid>
<MudItem xs="12" sm="6">
<MudTextField T="string" @bind-Value="@ViewModel.PaymentSetting.ZarinPalApiKey" Variant="Variant.Outlined" Label="کد مرچنت زرین پال"></MudTextField>
</MudItem>
<MudItem xs="12" sm="6">
<MudTextField T="string" @bind-Value="@ViewModel.PaymentSetting.Shaba" Variant="Variant.Outlined" Label="شماره شبا پشتیبانی"></MudTextField>
</MudItem>
</MudGrid>
</MudPaper>
</MudItem>
</MudGrid>
</MudStack>
@code
{
public ShopManagementPageViewModel ViewModel { get; set; }
protected override async Task OnInitializedAsync()
{
ViewModel = new ShopManagementPageViewModel(NavigationManager, Snackbar, UserUtility, RestWrapper, DialogService);
await ViewModel.InitializeAsync();
await base.OnInitializedAsync();
}
}

View File

@ -0,0 +1,182 @@
using NetinaShop.Domain.MartenEntities.Settings;
using NetinaShop.Domain.Models.Districts;
namespace NetinaShop.AdminPanel.PWA.Pages;
public class ShopManagementPageViewModel : BaseViewModel
{
private readonly NavigationManager _navigationManager;
private readonly ISnackbar _snackbar;
private readonly IUserUtility _userUtility;
private readonly IDialogService _dialogService;
private readonly IRestWrapper _restWrapper;
public ShopSetting ShopSetting { get; set; } = new ShopSetting();
public PaymentSetting PaymentSetting { get; set; } = new PaymentSetting();
public List<City> Cities = new List<City>();
public City SelectedCity = new City();
public List<Province> Provinces = new List<Province>();
public Province SelectedProvince = new Province();
public ShopManagementPageViewModel(NavigationManager navigationManager, ISnackbar snackbar, IUserUtility userUtility, IRestWrapper restWrapper, IDialogService dialogService)
{
_navigationManager = navigationManager;
_snackbar = snackbar;
_userUtility = userUtility;
_restWrapper = restWrapper;
_dialogService = dialogService;
}
public override async Task InitializeAsync()
{
try
{
IsProcessing = true;
var token = await _userUtility.GetBearerTokenAsync();
if (token == null)
throw new Exception("Token is null");
ShopSetting = await _restWrapper.SettingRestApi.GetSettingAsync<ShopSetting>("ShopSetting", token);
if (ShopSetting.CityId != 0)
SelectedCity = new City { Id = ShopSetting.CityId, Name = ShopSetting.City };
if (ShopSetting.ProvinceId != 0)
SelectedProvince = new Province { Id = ShopSetting.ProvinceId, Name = ShopSetting.Province };
PaymentSetting = await _restWrapper.SettingRestApi.GetSettingAsync<PaymentSetting>("PaymentSetting", token);
}
catch (ApiException e)
{
var exe = await e.GetContentAsAsync<ApiResult>();
_snackbar.Add(exe != null ? exe.Message : e.Content, Severity.Error);
}
catch (Exception ex)
{
_snackbar.Add(ex.Message, Severity.Error);
}
finally
{
IsProcessing = false;
}
await base.InitializeAsync();
}
public async Task<IEnumerable<City>> SearchCityAsync(string city)
{
try
{
if (SelectedProvince.Id == default)
throw new Exception("لطفا استان را انتخاب کنید");
Cities = await _restWrapper.DistrictApiRest.GetCitiesAsync(SelectedProvince.Id);
if (city.IsNullOrEmpty())
return Cities;
return Cities.Where(c => c.Name.Contains(city));
}
catch (ApiException ex)
{
var exe = await ex.GetContentAsAsync<ApiResult>();
if (exe != null)
_snackbar.Add(exe.Message, Severity.Error);
_snackbar.Add(ex.Content, Severity.Error);
return Cities;
}
catch (Exception e)
{
_snackbar.Add(e.Message, Severity.Error);
return Cities;
}
}
public async Task<IEnumerable<Province>> SearchProvinceAsync(string province)
{
try
{
Provinces = await _restWrapper.DistrictApiRest.GetProvincesAsync();
if (province.IsNullOrEmpty())
return Provinces;
return Provinces.Where(c => c.Name.Contains(province));
}
catch (ApiException ex)
{
var exe = await ex.GetContentAsAsync<ApiResult>();
if (exe != null)
_snackbar.Add(exe.Message, Severity.Error);
_snackbar.Add(ex.Content, Severity.Error);
return Provinces;
}
catch (Exception e)
{
_snackbar.Add(e.Message, Severity.Error);
return Provinces;
}
}
public async Task SubmitShopSettingAsync()
{
try
{
if (SelectedProvince.Id == default)
throw new Exception("لطفا استان را انتخاب کنید");
if (SelectedCity.Id == default)
throw new Exception("لطفا شهر را انتخاب نمایید");
ShopSetting.CityId = SelectedCity.Id;
ShopSetting.ProvinceId = SelectedProvince.Id;
ShopSetting.City = SelectedCity.Name;
ShopSetting.Province = SelectedProvince.Name;
var token = await _userUtility.GetBearerTokenAsync();
if (token == null) throw new Exception("Token is null");
await _restWrapper.SettingRestApi.PostSettingAsync("ShopSetting", ShopSetting, token);
_snackbar.Add("تنظیمات فروشگاه با موفقیت به روز شد", Severity.Success);
}
catch (ApiException ex)
{
var exe = await ex.GetContentAsAsync<ApiResult>();
_snackbar.Add(exe != null ? exe.Message : ex.Content, Severity.Error);
}
catch (Exception e)
{
_snackbar.Add(e.Message, Severity.Error);
}
finally
{
IsProcessing = false;
}
}
public async Task SubmitPaymentSettingAsync()
{
try
{
var token = await _userUtility.GetBearerTokenAsync();
if (token == null) throw new Exception("Token is null");
await _restWrapper.SettingRestApi.PostSettingAsync("PaymentSetting", PaymentSetting, token);
_snackbar.Add("تنظیمات فروشگاه با موفقیت به روز شد", Severity.Success);
}
catch (ApiException ex)
{
var exe = await ex.GetContentAsAsync<ApiResult>();
_snackbar.Add(exe != null ? exe.Message : ex.Content, Severity.Error);
}
catch (Exception e)
{
_snackbar.Add(e.Message, Severity.Error);
}
finally
{
IsProcessing = false;
}
}
}

View File

@ -1,4 +1,4 @@
@page "/users"
@page "/setting/user"
@using NetinaShop.Domain.Entities.Users
@attribute [Microsoft.AspNetCore.Authorization.Authorize]

View File

@ -0,0 +1,13 @@
namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
public interface IDistrictApiRest
{
[Get("/city")]
public Task<List<City>> GetCitiesAsync();
[Get("/city")]
public Task<List<City>> GetCitiesAsync([Query]int provinceId);
[Get("/province")]
public Task<List<Province>> GetProvincesAsync();
}

View File

@ -4,10 +4,10 @@ public interface IPageRestApi
{
[Get("/type/{type}")]
Task<BasePageEntitySDto> ReadByType([Query] string type, [Header("Authorization")] string authorization);
Task<BasePageSDto> ReadByType([Query] string type, [Header("Authorization")] string authorization);
[Get("/{id}")]
Task<BasePageEntitySDto> ReadById(Guid id, [Header("Authorization")] string authorization);
Task<BasePageSDto> ReadById(Guid id, [Header("Authorization")] string authorization);
[Post("")]
Task CreatePage([Body] PageActionRequestDto request, [Header("Authorization")] string authorization);

View File

@ -21,4 +21,6 @@ public interface IRestWrapper
public IPageRestApi PageRestApi { get; }
public IScraperRestApi ScraperRestApi { get; }
public IDashboardApiRest DashboardApiRest { get; }
public ISettingRestApi SettingRestApi { get; }
public IDistrictApiRest DistrictApiRest { get; }
}

View File

@ -0,0 +1,10 @@
namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
public interface ISettingRestApi
{
[Get("/{settingName}")]
public Task<TSetting> GetSettingAsync<TSetting>(string settingName, [Header("Authorization")] string authorization) where TSetting : class;
[Post("/{settingName}")]
public Task PostSettingAsync<TSetting>(string settingName,[Body]TSetting setting , [Header("Authorization")]string authorization) where TSetting : class;
}

View File

@ -32,6 +32,7 @@ public class RestWrapper : IRestWrapper
public IPaymentRestApi PaymentRestApi => RestService.For<IPaymentRestApi>(Address.PaymentController, setting);
public IPageRestApi PageRestApi => RestService.For<IPageRestApi>(Address.PageController, setting);
public IScraperRestApi ScraperRestApi => RestService.For<IScraperRestApi>(Address.ScraperController, setting);
public ISettingRestApi SettingRestApi => RestService.For<ISettingRestApi>(Address.SettingController, setting);
public IDashboardApiRest DashboardApiRest => RestService.For<IDashboardApiRest>(Address.DashboardController, setting);
public IDistrictApiRest DistrictApiRest => RestService.For<IDistrictApiRest>(Address.DistrictController, setting);
}

View File

@ -1,37 +0,0 @@
namespace NetinaShop.AdminPanel.PWA.Utilities.Models;
public class CustomEventHelper
{
private readonly Func<EventArgs, Task> _callback;
public CustomEventHelper(Func<EventArgs, Task> callback)
{
_callback = callback;
}
[JSInvokable]
public Task OnCustomEvent(EventArgs args) => _callback(args);
}
public class CustomEventInterop : IDisposable
{
private readonly IJSRuntime _jsRuntime;
private DotNetObjectReference<CustomEventHelper> Reference;
public CustomEventInterop(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public ValueTask<string> SetupCustomEventCallback(Func<EventArgs, Task> callback)
{
Reference = DotNetObjectReference.Create(new CustomEventHelper(callback));
// addCustomEventListener will be a js function we create later
return _jsRuntime.InvokeAsync<string>("addCustomEventListener", Reference);
}
public void Dispose()
{
Reference?.Dispose();
}
}

View File

@ -1,6 +1,6 @@
{
"version": "0.17.19.26",
"versionNumber": 0.171926,
"version": "0.19.22.38",
"versionNumber": 0.192238,
"versionName": "چرتکه",
"description": "",
"features": [
@ -8,10 +8,14 @@
"تکمیل پروسه سفارش گیری",
"قابلیت افزودن تصویر به برند ها",
"قابلیت افزودن تصویر به دسته بندی محصولات",
"تغییر دیالوگ پرسشی"
"تغییر دیالوگ پرسشی",
"افزودن تنظیمات درگاه",
"افزودن بخش تنظیمات فروشگاه"
],
"bugFixes": [
"حل مشکلات امنیتی",
"رفع مشکلات رسپانسیو"
"رفع مشکلات رسپانسیو",
"رفع مشکل دریافت تصاویر",
"رفع مشکل عدم اتصال به درگاه"
]
}

View File

@ -1072,6 +1072,10 @@ input:checked + .toggle-bg {
margin-left: 2.5rem;
margin-right: 2.5rem;
}
.mx-2 {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
.mx-3 {
margin-left: 0.75rem;
margin-right: 0.75rem;
@ -1674,6 +1678,10 @@ input:checked + .toggle-bg {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.py-5 {
padding-top: 1.25rem;
padding-bottom: 1.25rem;
}
.py-8 {
padding-top: 2rem;
padding-bottom: 2rem;

View File

@ -1150,6 +1150,11 @@ input:checked + .toggle-bg {
margin-right: 2.5rem;
}
.mx-2 {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
.mx-3 {
margin-left: 0.75rem;
margin-right: 0.75rem;
@ -1190,6 +1195,11 @@ input:checked + .toggle-bg {
margin-bottom: 1rem;
}
.my-5 {
margin-top: 1.25rem;
margin-bottom: 1.25rem;
}
.my-auto {
margin-top: auto;
margin-bottom: auto;
@ -1932,6 +1942,11 @@ input:checked + .toggle-bg {
padding-bottom: 0.75rem;
}
.py-5 {
padding-top: 1.25rem;
padding-bottom: 1.25rem;
}
.py-8 {
padding-top: 2rem;
padding-bottom: 2rem;