feat : add version 0.6.12.22 , fix updating issue

complete discount and product page changing
release
Amir Hossein Khademi 2024-02-17 15:39:59 +03:30
parent f883451697
commit e6dd4c875e
36 changed files with 1457 additions and 123 deletions

View File

@ -2,6 +2,7 @@
Color="@Color" Color="@Color"
@onclick="OnClickCallback" @onclick="OnClickCallback"
@attributes="CapturedAttributes" @attributes="CapturedAttributes"
Size="Size"
Disabled="IsProcessing" Disabled="IsProcessing"
DisableElevation="true" DisableElevation="true"
> >
@ -47,6 +48,9 @@
[Parameter] [Parameter]
public bool IsProcessing { get; set; } = false; public bool IsProcessing { get; set; } = false;
[Parameter]
public Size Size { get; set; } = Size.Medium;
[Parameter] [Parameter]
public EventCallback OnClickCallback { get; set; } public EventCallback OnClickCallback { get; set; }

View File

@ -1,25 +1,36 @@
@inject IJSRuntime JsRuntime @inject IJSRuntime JsRuntime
@implements IAsyncDisposable
<div class="editor"></div> <div class="editor"></div>
<script type="text/javascript"> <script type="text/javascript">
function destroyEditor(){ function destroyEditor() {
window.editor?.destroy() // document.querySelector('.editor').ckeditorInstance.destroy();
window.editor = null;
console.log('Editor Destroyed');
} }
function lunchEditor(data) { function lunchEditor(data) {
if (!document.querySelector('.editor')) return
if (window.editor) return
ClassicEditor.create(document.querySelector('.editor'), { ClassicEditor.create(document.querySelector('.editor'), {
// Editor configuration. // Editor configuration.
}) })
.then(editor => { .then(editor => {
window.editor = editor; window.editor = editor;
window.editor.setData(data); window.editor.setData(data);
editor.on('change', function () { editor.editing.view.document.on('blur', () => {
console.log(editor.getData()); GLOBAL.DotNetReference.invokeMethodAsync('MyMethod',window.editor.getData());
}); });
}) })
.catch(handleSampleError); .catch(handleSampleError);
} }
var GLOBAL = {};
GLOBAL.DotNetReference = null;
GLOBAL.SetDotnetReference = function (pDotNetReference) {
GLOBAL.DotNetReference = pDotNetReference;
};
function handleSampleError(error) { function handleSampleError(error) {
const issueUrl = 'https://github.com/ckeditor/ckeditor5/issues'; const issueUrl = 'https://github.com/ckeditor/ckeditor5/issues';
@ -44,6 +55,15 @@
[Parameter] [Parameter]
public EventCallback<string> TextChanged { get; set; } public EventCallback<string> TextChanged { get; set; }
[JSInvokable("MyMethod")]
public void HandleEditorBlur(string data)
{
Text = data;
TextChanged.InvokeAsync(data);
StateHasChanged(); // Ensure UI updates
}
protected override Task OnParametersSetAsync() protected override Task OnParametersSetAsync()
{ {
return base.OnParametersSetAsync(); return base.OnParametersSetAsync();
@ -51,8 +71,23 @@
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
var lDotNetReference = DotNetObjectReference.Create(this);
await JsRuntime.InvokeVoidAsync("GLOBAL.SetDotnetReference", lDotNetReference);
await JsRuntime.InvokeVoidAsync("window.lunchEditor", Text); await JsRuntime.InvokeVoidAsync("window.lunchEditor", Text);
await base.OnAfterRenderAsync(firstRender); await base.OnAfterRenderAsync(firstRender);
} }
public void Dispose()
{
JsRuntime.InvokeVoidAsync("window.destroyEditor", Text);
}
public async ValueTask DisposeAsync()
{
await JsRuntime.InvokeVoidAsync("window.destroyEditor", Text);
}
} }

View File

@ -71,6 +71,11 @@
</MudNavGroup> </MudNavGroup>
<MudNavGroup Title="فروشگاه من" Expanded="false"
Icon="@Icons.Material.Outlined.Settings">
<MudNavLink Href="faqs" Icon="@Icons.Material.Filled.ManageAccounts">سوالات متداول</MudNavLink>
</MudNavGroup>
<MudNavGroup Title="تنظیماتـــ" Expanded="false" <MudNavGroup Title="تنظیماتـــ" Expanded="false"
Icon="@Icons.Material.Outlined.Settings"> Icon="@Icons.Material.Outlined.Settings">
@ -78,12 +83,13 @@
<MudNavLink Href="users" Icon="@Icons.Material.Filled.ManageAccounts">نقش ها و کاربران</MudNavLink> <MudNavLink Href="users" Icon="@Icons.Material.Filled.ManageAccounts">نقش ها و کاربران</MudNavLink>
</MudNavGroup> </MudNavGroup>
</MudNavMenu> </MudNavMenu>
<p class="bottom-0 align-bottom mx-auto">Version : @version</p>
</MudStack> </MudStack>
@code @code
{ {
string? version = typeof(Program)?.Assembly.GetName()?.Version?.ToString();
private void SelectNav() private void SelectNav()
{ {

View File

@ -0,0 +1,89 @@
@using NetinaShop.Domain.Entities.Users
@inject ISnackbar Snackbar
@inject IRestWrapper RestWrapper
@inject IUserUtility UserUtility
@inject IDialogService DialogService
<MudDialog DisableSidePadding="true" class="mx-auto">
<DialogContent>
<MudStack class="px-4">
<MudDivider class="-mt-3" />
<MudStack Spacing="0">
<MudText Typo="Typo.h6"><b>جست جو از دیجیکالا</b></MudText>
<MudText Typo="Typo.caption">میتوانید نام کالا را از دیجیکالا جست جو کرده و به فروشگاه اضافه کنید</MudText>
</MudStack>
<MudTextField T="string" Placeholder="جست جو بر اساس نام" Adornment="Adornment.Start" Immediate="true"
Clearable="true"
Variant="Variant.Outlined"
ValueChanged="@ViewModel.SearchChanged"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" class="my-auto"
OnAdornmentClick="@ViewModel.SearchAsync"></MudTextField>
<MudContainer class="h-[35rem]" Style="overflow-y: scroll">
@if (ViewModel.IsProcessing)
{
<MudProgressCircular class="my-auto" Size="Size.Large" Color="Color.Info" Indeterminate="true" />
}
else
{
@foreach (var item in ViewModel.PageDto)
{
<MudCard Outlined="true" class="my-2">
<MudCardContent>
<MudStack Row="true">
<MudImage Width="100" Height="100" Class="rounded-lg" Src="@item.MainImage" />
<MudStack>
<MudText Typo="Typo.body1">@item.PersianName</MudText>
<MudText Typo="Typo.body1">@item.EnglishName</MudText>
<MudText Typo="Typo.body2">@item.Summery</MudText>
<MudText Typo="Typo.body2">قیمت : @item.Cost.ToString("N0")</MudText>
</MudStack>
</MudStack>
</MudCardContent>
<MudCardActions>
<MudButton OnClick="async()=> await ViewModel.AddProductToShopAsync(item.ScraperId)"
Variant="Variant.Outlined"
Color="Color.Success">افزودن به فروشگاه</MudButton>
<a href="@item.ScraperUrl" target="_blank">
<MudButton class="mx-3"
Variant="Variant.Outlined"
Color="Color.Warning">مشاهده کالا</MudButton>
</a>
</MudCardActions>
</MudCard>
}
}
</MudContainer>
</MudStack>
</DialogContent>
<DialogActions>
<MudStack Row="true" class="w-full mx-4 mb-2">
@* <BaseButtonUi class="w-64 rounded-md" IsProcessing="@ViewModel.IsProcessing"
Icon="@Icons.Material.Outlined.Check"
Variant="Variant.Filled" Color="Color.Success"
Content="ثبت ویرایش" OnClickCallback="@ViewModel.SubmitEditAsync" /> *@
<MudSpacer />
<MudButton Variant="Variant.Outlined" Size="Size.Large" Color="Color.Error" OnClick="@ViewModel.Cancel">بستن</MudButton>
</MudStack>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
public DigikalaProductActionDialogBoxViewModel ViewModel { get; set; }
protected override async Task OnInitializedAsync()
{
ViewModel = new DigikalaProductActionDialogBoxViewModel(Snackbar, RestWrapper, UserUtility, DialogService, MudDialog);
await ViewModel.InitializeAsync();
await base.OnInitializedAsync();
}
}

View File

@ -0,0 +1,101 @@
using NetinaShop.Domain.Dtos.ScraperDtos.Response;
namespace NetinaShop.AdminPanel.PWA.Dialogs;
public class DigikalaProductActionDialogBoxViewModel : BaseViewModel<ObservableCollection<ScraperProductDto>>
{
private readonly ISnackbar _snackbar;
private readonly IRestWrapper _restWrapper;
private readonly IUserUtility _userUtility;
private readonly IDialogService _dialogService;
private readonly MudDialogInstance _mudDialog;
public string Search = string.Empty;
public DigikalaProductActionDialogBoxViewModel(ISnackbar snackbar,
IRestWrapper restWrapper,
IUserUtility userUtility,
IDialogService dialogService,
MudDialogInstance mudDialog)
{
_snackbar = snackbar;
_restWrapper = restWrapper;
_userUtility = userUtility;
_dialogService = dialogService;
_mudDialog = mudDialog;
}
public void Cancel() => _mudDialog.Cancel();
public async Task AddProductToShopAsync(string productId)
{
var options = new DialogOptions { CloseOnEscapeKey = true };
var parameters = new DialogParameters<QuestionDialog>();
parameters.Add(x => x.ContentText, "آیا از افزودن محصول اطمینان دارید ?");
var dialogReference = await _dialogService.ShowAsync<QuestionDialog>("افزودن محصول از دیجیکالا", parameters, options);
var result = await dialogReference.Result;
if (!result.Canceled)
{
try
{
var token = await _userUtility.GetBearerTokenAsync();
if (token == null)
throw new Exception("Token is null");
IsProcessing = true;
await _restWrapper.ScraperRestApi.AddDigiProductToShopAsync(productId,Search, 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 SearchChanged(string search)
{
if (search.IsNullOrEmpty() && !Search.IsNullOrEmpty())
await InitializeAsync();
Search = search;
}
public async Task SearchAsync()
{
try
{
var token = await _userUtility.GetBearerTokenAsync();
if (token == null)
throw new Exception("Token is null");
if (Search.IsNullOrEmpty())
throw new AppException("اسم کالا برای جست جو وارد نشده است");
IsProcessing = true;
var dto = await _restWrapper.ScraperRestApi.SearchDigiProductsAsync(Search, token);
PageDto.Clear();
dto.ForEach(d => PageDto.Add(d));
}
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,5 @@
using Mapster; using Mapster;
using NetinaShop.Domain.Dtos.ResponseDtos;
using NetinaShop.Domain.Entities.Blogs; using NetinaShop.Domain.Entities.Blogs;
using NetinaShop.Domain.Entities.Discounts; using NetinaShop.Domain.Entities.Discounts;
using NetinaShop.Domain.Mappers; using NetinaShop.Domain.Mappers;
@ -230,15 +231,33 @@ public class DiscountActionDialogBoxViewModel : BaseViewModel<DiscountLDto>
{ {
IsProcessing = true; IsProcessing = true;
if (_discountId == default) if (_discountId == default)
throw new Exception("بلاگ اشتباه است"); throw new Exception("Id is null");
if (SelectedCategory == null)
throw new Exception("لطفا یک دسته بندی انتخاب کنید"); if (IsAllEnable)
{
PageDto.Type = DiscountType.All;
}
else if (IsCategoryEnable)
{
PageDto.Type = DiscountType.Category;
if (SelectedCategory == null)
throw new Exception("دسته بندی را برای تخفیف انتخاب نمایید");
PageDto.CategoryId = SelectedCategory.Id;
}
else if (IsProductEnable)
{
PageDto.Type = DiscountType.Product;
if (SelectedProduct == null)
throw new Exception("کالا مورد نظر را برای تخفیف انتخاب نمایید");
PageDto.ProductId = SelectedProduct.Id;
}
var token = await _userUtility.GetBearerTokenAsync(); var token = await _userUtility.GetBearerTokenAsync();
if (token == null) if (token == null)
throw new Exception("Token is null"); throw new Exception("Token is null");
PageDto.Id = _discountId; PageDto.Id = _discountId;
var request = PageDto.Adapt<UpdateDiscountCommand>(); var request = PageDto.Adapt<UpdateDiscountCommand>();
await _restWrapper.CrudApiRest<Discount, Guid>(Address.DiscountController).Create<UpdateDiscountCommand>(request, token); await _restWrapper.CrudApiRest<Discount, Guid>(Address.DiscountController).Update<UpdateDiscountCommand>(request, token);
_snackbar.Add($"ویرایش تخفیف با موفقیت انجام شد", Severity.Success); _snackbar.Add($"ویرایش تخفیف با موفقیت انجام شد", Severity.Success);
_mudDialog.Close(); _mudDialog.Close();
} }
@ -298,10 +317,12 @@ public class DiscountActionDialogBoxViewModel : BaseViewModel<DiscountLDto>
{ {
try try
{ {
GetProductsResponseDto response = new GetProductsResponseDto();
if (product.IsNullOrEmpty()) if (product.IsNullOrEmpty())
_products = await _restWrapper.ProductRestApi.ReadAll(0); response = await _restWrapper.ProductRestApi.ReadAll(0);
else else
_products = await _restWrapper.ProductRestApi.ReadAll(product); response = await _restWrapper.ProductRestApi.ReadAll(product);
_products = response.Products;
return _products; return _products;
} }
catch (ApiException ex) catch (ApiException ex)

View File

@ -0,0 +1,219 @@
@inject ISnackbar Snackbar
@inject IRestWrapper RestWrapper
@inject IUserUtility UserUtility
@inject IDialogService DialogService
<MudDialog class="mx-auto" DisableSidePadding="true">
<DialogContent>
<MudContainer class="max-h-[40rem] mt-4" Style="overflow-y: scroll">
<MudGrid>
<MudItem md="9">
<MudStack>
<MudStack Row="true">
<MudText Typo="Typo.h6" class="my-auto"><b>اطلاعات سفارش</b></MudText>
<MudSpacer/>
<MudPaper Elevation="0" class="bg-blue-500 px-4 py-2 my-auto">
<MudText Typo="Typo.body2" class="text-white">@ViewModel.PageDto.OrderStatus.ToDisplay()</MudText>
</MudPaper>
</MudStack>
<MudGrid>
<MudItem sm="4">
<MudField Variant="Variant.Outlined" Label="نام و نام خانوادگی">@ViewModel.PageDto.UserFullName</MudField>
</MudItem>
<MudItem sm="4">
<MudField Variant="Variant.Outlined" Label="شماره تماس">@ViewModel.PageDto.UserPhoneNumber</MudField>
</MudItem>
<MudItem sm="4">
<MudField Variant="Variant.Outlined" Label="کد تخفیف">@ViewModel.PageDto.DiscountCode</MudField>
</MudItem>
<MudItem sm="9">
<MudField Variant="Variant.Outlined" Label="آدرس">@ViewModel.PageDto.OrderDelivery.Address</MudField>
</MudItem>
<MudItem sm="3">
<MudField Variant="Variant.Outlined" Label="روش ارسال">@ViewModel.PageDto.OrderDelivery.ShippingMethod</MudField>
</MudItem>
</MudGrid>
<MudStack class="mt-3" Row="true">
<MudText Typo="Typo.h6"><b>کالاهای این سفارش</b></MudText>
<MudSpacer/>
<MudPaper Elevation="0" class="border-dashed border-violet-400 border-2 px-3 py-1 my-auto">
<MudText Typo="Typo.caption" class="text-violet-500"><b>@ViewModel.PageDto.OrderProducts.Sum(op => op.Count) قلم کالا</b></MudText>
</MudPaper>
</MudStack>
<MudDataGrid FixedFooter="true" FixedHeader="true" Striped="true"
Elevation="0"
Bordered="true"
T="OrderProductSDto" Items="@ViewModel.PageDto.OrderProducts"
Filterable="false" Loading="@ViewModel.IsProcessing"
SortMode="@SortMode.None" Groupable="false"
class="border-solid border-2 border-gray-200">
<Columns>
<TemplateColumn T="OrderProductSDto" Title="نام کالا">
<CellTemplate>
<b>@context.Item.ProductName</b>
</CellTemplate>
</TemplateColumn>
<TemplateColumn T="OrderProductSDto" Title="قیمت کالا">
<CellTemplate>
<p>@context.Item.ProductFeeWithDiscount.ToString("N0") ریالــ</p>
</CellTemplate>
</TemplateColumn>
<PropertyColumn Title="تعداد" Property="arg => arg.Count" />
<TemplateColumn T="OrderProductSDto" Title="قیمت کل">
<CellTemplate>
<p>@context.Item.ProductCost.ToString("N0") ریالــ</p>
</CellTemplate>
</TemplateColumn>
</Columns>
</MudDataGrid>
</MudStack>
</MudItem>
<MudItem md="3">
<MudText Typo="Typo.h6" class="mb-2"><b>مبــــالغ</b></MudText>
<MudPaper class="bg-[--mud-palette-background-grey] rounded-md">
<MudStack class="p-3">
<MudStack Row="true">
<MudText class="my-auto" Typo="Typo.body2">جمع کالاها : </MudText>
<MudSpacer/>
<MudText Typo="Typo.body1"><b>@ViewModel.PageDto.TotalProductsPrice.ToString("N0")</b> ریالــ</MudText>
</MudStack>
<MudDivider/>
<MudStack Row="true">
<MudText class="my-auto" Typo="Typo.body2">مالیات : </MudText>
<MudSpacer/>
<MudText Typo="Typo.body1"><b>@ViewModel.PageDto.TaxesPrice.ToString("N0")</b> ریالــ</MudText>
</MudStack>
<MudDivider/>
<MudStack Row="true">
<MudText class="my-auto" Typo="Typo.body2">بسته بندی : </MudText>
<MudSpacer/>
<MudText Typo="Typo.body1"><b>@ViewModel.PageDto.PackingPrice.ToString("N0")</b> ریالــ</MudText>
</MudStack>
<MudDivider/>
<MudStack Row="true">
<MudText class="my-auto" Typo="Typo.body2">هزینه ارسال : </MudText>
<MudSpacer/>
<MudText Typo="Typo.body1"><b>@ViewModel.PageDto.DeliveryPrice.ToString("N0")</b> ریالــ</MudText>
</MudStack>
</MudStack>
<MudPaper class="mud-theme-dark rounded-md">
<MudStack class="p-3">
<MudStack Row="true">
<MudText class="my-auto" Typo="Typo.body2">جمع کل : </MudText>
<MudSpacer/>
<MudText Typo="Typo.body1"><b>@ViewModel.PageDto.TotalPriceWithoutDiscount.ToString("N0")</b> ریالــ</MudText>
</MudStack>
<MudDivider/>
<MudStack Row="true">
<MudText class="my-auto" Typo="Typo.body2">مقدار تخفیف : </MudText>
<MudSpacer/>
<MudText Typo="Typo.body1"><b>@ViewModel.PageDto.DiscountPrice.ToString("N0")</b> ریالــ</MudText>
</MudStack>
<MudDivider/>
<MudStack Row="true">
<MudText class="my-auto" Typo="Typo.body2">مبلغ قابل پرداخت : </MudText>
<MudSpacer/>
<MudText Typo="Typo.body1"><b>@ViewModel.PageDto.TotalPrice.ToString("N0")</b> ریالــ</MudText>
</MudStack>
</MudStack>
</MudPaper>
</MudPaper>
<MudText Typo="Typo.h6" class="mt-3"><b>پرداختــ ها</b></MudText>
<MudPaper Elevation="0" class="bg-[--mud-palette-background-grey] rounded-md">
<MudStack class="p-3">
@foreach (var item in ViewModel.PageDto.Payments)
{
<MudStack Row="true">
<MudText class="my-auto" Typo="Typo.body2">@item.Type.ToDisplay()</MudText>
<MudSpacer/>
<MudText Typo="Typo.body1"><b>@item.Amount.ToString("N0")</b> ریالــ</MudText>
</MudStack>
}
</MudStack>
</MudPaper>
</MudItem>
</MudGrid>
</MudContainer>
</DialogContent>
<DialogActions>
<MudStack Row="true" class="w-full h-fit mx-4 mb-3 bottom-0">
<BaseButtonUi class="w-64 h-12 rounded-md" IsProcessing="@ViewModel.IsProcessing"
Icon="@Icons.Material.Outlined.AirportShuttle"
Variant="Variant.Filled" Color="Color.Success"
Content="ثبت ارسال سفارش" OnClickCallback="ViewModel.SubmitEditAsync" />
<BaseButtonUi class="w-52 h-12 rounded-md" IsProcessing="@ViewModel.IsProcessing"
Icon="@Icons.Material.Outlined.RemoveCircle"
Variant="Variant.Outlined" Color="Color.Error"
Content="لغو سفارش" OnClickCallback="ViewModel.SubmitEditAsync" />
<MudSpacer />
<BaseButtonUi class="w-52 h-12 rounded-md my-auto" IsProcessing="@ViewModel.IsProcessing"
Icon="@Icons.Material.Outlined.Print"
Size="Size.Small"
Variant="Variant.Outlined" Color="Color.Info"
Content="چاپــ فاکتور" OnClickCallback="ViewModel.SubmitEditAsync" />
<MudButton Variant="Variant.Outlined" Size="Size.Medium" Color="Color.Error" OnClick="ViewModel.Cancel">بستن</MudButton>
</MudStack>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
[Parameter]
public OrderSDto? Order { get; set; }
public OrderActionDialogBoxViewModel ViewModel { get; set; }
protected override async Task OnInitializedAsync()
{
if (Order == null)
ViewModel = new OrderActionDialogBoxViewModel(Snackbar, RestWrapper, UserUtility, DialogService, MudDialog);
else
ViewModel = new OrderActionDialogBoxViewModel(Snackbar, RestWrapper, UserUtility, DialogService, MudDialog, Order);
await ViewModel.InitializeAsync();
await base.OnInitializedAsync();
}
}

View File

@ -0,0 +1,106 @@
namespace NetinaShop.AdminPanel.PWA.Dialogs;
public class OrderActionDialogBoxViewModel : BaseViewModel<OrderLDto>
{
private readonly ISnackbar _snackbar;
private readonly IRestWrapper _restWrapper;
private readonly IUserUtility _userUtility;
private readonly IDialogService _dialogService;
private readonly MudDialogInstance _mudDialog;
public OrderActionDialogBoxViewModel(ISnackbar snackbar, IRestWrapper restWrapper, IUserUtility userUtility, IDialogService dialogService, MudDialogInstance mudDialog)
{
_snackbar = snackbar;
_restWrapper = restWrapper;
_userUtility = userUtility;
_dialogService = dialogService;
_mudDialog = mudDialog;
}
public OrderActionDialogBoxViewModel(ISnackbar snackbar,
IRestWrapper restWrapper,
IUserUtility userUtility,
IDialogService dialogService,
MudDialogInstance mudDialog,
OrderSDto order)
{
_snackbar = snackbar;
_restWrapper = restWrapper;
_userUtility = userUtility;
_dialogService = dialogService;
_mudDialog = mudDialog;
PageDto.Id = order.Id;
IsEditing = true;
}
public void Cancel() => _mudDialog.Cancel();
public bool IsEditing = false;
public override async Task InitializeAsync()
{
if (IsEditing && PageDto.Id != default)
{
try
{
var token = await _userUtility.GetBearerTokenAsync();
if (token == null)
throw new Exception("Token is null");
IsProcessing = true;
var order = await _restWrapper.OrderRestApi.ReadOne(PageDto.Id, token);
PageDto = order;
}
catch (ApiException ex)
{
var exe = await ex.GetContentAsAsync<ApiResult>();
if (exe != null)
_snackbar.Add(exe.Message, Severity.Error);
_snackbar.Add(ex.Content, Severity.Error);
_mudDialog.Cancel();
}
catch (Exception e)
{
_snackbar.Add(e.Message, Severity.Error);
_mudDialog.Cancel();
}
finally
{
IsProcessing = false;
}
};
}
public async Task SubmitEditAsync()
{
try
{
IsProcessing = true;
_snackbar.Add($"ویرایش سفارش {PageDto.FactorCode} با موفقیت انجام شد", Severity.Success);
_mudDialog.Close();
}
catch (ApiException ex)
{
var exe = await ex.GetContentAsAsync<ApiResult>();
if (exe != null)
_snackbar.Add(exe.Message, Severity.Error);
_snackbar.Add(ex.Content, Severity.Error);
_mudDialog.Cancel();
}
catch (Exception e)
{
_snackbar.Add(e.Message, Severity.Error);
_mudDialog.Cancel();
}
finally
{
IsProcessing = false;
}
}
}

View File

@ -1,14 +1,10 @@
@using Radzen.Blazor @using NetinaShop.AdminPanel.PWA.Extensions
@using NetinaShop.AdminPanel.PWA.Extensions
@inject ISnackbar Snackbar @inject ISnackbar Snackbar
@inject IRestWrapper RestWrapper @inject IRestWrapper RestWrapper
@inject IUserUtility UserUtility @inject IUserUtility UserUtility
@inject IDialogService DialogService @inject IDialogService DialogService
<head>
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
</head>
<MudDialog class="mx-auto"> <MudDialog class="mx-auto">
<DialogContent> <DialogContent>
@ -177,42 +173,6 @@
</MudItem> </MudItem>
</MudGrid> </MudGrid>
</div> </div>
<script type="text/javascript">
var myInterval = setInterval(()=>{
lunchEditor()
console.log("retry to load editor")
},50)
function lunchEditor() {
if (document.querySelector('.editor')) {
ClassicEditor
.create(document.querySelector('.editor'), {
// Editor configuration.
})
.then(editor => {
window.editor = editor;
clearInterval(myInterval)
console.info("Editor Lunched , Interval Cleared !")
})
.catch(handleSampleError);
}else return
}
function handleSampleError(error) {
const issueUrl = 'https://github.com/ckeditor/ckeditor5/issues';
const message = [
'Oops, something went wrong!',
`Please, report the following error on ${issueUrl} with the build id "pws0dnpd0jqj-zi42lsl7aqxa" and the error stack trace:`
].join('\n');
console.error(message);
console.error(error);
}
</script>
</MudTabPanel> </MudTabPanel>
<MudTabPanel Text="تـــــصاویر" Icon="@Icons.Material.Outlined.ImageSearch"> <MudTabPanel Text="تـــــصاویر" Icon="@Icons.Material.Outlined.ImageSearch">
@ -307,7 +267,7 @@
</MudContainer> </MudContainer>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<MudStack Row="true" class="w-full h-fit mx-4 bottom-0"> <MudStack Row="true" class="w-full h-fit mx-4 mb-3 bottom-0">
@if (ViewModel.IsEditing) @if (ViewModel.IsEditing)
{ {

View File

@ -80,7 +80,8 @@ public class ProductActionDialogBoxViewModel : BaseViewModel<ProductLDto>
try try
{ {
IsProcessing = true; IsProcessing = true;
var productLDto = await _restWrapper.CrudDtoApiRest<Product, ProductLDto, Guid>(Address.ProductController).ReadOne(_product.Id); var response = await _restWrapper.ProductRestApi.ReadOne(_product.Id);
var productLDto = response.Product;
PageDto.ExpertCheck = productLDto.ExpertCheck; PageDto.ExpertCheck = productLDto.ExpertCheck;
PageDto.Summery = productLDto.Summery; PageDto.Summery = productLDto.Summery;
PageDto.BeDisplayed = productLDto.BeDisplayed; PageDto.BeDisplayed = productLDto.BeDisplayed;

View File

@ -40,8 +40,8 @@
<MudLayout> <MudLayout>
<MudAppBar class="py-2" Color="Color.Transparent" Fixed="false" Elevation="2"> <MudAppBar class="py-2" Color="Color.Transparent" Fixed="false" Elevation="2">
<MudHidden Breakpoint="Breakpoint.SmAndUp"> <MudHidden Breakpoint="Breakpoint.MdAndUp">
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" /> <MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" OnClick="@ToggleDrawer" Edge="Edge.Start" />
</MudHidden> </MudHidden>
@* <MudAvatar Size="Size.Large" Variant="Variant.Outlined"> @* <MudAvatar Size="Size.Large" Variant="Variant.Outlined">
<MudImage Src="https://img.freepik.com/free-photo/portrait-white-man-isolated_53876-40306.jpg?size=626&ext=jpg&ga=GA1.1.632798143.1705708800&semt=ais"></MudImage> <MudImage Src="https://img.freepik.com/free-photo/portrait-white-man-isolated_53876-40306.jpg?size=626&ext=jpg&ga=GA1.1.632798143.1705708800&semt=ais"></MudImage>
@ -53,14 +53,18 @@
<MudText Color="Color.Inherit" Typo="Typo.caption">@_user?.PhoneNumber</MudText> <MudText Color="Color.Inherit" Typo="Typo.caption">@_user?.PhoneNumber</MudText>
</MudStack> </MudStack>
<MudSpacer /> <MudSpacer />
<MudIconButton Size="Size.Medium" Color="Color.Inherit" Icon="@Icons.Material.Outlined.Settings"/> @* <MudIconButton Size="Size.Medium" Color="Color.Inherit" Icon="@Icons.Material.Outlined.Settings"/> *@
<MudIconButton Size="Size.Medium" Color="Color.Error" OnClick="LogoutAsync" Icon="@Icons.Material.Outlined.ExitToApp" /> <MudIconButton Size="Size.Medium" Color="Color.Error" OnClick="LogoutAsync" Icon="@Icons.Material.Outlined.ExitToApp" />
</MudAppBar> </MudAppBar>
<MudDrawer @bind-Open="@open" Breakpoint="Breakpoint.MdAndUp" Elevation="1" Variant="@DrawerVariant.Responsive">
<SideBarUi/>
</MudDrawer>
<MudGrid Spacing="0"> <MudGrid Spacing="0">
<MudItem sm="0" md="3" lg="2"> <MudItem sm="0" md="3" lg="2">
<MudHidden Breakpoint="Breakpoint.SmAndDown"> <MudHidden Breakpoint="Breakpoint.MdAndDown">
<SideBarUi/> <SideBarUi/>
</MudHidden> </MudHidden>
</MudItem> </MudItem>
@ -73,7 +77,7 @@
</MudGrid> </MudGrid>
</MudLayout> </MudLayout>
<div dir="ltr"> <div dir="ltr">
<PWAUpdater StateChanged="Callback" Text="@_updateText" ButtonCaption="اپدیت کنید"/> <PWAUpdater Align="PWAUpdater.Aligns.Buttom" Text="@_updateText" ButtonCaption="اپدیت کنید"/>
</div> </div>
</MudRTLProvider> </MudRTLProvider>
@ -101,6 +105,8 @@
private string _updateText = "! نسخه جدید پنل ادمین رسید"; private string _updateText = "! نسخه جدید پنل ادمین رسید";
private ApplicationUserSDto? _user; private ApplicationUserSDto? _user;
bool open = false;
void ToggleDrawer() => open = !open;
private async Task LogoutAsync() private async Task LogoutAsync()
{ {
await UserUtility.LogoutAsync(); await UserUtility.LogoutAsync();
@ -118,11 +124,6 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_user = await UserUtility.GetUserAsync(); _user = await UserUtility.GetUserAsync();
await base.OnInitializedAsync(); await base.OnInitializedAsync();
} }
private void Callback(PWAUpdater.States obj)
{
}
} }

View File

@ -19,4 +19,8 @@ public static class Address
public static string DiscountController => $"{BaseAddress}/discount"; public static string DiscountController => $"{BaseAddress}/discount";
public static string RoleController => $"{BaseAddress}/user/role"; public static string RoleController => $"{BaseAddress}/user/role";
public static string ShippingController => $"{BaseAddress}/warehouse/shipping"; public static string ShippingController => $"{BaseAddress}/warehouse/shipping";
public static string OrderController => $"{BaseAddress}/order";
public static string PaymentController => $"{BaseAddress}/accounting/pay";
public static string PageController => $"{BaseAddress}/page";
public static string ScraperController => $"{BaseAddress}/scraper";
} }

View File

@ -0,0 +1,30 @@
namespace NetinaShop.AdminPanel.PWA.Models;
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
public class OrderProduct
{
public int Count { get; set; }
public string Id { get; set; }
}
public class OrderTest
{
public string FactorCode { get; set; }
public int TotalPrice { get; set; }
public int DeliveryPrice { get; set; }
public int TaxesPrice { get; set; }
public int ServicePrice { get; set; }
public int PackingPrice { get; set; }
public int TotalProductsPrice { get; set; }
public int DiscountPrice { get; set; }
public bool IsPayed { get; set; }
public int OrderStatus { get; set; }
public DateTime DoneAt { get; set; }
public DateTime OrderAt { get; set; }
public int PreparingMinute { get; set; }
public string DiscountCode { get; set; }
public List<OrderProduct> OrderProducts { get; set; }
public List<object> OrderDeliveries { get; set; }
public string Id { get; set; }
}

View File

@ -5,8 +5,9 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest> <ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
<AssemblyVersion>0.4.3.12</AssemblyVersion> <AssemblyVersion>0.6.12.22</AssemblyVersion>
<FileVersion>0.4.3.12</FileVersion> <FileVersion>0.6.12.22</FileVersion>
<AssemblyName>$(MSBuildProjectName)</AssemblyName>
</PropertyGroup> </PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
@ -38,10 +39,6 @@
<ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" /> <ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Utilities\" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Using Include="Blazored.LocalStorage" /> <Using Include="Blazored.LocalStorage" />
<Using Include="Microsoft.AspNetCore.Components" /> <Using Include="Microsoft.AspNetCore.Components" />

View File

@ -0,0 +1,66 @@
@page "/faqs"
@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">
<MudStack Row="true" class="mb-8">
<MudText Typo="Typo.h4">سوالات متداول فروشگاه من</MudText>
<MudSpacer />
<MudButton Variant="Variant.Outlined" Size="Size.Large" Color="Color.Success" OnClick="@ViewModel.SaveAsync">ذخیره سوالات</MudButton>
</MudStack>
<MudGrid>
<MudItem sm="4">
<MudTextField T="string" Label="سوال" @bind-Value="@ViewModel.Question" Variant="Variant.Outlined"></MudTextField>
</MudItem>
<MudItem sm="6">
<MudTextField T="string" Label="پاسخ" @bind-Value="@ViewModel.Answer" Variant="Variant.Outlined"></MudTextField>
</MudItem>
<MudItem sm="1">
<MudButton class="my-auto" EndIcon="@Icons.Material.Outlined.Add"
Variant="Variant.Outlined" Size="Size.Large" Color="Color.Info" OnClick="@ViewModel.AddNewQuestion">افزودن</MudButton>
</MudItem>
</MudGrid>
<MudPaper>
<MudExpansionPanels class="mt-8">
@foreach (var item in ViewModel.PageDto.Faqs)
{
<MudExpansionPanel>
<TitleContent>
<MudStack Row="true">
<MudIconButton Icon="@Icons.Material.Outlined.Delete"
Size="@Size.Small"
Variant="@Variant.Outlined"
Color="@Color.Error"
OnClick="()=>ViewModel.RemoveQuestion(item.Key)" />
<MudText>@item.Key</MudText>
</MudStack>
</TitleContent>
<ChildContent>
@item.Value
</ChildContent>
</MudExpansionPanel>
}
</MudExpansionPanels>
</MudPaper>
</MudItem>
</MudGrid>
</MudStack>
@code
{
public FaqManagementPageViewModel ViewModel { get; set; }
protected override async Task OnInitializedAsync()
{
ViewModel = new FaqManagementPageViewModel(NavigationManager, Snackbar, UserUtility, RestWrapper, DialogService);
await ViewModel.InitializeAsync();
await base.OnInitializedAsync();
}
}

View File

@ -0,0 +1,139 @@
using NetinaShop.Domain.Entities.Pages;
namespace NetinaShop.AdminPanel.PWA.Pages;
public class FaqManagementPageViewModel : BaseViewModel<FAQPage>
{
private readonly NavigationManager _navigationManager;
private readonly ISnackbar _snackbar;
private readonly IUserUtility _userUtility;
private readonly IDialogService _dialogService;
private readonly IRestWrapper _restWrapper;
public FaqManagementPageViewModel(NavigationManager navigationManager, ISnackbar snackbar, IUserUtility userUtility, IRestWrapper restWrapper, IDialogService dialogService)
{
_navigationManager = navigationManager;
_snackbar = snackbar;
_userUtility = userUtility;
_restWrapper = restWrapper;
_dialogService = dialogService;
}
public string Question { get; set; } = string.Empty;
public string Answer { get; set; } = string.Empty;
private PageActionRequestDto? _request;
public override async Task InitializeAsync()
{
try
{
var token = await _userUtility.GetBearerTokenAsync();
if (token == null)
throw new Exception("Token is null");
IsProcessing = true;
PageDto.Faqs.Clear();
var typeName = typeof(FAQPage).FullName;
var dto = await _restWrapper.PageRestApi.ReadByType(typeName, token);
if (!dto.Data.IsNullOrEmpty())
{
PageDto = dto.GetData<FAQPage>();
_request = new PageActionRequestDto
{
Name = dto.Name,
Content = dto.Content,
Description = dto.Description,
Id = dto.Id,
IsCustomPage = dto.IsCustomPage,
IsHtmlBasePage = dto.IsHtmlBasePage,
Slug = dto.Slug,
Type = typeof(FAQPage).FullName
};
}
}
catch (ApiException ex)
{
var exe = await ex.GetContentAsAsync<ApiResult>();
_snackbar.Add(exe != null ? exe.Message : ex.Content, Severity.Error);
}
catch (Exception e)
{
_snackbar.Add(e.Message, Severity.Error);
}
finally
{
IsProcessing = false;
}
await base.InitializeAsync();
}
public async Task SaveAsync()
{
try
{
var token = await _userUtility.GetBearerTokenAsync();
if (token == null)
throw new Exception("Token is null");
IsProcessing = true;
var request = new PageActionRequestDto
{
Name = "سوالات متداول",
Content = string.Empty,
Description = string.Empty,
Data = PageDto,
IsCustomPage = true,
IsHtmlBasePage = false,
Slug = "faq",
Type = typeof(FAQPage).FullName
};
if (_request != null)
{
request = _request;
}
request.Data = PageDto;
await _restWrapper.PageRestApi.CreatePage(request, token);
}
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 void AddNewQuestion()
{
try
{
if (Question.IsNullOrEmpty())
throw new Exception("سوال را وارد کنید");
if (Answer.IsNullOrEmpty())
throw new Exception("پاسخ را وارد کنید");
PageDto.Faqs.Add(Question,Answer);
}
catch (Exception e)
{
_snackbar.Add(e.Message, Severity.Error);
}
}
public void RemoveQuestion(string question)
{
try
{
PageDto.Faqs.Remove(question);
}
catch (Exception e)
{
_snackbar.Add(e.Message, Severity.Error);
}
}
}

View File

@ -44,34 +44,53 @@
<MudPaper> <MudPaper>
<MudDataGrid FixedFooter="true" FixedHeader="true" Striped="true" <MudDataGrid FixedFooter="true" FixedHeader="true" Striped="true"
T="BlogSDto" Items="@ViewModel.PageDto" CurrentPage="@ViewModel.CurrentPage" T="OrderSDto" Items="@ViewModel.MainOrders" CurrentPage="@ViewModel.MainGridCurrentPage"
RowsPerPage="20" Filterable="false" Loading="@ViewModel.IsProcessing" RowsPerPage="20" Filterable="false" Loading="@ViewModel.IsProcessing"
SortMode="@SortMode.None" Groupable="false"> SortMode="@SortMode.None" Groupable="false">
<ToolBarContent> <ToolBarContent>
<MudTextField T="string" Placeholder="جست جو بر اساس کد پیگیری" Adornment="Adornment.Start" Immediate="true" @* <MudTextField T="string" Placeholder="جست جو بر اساس کد پیگیری" Adornment="Adornment.Start" Immediate="true"
Clearable="true" Clearable="true"
ValueChanged="@ViewModel.SearchChanged" ValueChanged="@ViewModel.SearchChanged"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" class="my-auto" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" class="my-auto"
OnAdornmentClick="@ViewModel.SearchAsync"></MudTextField> OnAdornmentClick="@ViewModel.SearchAsync"></MudTextField> *@
</ToolBarContent> </ToolBarContent>
<Columns> <Columns>
<PropertyColumn Title="کد پیگیری" Property="arg => arg.Title"/> <PropertyColumn Title="کد سفارش" Property="arg => arg.FactorCode" />
<PropertyColumn Title="مبلغ" Property="arg => arg.CategoryName"/> <PropertyColumn Title="نام خریدار" Property="arg => arg.UserFullName" />
<PropertyColumn Title="تاریخ پرداخت" Property="arg => arg.ReadingTime"/> <TemplateColumn T="OrderSDto" Title="تاریخ سفارش">
<CellTemplate>
<p>@context.Item.OrderAt.ToPersianDateTime().ToLongDateString()</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn T="OrderSDto" Title="تاریخ پرداخت">
<CellTemplate>
<p>@context.Item.PayedAt.ToPersianDateTime().ToLongDateString()</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn T="OrderSDto" Title="مبلغ سفارش ">
<CellTemplate>
<p>@context.Item.TotalPrice.ToString("N0") ریالــ</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn T="OrderSDto" Title="وضعیت سفارش">
<CellTemplate>
<p>@context.Item.OrderStatus.ToDisplay()</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn CellClass="d-flex justify-end"> <TemplateColumn CellClass="d-flex justify-end">
<CellTemplate> <CellTemplate>
<MudStack Row="true"> <MudStack Row="true">
<MudIconButton Icon="@Icons.Material.Filled.Edit" <MudIconButton Icon="@Icons.Material.Filled.RemoveRedEye"
Size="@Size.Small" Size="@Size.Small"
Variant="@Variant.Outlined" Variant="@Variant.Outlined"
Color="@Color.Info" Color="@Color.Info"
OnClick="async () => await ViewModel.EditBlogClicked(context.Item)"/> OnClick="async () => await ViewModel.ShowClicked(context.Item)"/>
<MudIconButton Icon="@Icons.Material.Filled.Delete"
Size="@Size.Small"
Variant="@Variant.Outlined"
OnClick="async () => await ViewModel.DeleteBlogAsync(context.Item.Id)"
Color="@Color.Error"/>
</MudStack> </MudStack>
</CellTemplate> </CellTemplate>
</TemplateColumn> </TemplateColumn>
@ -79,7 +98,7 @@
<PagerContent> <PagerContent>
<MudStack Row="true" class="w-full"> <MudStack Row="true" class="w-full">
<MudPagination Rectangular="true" Variant="Variant.Filled" Count="@ViewModel.PageCount" <MudPagination Rectangular="true" Variant="Variant.Filled" Count="@ViewModel.MainGridPageCount"
SelectedChanged="@ViewModel.ChangePageAsync" class="my-4 mx-auto"/> SelectedChanged="@ViewModel.ChangePageAsync" class="my-4 mx-auto"/>
</MudStack> </MudStack>
@ -92,10 +111,10 @@
@code @code
{ {
public BlogsPageViewModel ViewModel { get; set; } public OrdersPageViewModel ViewModel { get; set; }
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
ViewModel = new BlogsPageViewModel(NavigationManager, Snackbar, UserUtility, RestWrapper, DialogService); ViewModel = new OrdersPageViewModel(NavigationManager, Snackbar, UserUtility, RestWrapper, DialogService);
await ViewModel.InitializeAsync(); await ViewModel.InitializeAsync();
await base.OnInitializedAsync(); await base.OnInitializedAsync();
} }

View File

@ -1,6 +1,109 @@
namespace NetinaShop.AdminPanel.PWA.Pages; namespace NetinaShop.AdminPanel.PWA.Pages;
public class OrdersPageViewModel public class OrdersPageViewModel : BaseViewModel
{ {
private readonly NavigationManager _navigationManager;
private readonly ISnackbar _snackbar;
private readonly IUserUtility _userUtility;
private readonly IDialogService _dialogService;
private readonly IRestWrapper _restWrapper;
public string Search = string.Empty;
public int MainGridCurrentPage = 0;
public int MainGridPageCount = 1;
public ObservableCollection<OrderSDto> MainOrders { get; } = new ObservableCollection<OrderSDto>();
public OrdersPageViewModel(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
{
var token = await _userUtility.GetBearerTokenAsync();
if (token == null)
throw new Exception("Token is null");
IsProcessing = true;
MainOrders.Clear();
var dto = await _restWrapper.OrderRestApi.ReadAll(MainGridCurrentPage, null, null, null,token);
dto.ForEach(d => MainOrders.Add(d));
if (MainOrders.Count == 15)
MainGridPageCount = 2;
}
catch (ApiException ex)
{
var exe = await ex.GetContentAsAsync<ApiResult>();
_snackbar.Add(exe != null ? exe.Message : ex.Content, Severity.Error);
}
catch (Exception e)
{
_snackbar.Add(e.Message, Severity.Error);
}
finally
{
IsProcessing = false;
}
await base.InitializeAsync();
}
public async Task ChangePageAsync(int page)
{
MainGridCurrentPage = page - 1;
if (MainGridCurrentPage > MainGridPageCount - 2)
{
try
{
IsProcessing = true;
var token = await _userUtility.GetBearerTokenAsync();
if (token == null)
throw new Exception("Token is null");
var dto = await _restWrapper.OrderRestApi.ReadAll(MainGridCurrentPage, null, null, null, token);
dto.ForEach(d => MainOrders.Add(d));
if (MainOrders.Count == 15)
MainGridPageCount = MainGridCurrentPage + 2;
}
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 ShowClicked(OrderSDto order)
{
DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Large, NoHeader = true, FullWidth = true, DisableBackdropClick = true };
var parameters = new DialogParameters<OrderActionDialogBox>();
parameters.Add(x => x.Order, order);
var dialogResult = await _dialogService.ShowAsync<OrderActionDialogBox>($" سفارش {order.FactorCode}", parameters, maxWidth);
var result = await dialogResult.Result;
if (!result.Canceled && result.Data is bool and true)
{
await InitializeAsync();
}
}
} }

View File

@ -0,0 +1,95 @@
@page "/pages"
@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">
<MudStack Row="true" class="mb-5">
<MudText Typo="Typo.h4">پرداختـــ ها</MudText>
<MudSpacer />
</MudStack>
<MudPaper>
<MudDataGrid FixedFooter="true" FixedHeader="true" Striped="true"
T="PaymentSDto" Items="@ViewModel.PageDto" CurrentPage="@ViewModel.CurrentPage"
RowsPerPage="20" Filterable="false" Loading="@ViewModel.IsProcessing"
SortMode="@SortMode.None" Groupable="false">
@* <ToolBarContent>
<MudTextField T="string" Placeholder="جست جو بر اساس کد پیگیری" Adornment="Adornment.Start" Immediate="true"
Clearable="true"
ValueChanged="@ViewModel.SearchChanged"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" class="my-auto"
OnAdornmentClick="@ViewModel.SearchAsync"></MudTextField>
</ToolBarContent> *@
<Columns>
<PropertyColumn Title="شماره فاکتور" Property="arg => arg.FactorNumber" />
<PropertyColumn Title="پرداخت کننده" Property="arg => arg.UserFullName" />
<PropertyColumn Title="شماره تماس" Property="arg => arg.UserPhoneNumber" />
<TemplateColumn T="PaymentSDto" Title="نوع پرداخت">
<CellTemplate>
<p>@context.Item.Type.ToDisplay()</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn T="PaymentSDto" Title="وظعیت پرداخت">
<CellTemplate>
<p>@context.Item.Status.ToDisplay()</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn T="PaymentSDto" Title="مبلغ سفارش ">
<CellTemplate>
<p>@context.Item.Amount.ToString("N0") ریالــ</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn T="PaymentSDto" Title="تاریخ پرداخت">
<CellTemplate>
<p>@context.Item.CreatedAt.ToPersianDateTime().ToLongDateString()</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn CellClass="d-flex justify-end">
<CellTemplate>
<MudStack Row="true">
<MudIconButton Icon="@Icons.Material.Filled.PanoramaFishEye"
Size="@Size.Small"
Variant="@Variant.Outlined"
Color="@Color.Info"
OnClick="async()=>await ViewModel.ShowClicked(context.Item)" />
</MudStack>
</CellTemplate>
</TemplateColumn>
</Columns>
<PagerContent>
<MudStack Row="true" class="w-full">
<MudPagination Rectangular="true" Variant="Variant.Filled" Count="@ViewModel.PageCount"
SelectedChanged="@ViewModel.ChangePageAsync" class="my-4 mx-auto" />
</MudStack>
</PagerContent>
</MudDataGrid>
</MudPaper>
</MudItem>
</MudGrid>
</MudStack>
@code
{
public PaymentsPageViewModel ViewModel { get; set; }
protected override async Task OnInitializedAsync()
{
ViewModel = new PaymentsPageViewModel(NavigationManager, Snackbar, UserUtility, RestWrapper, DialogService);
await ViewModel.InitializeAsync();
await base.OnInitializedAsync();
}
}

View File

@ -0,0 +1,6 @@
namespace NetinaShop.AdminPanel.PWA.Pages;
public class PagesManagementPageViewModel
{
}

View File

@ -16,34 +16,55 @@
</MudStack> </MudStack>
<MudPaper> <MudPaper>
<MudDataGrid FixedFooter="true" FixedHeader="true" Striped="true" <MudDataGrid FixedFooter="true" FixedHeader="true" Striped="true"
T="BlogSDto" Items="@ViewModel.PageDto" CurrentPage="@ViewModel.CurrentPage" T="PaymentSDto" Items="@ViewModel.PageDto" CurrentPage="@ViewModel.CurrentPage"
RowsPerPage="20" Filterable="false" Loading="@ViewModel.IsProcessing" RowsPerPage="20" Filterable="false" Loading="@ViewModel.IsProcessing"
SortMode="@SortMode.None" Groupable="false"> SortMode="@SortMode.None" Groupable="false">
<ToolBarContent> @* <ToolBarContent>
<MudTextField T="string" Placeholder="جست جو بر اساس کد پیگیری" Adornment="Adornment.Start" Immediate="true" <MudTextField T="string" Placeholder="جست جو بر اساس کد پیگیری" Adornment="Adornment.Start" Immediate="true"
Clearable="true" Clearable="true"
ValueChanged="@ViewModel.SearchChanged" ValueChanged="@ViewModel.SearchChanged"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" class="my-auto" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" class="my-auto"
OnAdornmentClick="@ViewModel.SearchAsync"></MudTextField> OnAdornmentClick="@ViewModel.SearchAsync"></MudTextField>
</ToolBarContent> </ToolBarContent> *@
<Columns> <Columns>
<PropertyColumn Title="کد پیگیری" Property="arg => arg.Title" /> <PropertyColumn Title="شماره فاکتور" Property="arg => arg.FactorNumber" />
<PropertyColumn Title="مبلغ" Property="arg => arg.CategoryName" /> <PropertyColumn Title="پرداخت کننده" Property="arg => arg.UserFullName" />
<PropertyColumn Title="تاریخ پرداخت" Property="arg => arg.ReadingTime" /> <PropertyColumn Title="شماره تماس" Property="arg => arg.UserPhoneNumber" />
<TemplateColumn T="PaymentSDto" Title="نوع پرداخت">
<CellTemplate>
<p>@context.Item.Type.ToDisplay()</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn T="PaymentSDto" Title="وظعیت پرداخت">
<CellTemplate>
<p>@context.Item.Status.ToDisplay()</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn T="PaymentSDto" Title="مبلغ سفارش ">
<CellTemplate>
<p>@context.Item.Amount.ToString("N0") ریالــ</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn T="PaymentSDto" Title="تاریخ پرداخت">
<CellTemplate>
<p>@context.Item.CreatedAt.ToPersianDateTime().ToLongDateString()</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn CellClass="d-flex justify-end"> <TemplateColumn CellClass="d-flex justify-end">
<CellTemplate> <CellTemplate>
<MudStack Row="true"> <MudStack Row="true">
<MudIconButton Icon="@Icons.Material.Filled.Edit" <MudIconButton Icon="@Icons.Material.Filled.PanoramaFishEye"
Size="@Size.Small" Size="@Size.Small"
Variant="@Variant.Outlined" Variant="@Variant.Outlined"
Color="@Color.Info" Color="@Color.Info"
OnClick="async()=>await ViewModel.EditBlogClicked(context.Item)" /> OnClick="async()=>await ViewModel.ShowClicked(context.Item)" />
<MudIconButton Icon="@Icons.Material.Filled.Delete"
Size="@Size.Small"
Variant="@Variant.Outlined"
OnClick="async () => await ViewModel.DeleteBlogAsync(context.Item.Id)"
Color="@Color.Error" />
</MudStack> </MudStack>
</CellTemplate> </CellTemplate>
</TemplateColumn> </TemplateColumn>
@ -64,10 +85,10 @@
@code @code
{ {
public BlogsPageViewModel ViewModel { get; set; } public PaymentsPageViewModel ViewModel { get; set; }
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
ViewModel = new BlogsPageViewModel(NavigationManager, Snackbar, UserUtility, RestWrapper, DialogService); ViewModel = new PaymentsPageViewModel(NavigationManager, Snackbar, UserUtility, RestWrapper, DialogService);
await ViewModel.InitializeAsync(); await ViewModel.InitializeAsync();
await base.OnInitializedAsync(); await base.OnInitializedAsync();
} }

View File

@ -1,6 +1,105 @@
namespace NetinaShop.AdminPanel.PWA.Pages; namespace NetinaShop.AdminPanel.PWA.Pages;
public class PaymentsPageViewModel public class PaymentsPageViewModel : BaseViewModel<ObservableCollection<PaymentSDto>>
{ {
private readonly NavigationManager _navigationManager;
private readonly ISnackbar _snackbar;
private readonly IUserUtility _userUtility;
private readonly IDialogService _dialogService;
private readonly IRestWrapper _restWrapper;
public string Search = string.Empty;
public int CurrentPage = 0;
public int PageCount = 1;
public PaymentsPageViewModel(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
{
var token = await _userUtility.GetBearerTokenAsync();
if (token == null)
throw new Exception("Token is null");
IsProcessing = true;
PageDto.Clear();
var dto = await _restWrapper.PaymentRestApi.ReadAll(CurrentPage, token);
dto.ForEach(d => PageDto.Add(d));
if (PageDto.Count == 20)
PageCount = 2;
}
catch (ApiException ex)
{
var exe = await ex.GetContentAsAsync<ApiResult>();
_snackbar.Add(exe != null ? exe.Message : ex.Content, Severity.Error);
}
catch (Exception e)
{
_snackbar.Add(e.Message, Severity.Error);
}
finally
{
IsProcessing = false;
}
await base.InitializeAsync();
}
public async Task ChangePageAsync(int page)
{
CurrentPage = page - 1;
if (CurrentPage > PageCount - 2)
{
try
{
IsProcessing = true;
var token = await _userUtility.GetBearerTokenAsync();
if (token == null)
throw new Exception("Token is null");
var dto = await _restWrapper.PaymentRestApi.ReadAll(CurrentPage, token);
dto.ForEach(d => PageDto.Add(d));
if (PageDto.Count == 20)
PageCount = CurrentPage + 2;
}
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 ShowClicked(PaymentSDto order)
{
//DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Large, NoHeader = true, FullWidth = true, DisableBackdropClick = true };
//var parameters = new DialogParameters<OrderActionDialogBox>();
//parameters.Add(x => x.Order, order);
//var dialogResult = await _dialogService.ShowAsync<OrderActionDialogBox>($" سفارش {order.FactorCode}", parameters, maxWidth);
//var result = await dialogResult.Result;
//if (!result.Canceled && result.Data is bool and true)
//{
// await InitializeAsync();
//}
}
} }

View File

@ -20,6 +20,12 @@
Color="Color.Secondary" Color="Color.Secondary"
OnClick="@ViewModel.AddProductClicked" OnClick="@ViewModel.AddProductClicked"
class="my-auto">افزودن محصول</MudButton> class="my-auto">افزودن محصول</MudButton>
@* <MudButton Variant="Variant.Filled"
DisableElevation="true"
StartIcon="@Icons.Material.Outlined.Add"
Color="Color.Error"
OnClick="@ViewModel.AddDigikalaProductClicked"
class="my-auto">افزودن محصول از دیجیکالا</MudButton> *@
</MudStack> </MudStack>
<MudPaper> <MudPaper>
<MudDataGrid FixedFooter="true" FixedHeader="true" Striped="true" <MudDataGrid FixedFooter="true" FixedHeader="true" Striped="true"

View File

@ -1,4 +1,5 @@
using MudBlazor; using MudBlazor;
using NetinaShop.Domain.Dtos.ResponseDtos;
namespace NetinaShop.AdminPanel.PWA.Pages; namespace NetinaShop.AdminPanel.PWA.Pages;
@ -30,7 +31,7 @@ public class ProductsPageViewModel : BaseViewModel<ObservableCollection<ProductS
IsProcessing = true; IsProcessing = true;
PageDto.Clear(); PageDto.Clear();
var dto = await _restWrapper.ProductRestApi.ReadAll(CurrentPage); var dto = await _restWrapper.ProductRestApi.ReadAll(CurrentPage);
dto.ForEach(d => PageDto.Add(d)); dto.Products.ForEach(d => PageDto.Add(d));
if (PageDto.Count == 20) if (PageDto.Count == 20)
PageCount = 2; PageCount = 2;
} }
@ -61,18 +62,17 @@ public class ProductsPageViewModel : BaseViewModel<ObservableCollection<ProductS
{ {
IsProcessing = true; IsProcessing = true;
List<ProductSDto> dto = new List<ProductSDto>(); GetProductsResponseDto dto = new GetProductsResponseDto();
if (Search.IsNullOrEmpty()) if (Search.IsNullOrEmpty())
{ {
dto = await _restWrapper.CrudDtoApiRest<Product, ProductSDto, Guid>(Address.ProductController) dto = await _restWrapper.ProductRestApi.ReadAll(CurrentPage);
.ReadAll(CurrentPage);
} }
else else
{ {
dto = await _restWrapper.ProductRestApi.ReadAll(CurrentPage, Search); dto = await _restWrapper.ProductRestApi.ReadAll(CurrentPage, Search);
} }
dto.ForEach(d => PageDto.Add(d)); dto.Products.ForEach(d => PageDto.Add(d));
if (PageDto.Count % 20 == 0) if (PageDto.Count % 20 == 0)
PageCount = CurrentPage + 2; PageCount = CurrentPage + 2;
@ -96,7 +96,7 @@ public class ProductsPageViewModel : BaseViewModel<ObservableCollection<ProductS
public async Task AddProductClicked() public async Task AddProductClicked()
{ {
DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, DisableBackdropClick = true }; DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Large, FullWidth = true, DisableBackdropClick = true };
var dialogResult = await _dialogService.ShowAsync<ProductActionDialogBox>("افزودن محصول جدید", maxWidth); var dialogResult = await _dialogService.ShowAsync<ProductActionDialogBox>("افزودن محصول جدید", maxWidth);
var result = await dialogResult.Result; var result = await dialogResult.Result;
if (!result.Canceled && result.Data is bool and true) if (!result.Canceled && result.Data is bool and true)
@ -105,9 +105,21 @@ public class ProductsPageViewModel : BaseViewModel<ObservableCollection<ProductS
} }
} }
public async Task AddDigikalaProductClicked()
{
DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, DisableBackdropClick = true };
var dialogResult = await _dialogService.ShowAsync<DigikalaProductActionDialogBox>("افزودن محصول جدید", maxWidth);
var result = await dialogResult.Result;
if (!result.Canceled && result.Data is bool and true)
{
await InitializeAsync();
}
}
public async Task EditProductClicked(ProductSDto product) public async Task EditProductClicked(ProductSDto product)
{ {
DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, DisableBackdropClick = true }; DialogOptions maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Large, FullWidth = true, DisableBackdropClick = true };
var parameters = new DialogParameters<ProductActionDialogBox>(); var parameters = new DialogParameters<ProductActionDialogBox>();
parameters.Add(x => x.Product, product); parameters.Add(x => x.Product, product);
var dialogResult = await _dialogService.ShowAsync<ProductActionDialogBox>($"ویرایش محصول {product.PersianName}", parameters, maxWidth); var dialogResult = await _dialogService.ShowAsync<ProductActionDialogBox>($"ویرایش محصول {product.PersianName}", parameters, maxWidth);
@ -173,7 +185,7 @@ public class ProductsPageViewModel : BaseViewModel<ObservableCollection<ProductS
PageCount = 1; PageCount = 1;
PageDto.Clear(); PageDto.Clear();
var dto = await _restWrapper.ProductRestApi.ReadAll(CurrentPage, Search); var dto = await _restWrapper.ProductRestApi.ReadAll(CurrentPage, Search);
dto.ForEach(d => PageDto.Add(d)); dto.Products.ForEach(d => PageDto.Add(d));
if (PageDto.Count == 20) if (PageDto.Count == 20)
PageCount = 2; PageCount = 2;
} }

View File

@ -28,7 +28,13 @@
<Columns> <Columns>
<PropertyColumn Title="نام" Property="arg => arg.Name" /> <PropertyColumn Title="نام" Property="arg => arg.Name" />
<PropertyColumn Title="نام انبار ارسال" Property="arg => arg.WarehouseName" /> <PropertyColumn Title="نام انبار ارسال" Property="arg => arg.WarehouseName" />
<PropertyColumn Title="هرینه ارسال" Property="arg => arg.DeliveryCost" />
<TemplateColumn T="ShippingSDto" Title="هزینه ارسال">
<CellTemplate>
<p>@context.Item.DeliveryCost.ToString("N0") ریالــ</p>
</CellTemplate>
</TemplateColumn>
<TemplateColumn T="ShippingSDto" Title="ارسال فوری می باشد"> <TemplateColumn T="ShippingSDto" Title="ارسال فوری می باشد">
<CellTemplate> <CellTemplate>
@if (@context.Item.IsExpressShipping) @if (@context.Item.IsExpressShipping)

View File

@ -0,0 +1,10 @@
namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
public interface IOrderRestApi
{
[Get("")]
Task<List<OrderSDto>> ReadAll([Query]int page, [Query] long? selectedDate, [Query] OrderStatus? orderStatus, [Query] OrderQueryDateFilter? dateFilter, [Header("Authorization")] string authorization);
[Get("/{id}")]
Task<OrderLDto> ReadOne(Guid id, [Header("Authorization")] string authorization);
}

View File

@ -0,0 +1,14 @@
namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
public interface IPageRestApi
{
[Get("/type/{type}")]
Task<BasePageEntitySDto> ReadByType([Query] string type, [Header("Authorization")] string authorization);
[Get("/{id}")]
Task<BasePageEntitySDto> ReadById(Guid id, [Header("Authorization")] string authorization);
[Post("")]
Task CreatePage([Body] PageActionRequestDto request, [Header("Authorization")] string authorization);
}

View File

@ -0,0 +1,8 @@
namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
public interface IPaymentRestApi
{
[Get("")]
Task<List<PaymentSDto>> ReadAll([Query] int page, [Header("Authorization")] string authorization);
}

View File

@ -1,14 +1,18 @@
namespace NetinaShop.AdminPanel.PWA.Services.RestServices; using NetinaShop.Domain.Dtos.ResponseDtos;
namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
public interface IProductRestApi public interface IProductRestApi
{ {
[Get("")] [Get("")]
Task<List<ProductSDto>> ReadAll(); Task<GetProductsResponseDto> ReadAll();
[Get("/{productId}")]
Task<GetProductResponseDto> ReadOne(Guid productId);
[Get("")] [Get("")]
Task<List<ProductSDto>> ReadAll([Query] int page); Task<GetProductsResponseDto> ReadAll([Query] int page);
[Get("")] [Get("")]
Task<List<ProductSDto>> ReadAll([Query] int page, [Query] string productName); Task<GetProductsResponseDto> ReadAll([Query] int page, [Query] string productName);
[Get("")] [Get("")]
Task<List<ProductSDto>> ReadAll([Query] string productName); Task<GetProductsResponseDto> ReadAll([Query] string productName);
} }

View File

@ -16,4 +16,8 @@ public interface IRestWrapper
public IDiscountRestApi DiscountRest { get; } public IDiscountRestApi DiscountRest { get; }
public IBlogCategoryRestApi BlogCategoryRestApi { get; } public IBlogCategoryRestApi BlogCategoryRestApi { get; }
public IRoleRestApi RoleRestApi { get; } public IRoleRestApi RoleRestApi { get; }
public IOrderRestApi OrderRestApi { get; }
public IPaymentRestApi PaymentRestApi { get; }
public IPageRestApi PageRestApi { get; }
public IScraperRestApi ScraperRestApi { get; }
} }

View File

@ -0,0 +1,11 @@
using NetinaShop.Domain.Dtos.ScraperDtos.Response;
namespace NetinaShop.AdminPanel.PWA.Services.RestServices;
public interface IScraperRestApi
{
[Get("/digi")]
Task<List<ScraperProductDto>> SearchDigiProductsAsync([Query] string productName, [Header("Authorization")]string authorization);
[Post("/digi/{productId}")]
Task<bool> AddDigiProductToShopAsync(string productId, [Query] string productName, [Header("Authorization")] string authorization);
}

View File

@ -27,5 +27,9 @@ public class RestWrapper : IRestWrapper
public IBlogRestApi BlogRestApi => RestService.For<IBlogRestApi>(Address.BlogController, setting); public IBlogRestApi BlogRestApi => RestService.For<IBlogRestApi>(Address.BlogController, setting);
public IDiscountRestApi DiscountRest => RestService.For<IDiscountRestApi>(Address.DiscountController, setting); public IDiscountRestApi DiscountRest => RestService.For<IDiscountRestApi>(Address.DiscountController, setting);
public IBlogCategoryRestApi BlogCategoryRestApi => RestService.For<IBlogCategoryRestApi>(Address.BlogCategoryController, setting); public IBlogCategoryRestApi BlogCategoryRestApi => RestService.For<IBlogCategoryRestApi>(Address.BlogCategoryController, setting);
public IRoleRestApi RoleRestApi => RestService.For<IRoleRestApi>(Address.RoleController); public IRoleRestApi RoleRestApi => RestService.For<IRoleRestApi>(Address.RoleController, setting);
public IOrderRestApi OrderRestApi => RestService.For<IOrderRestApi>(Address.OrderController, setting);
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);
} }

View File

@ -0,0 +1,37 @@
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

@ -1084,6 +1084,10 @@ input:checked + .toggle-bg {
margin-top: 2.5rem; margin-top: 2.5rem;
margin-bottom: 2.5rem; margin-bottom: 2.5rem;
} }
.my-2 {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.my-4 { .my-4 {
margin-top: 1rem; margin-top: 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
@ -1128,6 +1132,9 @@ input:checked + .toggle-bg {
.mb-5 { .mb-5 {
margin-bottom: 1.25rem; margin-bottom: 1.25rem;
} }
.mb-8 {
margin-bottom: 2rem;
}
.mr-1 { .mr-1 {
margin-right: 0.25rem; margin-right: 0.25rem;
} }
@ -1158,6 +1165,9 @@ input:checked + .toggle-bg {
.mt-6 { .mt-6 {
margin-top: 1.5rem; margin-top: 1.5rem;
} }
.mt-8 {
margin-top: 2rem;
}
.line-clamp-1 { .line-clamp-1 {
overflow: hidden; overflow: hidden;
display: -webkit-box; display: -webkit-box;
@ -1224,6 +1234,9 @@ input:checked + .toggle-bg {
.h-96 { .h-96 {
height: 24rem; height: 24rem;
} }
.h-\[35rem\] {
height: 35rem;
}
.h-fit { .h-fit {
height: -moz-fit-content; height: -moz-fit-content;
height: fit-content; height: fit-content;
@ -1237,6 +1250,9 @@ input:checked + .toggle-bg {
.max-h-\[30rem\] { .max-h-\[30rem\] {
max-height: 30rem; max-height: 30rem;
} }
.max-h-\[40rem\] {
max-height: 40rem;
}
.min-h-\[28rem\] { .min-h-\[28rem\] {
min-height: 28rem; min-height: 28rem;
} }
@ -1429,6 +1445,9 @@ input:checked + .toggle-bg {
.border-0 { .border-0 {
border-width: 0px; border-width: 0px;
} }
.border-2 {
border-width: 2px;
}
.border-4 { .border-4 {
border-width: 4px; border-width: 4px;
} }
@ -1441,6 +1460,9 @@ input:checked + .toggle-bg {
.border-solid { .border-solid {
border-style: solid; border-style: solid;
} }
.border-dashed {
border-style: dashed;
}
.border-blue-500 { .border-blue-500 {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(63 131 248 / var(--tw-border-opacity)); border-color: rgb(63 131 248 / var(--tw-border-opacity));
@ -1469,6 +1491,10 @@ input:checked + .toggle-bg {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(104 117 245 / var(--tw-border-opacity)); border-color: rgb(104 117 245 / var(--tw-border-opacity));
} }
.border-violet-400 {
--tw-border-opacity: 1;
border-color: rgb(167 139 250 / var(--tw-border-opacity));
}
.bg-\[\#000000\] { .bg-\[\#000000\] {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(0 0 0 / var(--tw-bg-opacity)); background-color: rgb(0 0 0 / var(--tw-bg-opacity));
@ -1556,6 +1582,10 @@ input:checked + .toggle-bg {
padding-left: 0.5rem; padding-left: 0.5rem;
padding-right: 0.5rem; padding-right: 0.5rem;
} }
.px-3 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
.px-4 { .px-4 {
padding-left: 1rem; padding-left: 1rem;
padding-right: 1rem; padding-right: 1rem;
@ -1572,6 +1602,10 @@ input:checked + .toggle-bg {
padding-top: 0.125rem; padding-top: 0.125rem;
padding-bottom: 0.125rem; padding-bottom: 0.125rem;
} }
.py-1 {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.py-2 { .py-2 {
padding-top: 0.5rem; padding-top: 0.5rem;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
@ -1600,6 +1634,9 @@ input:checked + .toggle-bg {
.text-justify { .text-justify {
text-align: justify; text-align: justify;
} }
.align-bottom {
vertical-align: bottom;
}
.text-5xl { .text-5xl {
font-size: 3rem; font-size: 3rem;
} }
@ -1673,6 +1710,10 @@ input:checked + .toggle-bg {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(225 29 72 / var(--tw-text-opacity)); color: rgb(225 29 72 / var(--tw-text-opacity));
} }
.text-violet-500 {
--tw-text-opacity: 1;
color: rgb(139 92 246 / var(--tw-text-opacity));
}
.text-white { .text-white {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity)); color: rgb(255 255 255 / var(--tw-text-opacity));

View File

@ -1165,6 +1165,11 @@ input:checked + .toggle-bg {
margin-bottom: 2.5rem; margin-bottom: 2.5rem;
} }
.my-2 {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.my-4 { .my-4 {
margin-top: 1rem; margin-top: 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
@ -1223,6 +1228,10 @@ input:checked + .toggle-bg {
margin-bottom: 1.25rem; margin-bottom: 1.25rem;
} }
.mb-8 {
margin-bottom: 2rem;
}
.mr-1 { .mr-1 {
margin-right: 0.25rem; margin-right: 0.25rem;
} }
@ -1263,6 +1272,10 @@ input:checked + .toggle-bg {
margin-top: 1.5rem; margin-top: 1.5rem;
} }
.mt-8 {
margin-top: 2rem;
}
.line-clamp-1 { .line-clamp-1 {
overflow: hidden; overflow: hidden;
display: -webkit-box; display: -webkit-box;
@ -1350,6 +1363,10 @@ input:checked + .toggle-bg {
height: 24rem; height: 24rem;
} }
.h-\[35rem\] {
height: 35rem;
}
.h-fit { .h-fit {
height: -moz-fit-content; height: -moz-fit-content;
height: fit-content; height: fit-content;
@ -1367,6 +1384,10 @@ input:checked + .toggle-bg {
max-height: 30rem; max-height: 30rem;
} }
.max-h-\[40rem\] {
max-height: 40rem;
}
.min-h-\[28rem\] { .min-h-\[28rem\] {
min-height: 28rem; min-height: 28rem;
} }
@ -1617,6 +1638,10 @@ input:checked + .toggle-bg {
border-width: 0px; border-width: 0px;
} }
.border-2 {
border-width: 2px;
}
.border-4 { .border-4 {
border-width: 4px; border-width: 4px;
} }
@ -1633,6 +1658,10 @@ input:checked + .toggle-bg {
border-style: solid; border-style: solid;
} }
.border-dashed {
border-style: dashed;
}
.border-blue-500 { .border-blue-500 {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(63 131 248 / var(--tw-border-opacity)); border-color: rgb(63 131 248 / var(--tw-border-opacity));
@ -1668,6 +1697,11 @@ input:checked + .toggle-bg {
border-color: rgb(104 117 245 / var(--tw-border-opacity)); border-color: rgb(104 117 245 / var(--tw-border-opacity));
} }
.border-violet-400 {
--tw-border-opacity: 1;
border-color: rgb(167 139 250 / var(--tw-border-opacity));
}
.bg-\[\#000000\] { .bg-\[\#000000\] {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(0 0 0 / var(--tw-bg-opacity)); background-color: rgb(0 0 0 / var(--tw-bg-opacity));
@ -1780,6 +1814,11 @@ input:checked + .toggle-bg {
padding-right: 0.5rem; padding-right: 0.5rem;
} }
.px-3 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
.px-4 { .px-4 {
padding-left: 1rem; padding-left: 1rem;
padding-right: 1rem; padding-right: 1rem;
@ -1800,6 +1839,11 @@ input:checked + .toggle-bg {
padding-bottom: 0.125rem; padding-bottom: 0.125rem;
} }
.py-1 {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.py-2 { .py-2 {
padding-top: 0.5rem; padding-top: 0.5rem;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
@ -1836,6 +1880,10 @@ input:checked + .toggle-bg {
text-align: justify; text-align: justify;
} }
.align-bottom {
vertical-align: bottom;
}
.text-5xl { .text-5xl {
font-size: 3rem; font-size: 3rem;
} }
@ -1930,6 +1978,11 @@ input:checked + .toggle-bg {
color: rgb(225 29 72 / var(--tw-text-opacity)); color: rgb(225 29 72 / var(--tw-text-opacity));
} }
.text-violet-500 {
--tw-text-opacity: 1;
color: rgb(139 92 246 / var(--tw-text-opacity));
}
.text-white { .text-white {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity)); color: rgb(255 255 255 / var(--tw-text-opacity));

View File

@ -5,7 +5,9 @@ self.importScripts('./service-worker-assets.js');
self.addEventListener('install', event => event.waitUntil(onInstall(event))); self.addEventListener('install', event => event.waitUntil(onInstall(event)));
self.addEventListener('activate', event => event.waitUntil(onActivate(event))); self.addEventListener('activate', event => event.waitUntil(onActivate(event)));
self.addEventListener('fetch', event => event.respondWith(onFetch(event))); self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
self.addEventListener('message', event => {
if (event.data?.type === 'SKIP_WAITING') self.skipWaiting();
});
const cacheNamePrefix = 'offline-cache-'; const cacheNamePrefix = 'offline-cache-';
const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`; const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ]; const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ];