feat: Implement role-based access control and manual payment management
- Added role-based permission checks in NavMenu for dashboard and management links. - Created ManualPaymentDialog for creating and managing manual payments. - Implemented TransactionDetailsDialog for displaying transaction details. - Developed Transactions page for viewing and filtering transactions. - Introduced RolePermissionsDialog for managing role permissions. - Established AuthorizationService to handle permission checks and user roles. - Enhanced UI components with MudBlazor for better user experience.
This commit is contained in:
@@ -59,6 +59,7 @@ public static class ConfigureServices
|
|||||||
services.AddGrpcServices(configuration);
|
services.AddGrpcServices(configuration);
|
||||||
|
|
||||||
// Application Services
|
// Application Services
|
||||||
|
services.AddScoped<BackOffice.Services.Authorization.IAuthorizationService, BackOffice.Services.Authorization.AuthorizationService>();
|
||||||
services.AddScoped<IDiscountProductService, DiscountProductService>();
|
services.AddScoped<IDiscountProductService, DiscountProductService>();
|
||||||
services.AddScoped<IDiscountCategoryService, DiscountCategoryService>();
|
services.AddScoped<IDiscountCategoryService, DiscountCategoryService>();
|
||||||
services.AddScoped<IDiscountOrderService, DiscountOrderService>();
|
services.AddScoped<IDiscountOrderService, DiscountOrderService>();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<MudText Typo="Typo.h6">لیست اعضا</MudText>
|
<MudText Typo="Typo.h6">لیست اعضا</MudText>
|
||||||
<MudSpacer />
|
<MudSpacer />
|
||||||
<MudStack Row="true" Spacing="2">
|
<MudStack Row="true" Spacing="2">
|
||||||
<MudSwitch @bind-Value="_filterIsActive"
|
<MudSwitch T="bool" @bind-Value="_filterIsActive"
|
||||||
Color="Color.Success"
|
Color="Color.Success"
|
||||||
Label="فقط فعالها" />
|
Label="فقط فعالها" />
|
||||||
<MudButton Variant="Variant.Filled"
|
<MudButton Variant="Variant.Filled"
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
@using BackOffice.BFF.ManualPayment.Protobuf
|
||||||
|
@using BackOffice.Pages.Payment.Components
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
@if (Mode == ManualPaymentDialogMode.Create)
|
||||||
|
{
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudText Typo="Typo.h6">ثبت پرداخت دستی جدید</MudText>
|
||||||
|
|
||||||
|
<MudNumericField T="long"
|
||||||
|
Label="شناسه کاربر"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
@bind-Value="_createModel.UserId" />
|
||||||
|
|
||||||
|
<MudNumericField T="long"
|
||||||
|
Label="مبلغ"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Adornment="Adornment.End"
|
||||||
|
AdornmentText="ریال"
|
||||||
|
@bind-Value="_createModel.Amount" />
|
||||||
|
|
||||||
|
<MudTextField T="string"
|
||||||
|
Label="نوع / توضیح نوع"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
@bind-Value="_createModel.TypeDisplay" />
|
||||||
|
|
||||||
|
<MudTextField T="string"
|
||||||
|
Label="توضیحات"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Lines="3"
|
||||||
|
@bind-Value="_createModel.Description" />
|
||||||
|
|
||||||
|
<MudTextField T="string"
|
||||||
|
Label="شماره مرجع (اختیاری)"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
@bind-Value="_createModel.ReferenceNumber" />
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
else if (Model is not null)
|
||||||
|
{
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudText Typo="Typo.h6">جزئیات پرداخت دستی</MudText>
|
||||||
|
<MudText Typo="Typo.body2">شناسه: @Model.Id</MudText>
|
||||||
|
<MudText Typo="Typo.body2">کاربر: @Model.UserFullName (@Model.UserId)</MudText>
|
||||||
|
<MudText Typo="Typo.body2">مبلغ: @Model.Amount.ToString("N0") ریال</MudText>
|
||||||
|
<MudText Typo="Typo.body2">نوع: @Model.TypeDisplay</MudText>
|
||||||
|
<MudText Typo="Typo.body2">وضعیت: @Model.StatusDisplay</MudText>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(Model.ReferenceNumber))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body2">شماره مرجع: @Model.ReferenceNumber</MudText>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudText Typo="Typo.body2">توضیحات: @Model.Description</MudText>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(Model.RejectionReason))
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Error">
|
||||||
|
دلیل رد: @Model.RejectionReason
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Model.Status == (int)ManualPaymentDialogStatus.Pending)
|
||||||
|
{
|
||||||
|
<MudTextField T="string"
|
||||||
|
Label="یادداشت تایید / دلیل رد"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Lines="3"
|
||||||
|
@bind-Value="_adminNote" />
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton Variant="Variant.Outlined" Color="Color.Default" OnClick="Close">
|
||||||
|
بستن
|
||||||
|
</MudButton>
|
||||||
|
|
||||||
|
@if (Mode == ManualPaymentDialogMode.Create)
|
||||||
|
{
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="CreateAsync">
|
||||||
|
ثبت
|
||||||
|
</MudButton>
|
||||||
|
}
|
||||||
|
else if (Model is not null && Model.Status == (int)ManualPaymentDialogStatus.Pending)
|
||||||
|
{
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Success" OnClick="ApproveAsync">
|
||||||
|
تایید
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Error" OnClick="RejectAsync">
|
||||||
|
رد
|
||||||
|
</MudButton>
|
||||||
|
}
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
using BackOffice.BFF.ManualPayment.Protobuf;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Payment.Components;
|
||||||
|
|
||||||
|
public enum ManualPaymentDialogMode
|
||||||
|
{
|
||||||
|
Create = 0,
|
||||||
|
Details = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ManualPaymentDialogStatus
|
||||||
|
{
|
||||||
|
Pending = 0,
|
||||||
|
Approved = 1,
|
||||||
|
Rejected = 2,
|
||||||
|
Cancelled = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class ManualPaymentDialog
|
||||||
|
{
|
||||||
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
[Inject] public ManualPaymentContract.ManualPaymentContractClient ManualPaymentClient { get; set; } = default!;
|
||||||
|
[Inject] public ISnackbar Snackbar { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter] public ManualPaymentDialogMode Mode { get; set; }
|
||||||
|
[Parameter] public ManualPaymentModel? Model { get; set; }
|
||||||
|
|
||||||
|
private ManualPaymentModel _createModel = new();
|
||||||
|
private string? _adminNote;
|
||||||
|
|
||||||
|
private async Task CreateAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new CreateManualPaymentRequest
|
||||||
|
{
|
||||||
|
UserId = _createModel.UserId,
|
||||||
|
Amount = _createModel.Amount,
|
||||||
|
Type = _createModel.Type,
|
||||||
|
Description = _createModel.Description
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(_createModel.ReferenceNumber))
|
||||||
|
{
|
||||||
|
request.ReferenceNumber = new Google.Protobuf.WellKnownTypes.StringValue
|
||||||
|
{
|
||||||
|
Value = _createModel.ReferenceNumber
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await ManualPaymentClient.CreateManualPaymentAsync(request);
|
||||||
|
Snackbar.Add("پرداخت دستی با موفقیت ثبت شد.", Severity.Success);
|
||||||
|
MudDialog.Close(DialogResult.Ok(true));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا در ثبت پرداخت دستی: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ApproveAsync()
|
||||||
|
{
|
||||||
|
if (Model is null) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new ApproveManualPaymentRequest
|
||||||
|
{
|
||||||
|
ManualPaymentId = Model.Id
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(_adminNote))
|
||||||
|
{
|
||||||
|
request.ApprovalNote = new Google.Protobuf.WellKnownTypes.StringValue
|
||||||
|
{
|
||||||
|
Value = _adminNote
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await ManualPaymentClient.ApproveManualPaymentAsync(request);
|
||||||
|
Snackbar.Add("پرداخت دستی تایید شد.", Severity.Success);
|
||||||
|
MudDialog.Close(DialogResult.Ok(true));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا در تایید پرداخت دستی: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RejectAsync()
|
||||||
|
{
|
||||||
|
if (Model is null) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_adminNote))
|
||||||
|
{
|
||||||
|
Snackbar.Add("لطفاً دلیل رد را وارد کنید.", Severity.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new RejectManualPaymentRequest
|
||||||
|
{
|
||||||
|
ManualPaymentId = Model.Id,
|
||||||
|
RejectionReason = _adminNote
|
||||||
|
};
|
||||||
|
|
||||||
|
await ManualPaymentClient.RejectManualPaymentAsync(request);
|
||||||
|
Snackbar.Add("پرداخت دستی رد شد.", Severity.Success);
|
||||||
|
MudDialog.Close(DialogResult.Ok(true));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا در رد پرداخت دستی: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Close() => MudDialog.Close(DialogResult.Cancel());
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
@namespace BackOffice.Pages.Payment.Components
|
||||||
|
|
||||||
|
@using MudBlazor
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
@if (Model is not null)
|
||||||
|
{
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudText Typo="Typo.h6">جزئیات تراکنش</MudText>
|
||||||
|
<MudText Typo="Typo.body2">شناسه: @Model.Id</MudText>
|
||||||
|
<MudText Typo="Typo.body2">RefId: @Model.RefId</MudText>
|
||||||
|
<MudText Typo="Typo.body2">مبلغ: @Model.Amount.ToString("N0") ریال</MudText>
|
||||||
|
<MudText Typo="Typo.body2">وضعیت: @StatusText</MudText>
|
||||||
|
<MudText Typo="Typo.body2">تاریخ پرداخت: @Model.PaymentDate.ToLocalTime().MiladiToJalaliWithTime()</MudText>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(Model.BankReferenceId))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body2">BankReferenceId: @Model.BankReferenceId</MudText>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(Model.TrackingCode))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body2">TrackingCode: @Model.TrackingCode</MudText>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Model.PaymentFailed && !string.IsNullOrWhiteSpace(Model.PaymentFailedReason))
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Error">
|
||||||
|
دلیل خطا: @Model.PaymentFailedReason
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton Variant="Variant.Outlined" Color="Color.Default" OnClick="Close">
|
||||||
|
بستن
|
||||||
|
</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Payment.Components;
|
||||||
|
|
||||||
|
public partial class TransactionDetailsDialog
|
||||||
|
{
|
||||||
|
[CascadingParameter]
|
||||||
|
private IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public TransactionDetailsModel? Model { get; set; }
|
||||||
|
|
||||||
|
private string StatusText => Model is null ? string.Empty : GetStatusText(Model.PaymentStatus);
|
||||||
|
|
||||||
|
private void Close()
|
||||||
|
{
|
||||||
|
MudDialog.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetStatusText(int status)
|
||||||
|
{
|
||||||
|
return status switch
|
||||||
|
{
|
||||||
|
0 => "در انتظار",
|
||||||
|
1 => "موفق",
|
||||||
|
2 => "ناموفق",
|
||||||
|
3 => "بازگشت وجه",
|
||||||
|
_ => "نامشخص"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TransactionDetailsModel
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public string? RefId { get; set; }
|
||||||
|
public long Amount { get; set; }
|
||||||
|
public int PaymentStatus { get; set; }
|
||||||
|
public DateTime PaymentDate { get; set; }
|
||||||
|
public string? BankReferenceId { get; set; }
|
||||||
|
public string? TrackingCode { get; set; }
|
||||||
|
public bool PaymentFailed { get; set; }
|
||||||
|
public string? PaymentFailedReason { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
246
src/BackOffice/Pages/Payment/ManualPayments.razor
Normal file
246
src/BackOffice/Pages/Payment/ManualPayments.razor
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
@page "/payment/manual-payments"
|
||||||
|
@using BackOffice.BFF.ManualPayment.Protobuf
|
||||||
|
@using BackOffice.Pages.Payment.Components
|
||||||
|
@using BackOffice.Common.BaseComponents
|
||||||
|
|
||||||
|
<BasePageComponent @ref="_basePage" OnSubmitClick="OnFilterSubmit" OnClearFilterClick="OnFilterCleared">
|
||||||
|
<Filters>
|
||||||
|
<MudNumericField T="long?"
|
||||||
|
HideSpinButtons="true"
|
||||||
|
Clearable="true"
|
||||||
|
Label="شناسه کاربر"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
@bind-Value="_userIdFilter" />
|
||||||
|
|
||||||
|
<MudTextField T="string"
|
||||||
|
Label="شماره مرجع"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
@bind-Value="_referenceFilter" />
|
||||||
|
|
||||||
|
<MudSelect T="int?"
|
||||||
|
Clearable="true"
|
||||||
|
Label="وضعیت"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
@bind-Value="_statusFilter">
|
||||||
|
<MudSelectItem T="int?" Value="@(0)">در انتظار</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(1)">تایید شده</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(2)">رد شده</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(3)">لغو شده</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
|
||||||
|
<MudSelect T="int?"
|
||||||
|
Clearable="true"
|
||||||
|
Label="نوع"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
@bind-Value="_typeFilter">
|
||||||
|
<MudSelectItem T="int?" Value="@(1)">واریز نقدی</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(2)">شارژ کیفپول تخفیف</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(3)">شارژ کیفپول شبکه</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(4)">تسویه حساب</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(5)">اصلاح خطا</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(6)">بازگشت وجه</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(99)">سایر</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
</Filters>
|
||||||
|
<Content>
|
||||||
|
<MudPaper Class="pa-2 mb-2">
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
||||||
|
<MudText Typo="Typo.subtitle1">پرداختهای دستی</MudText>
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
Color="Color.Primary"
|
||||||
|
OnClick="OpenCreateDialog">
|
||||||
|
ثبت پرداخت دستی جدید
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
</MudPaper>
|
||||||
|
|
||||||
|
<MudDataGrid T="ManualPaymentModel"
|
||||||
|
ServerData="LoadData"
|
||||||
|
Hover="true"
|
||||||
|
Dense="true">
|
||||||
|
<Columns>
|
||||||
|
<PropertyColumn Property="x => x.Id" Title="شناسه" />
|
||||||
|
<PropertyColumn Property="x => x.UserId" Title="شناسه کاربر" />
|
||||||
|
<PropertyColumn Property="x => x.UserFullName" Title="نام کاربر" />
|
||||||
|
<PropertyColumn Property="x => x.Amount" Title="مبلغ">
|
||||||
|
<CellTemplate>
|
||||||
|
@context.Item.Amount.ToString("N0")
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
<TemplateColumn Title="نوع">
|
||||||
|
<CellTemplate>
|
||||||
|
@GetTypeText(context.Item.Type)
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn Title="وضعیت">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudChip T="string"
|
||||||
|
Size="Size.Small"
|
||||||
|
Color="@GetStatusColor(context.Item.Status)">
|
||||||
|
@GetStatusText(context.Item.Status)
|
||||||
|
</MudChip>
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn Title="تاریخ ایجاد">
|
||||||
|
<CellTemplate>
|
||||||
|
@context.Item.Created.ToLocalTime().MiladiToJalaliWithTime()
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn Title="عملیات">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||||
|
<MudTooltip Text="جزئیات">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Info"
|
||||||
|
Size="Size.Small"
|
||||||
|
OnClick="@(() => OpenDetails(context.Item))" />
|
||||||
|
</MudTooltip>
|
||||||
|
</MudStack>
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
</Columns>
|
||||||
|
</MudDataGrid>
|
||||||
|
</Content>
|
||||||
|
</BasePageComponent>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Inject] public BackOffice.BFF.ManualPayment.Protobuf.ManualPaymentContract.ManualPaymentContractClient ManualPaymentClient { get; set; } = default!;
|
||||||
|
[Inject] public IDialogService DialogService { get; set; } = default!;
|
||||||
|
|
||||||
|
private BasePageComponent? _basePage;
|
||||||
|
private long? _userIdFilter;
|
||||||
|
private string? _referenceFilter;
|
||||||
|
private int? _statusFilter;
|
||||||
|
private int? _typeFilter;
|
||||||
|
|
||||||
|
private async Task<GridData<ManualPaymentModel>> LoadData(GridState<ManualPaymentModel> state)
|
||||||
|
{
|
||||||
|
var pageNumber = state.Page + 1;
|
||||||
|
var pageSize = state.PageSize;
|
||||||
|
|
||||||
|
var request = new GetManualPaymentsRequest
|
||||||
|
{
|
||||||
|
PageNumber = pageNumber,
|
||||||
|
PageSize = pageSize
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_userIdFilter.HasValue)
|
||||||
|
{
|
||||||
|
request.UserId = new Google.Protobuf.WellKnownTypes.Int64Value { Value = _userIdFilter.Value };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_statusFilter.HasValue)
|
||||||
|
{
|
||||||
|
request.Status = new Google.Protobuf.WellKnownTypes.Int32Value { Value = _statusFilter.Value };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_typeFilter.HasValue)
|
||||||
|
{
|
||||||
|
request.Type = new Google.Protobuf.WellKnownTypes.Int32Value { Value = _typeFilter.Value };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(_referenceFilter))
|
||||||
|
{
|
||||||
|
request.ReferenceNumber = new Google.Protobuf.WellKnownTypes.StringValue { Value = _referenceFilter };
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await ManualPaymentClient.GetManualPaymentsAsync(request);
|
||||||
|
|
||||||
|
return new GridData<ManualPaymentModel>
|
||||||
|
{
|
||||||
|
Items = response.Models.ToList(),
|
||||||
|
TotalItems = (int)response.MetaData.TotalCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnFilterSubmit()
|
||||||
|
{
|
||||||
|
await _basePage!.ReloadAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnFilterCleared()
|
||||||
|
{
|
||||||
|
_userIdFilter = null;
|
||||||
|
_referenceFilter = null;
|
||||||
|
_statusFilter = null;
|
||||||
|
_typeFilter = null;
|
||||||
|
await _basePage!.ReloadAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenCreateDialog()
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
{ nameof(ManualPaymentDialog.Mode), ManualPaymentDialogMode.Create }
|
||||||
|
};
|
||||||
|
|
||||||
|
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Medium, FullWidth = true };
|
||||||
|
|
||||||
|
var dialog = DialogService.Show<ManualPaymentDialog>("ثبت پرداخت دستی", parameters, options);
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled)
|
||||||
|
{
|
||||||
|
await _basePage!.ReloadAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenDetails(ManualPaymentModel model)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
{ nameof(ManualPaymentDialog.Mode), ManualPaymentDialogMode.Details },
|
||||||
|
{ nameof(ManualPaymentDialog.Model), model }
|
||||||
|
};
|
||||||
|
|
||||||
|
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small, FullWidth = true };
|
||||||
|
|
||||||
|
var dialog = DialogService.Show<ManualPaymentDialog>($"جزئیات پرداخت دستی #{model.Id}", parameters, options);
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled)
|
||||||
|
{
|
||||||
|
await _basePage!.ReloadAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetTypeText(int type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
1 => "واریز نقدی",
|
||||||
|
2 => "شارژ کیفپول تخفیف",
|
||||||
|
3 => "شارژ کیفپول شبکه",
|
||||||
|
4 => "تسویه حساب",
|
||||||
|
5 => "اصلاح خطا",
|
||||||
|
6 => "بازگشت وجه",
|
||||||
|
99 => "سایر",
|
||||||
|
_ => "نامشخص"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetStatusText(int status)
|
||||||
|
{
|
||||||
|
return status switch
|
||||||
|
{
|
||||||
|
0 => "در انتظار",
|
||||||
|
1 => "تایید شده",
|
||||||
|
2 => "رد شده",
|
||||||
|
3 => "لغو شده",
|
||||||
|
_ => "نامشخص"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Color GetStatusColor(int status)
|
||||||
|
{
|
||||||
|
return status switch
|
||||||
|
{
|
||||||
|
0 => Color.Warning,
|
||||||
|
1 => Color.Success,
|
||||||
|
2 => Color.Error,
|
||||||
|
3 => Color.Default,
|
||||||
|
_ => Color.Default
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
82
src/BackOffice/Pages/Payment/Transactions.razor
Normal file
82
src/BackOffice/Pages/Payment/Transactions.razor
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
@page "/payment/transactions"
|
||||||
|
@using BackOffice.Common.BaseComponents
|
||||||
|
|
||||||
|
<BasePageComponent @ref="_basePage" OnSubmitClick="OnFilterSubmit" OnClearFilterClick="OnFilterCleared">
|
||||||
|
<Filters>
|
||||||
|
<BackOffice.Common.BaseComponents.DateRangePicker @bind-From="_fromDate" @bind-To="_toDate" Label="بازه تاریخی" />
|
||||||
|
|
||||||
|
<MudSelect T="int?"
|
||||||
|
Clearable="true"
|
||||||
|
Label="وضعیت پرداخت"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
@bind-Value="_statusFilter">
|
||||||
|
<MudSelectItem T="int?" Value="@(0)">در انتظار</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(1)">موفق</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(2)">ناموفق</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(3)">بازگشت وجه</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
|
||||||
|
<MudSelect T="int?"
|
||||||
|
Clearable="true"
|
||||||
|
Label="نوع تراکنش"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
@bind-Value="_typeFilter">
|
||||||
|
<MudSelectItem T="int?" Value="@(0)">نامشخص</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(1)">خرید پکیج</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(2)">پرداخت دستی</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(3)">شارژ کیفپول</MudSelectItem>
|
||||||
|
<MudSelectItem T="int?" Value="@(4)">بازگشت وجه</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
</Filters>
|
||||||
|
<Content>
|
||||||
|
<MudPaper Class="pa-2 mb-2">
|
||||||
|
<MudText Typo="Typo.subtitle1">مدیریت تراکنشها</MudText>
|
||||||
|
</MudPaper>
|
||||||
|
|
||||||
|
<MudDataGrid T="TransactionModel"
|
||||||
|
ServerData="LoadData"
|
||||||
|
Hover="true"
|
||||||
|
Dense="true">
|
||||||
|
<Columns>
|
||||||
|
<PropertyColumn Property="x => x.Id" Title="شناسه" />
|
||||||
|
<PropertyColumn Property="x => x.RefId" Title="شماره مرجع" />
|
||||||
|
<PropertyColumn Property="x => x.Amount" Title="مبلغ">
|
||||||
|
<CellTemplate>
|
||||||
|
@context.Item.Amount.ToString("N0")
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
<TemplateColumn Title="وضعیت">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudChip T="string"
|
||||||
|
Size="Size.Small"
|
||||||
|
Color="@GetStatusColor(context.Item.PaymentStatus)">
|
||||||
|
@GetStatusText(context.Item.PaymentStatus)
|
||||||
|
</MudChip>
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn Title="تاریخ پرداخت">
|
||||||
|
<CellTemplate>
|
||||||
|
@context.Item.PaymentDate.ToLocalTime().MiladiToJalaliWithTime()
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn Title="نوع">
|
||||||
|
<CellTemplate>
|
||||||
|
@GetTypeText(context.Item.Type)
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn Title="عملیات">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudTooltip Text="جزئیات">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Info"
|
||||||
|
Size="Size.Small"
|
||||||
|
OnClick="@(() => OpenDetails(context.Item))" />
|
||||||
|
</MudTooltip>
|
||||||
|
</CellTemplate>
|
||||||
|
</TemplateColumn>
|
||||||
|
</Columns>
|
||||||
|
</MudDataGrid>
|
||||||
|
</Content>
|
||||||
|
</BasePageComponent>
|
||||||
|
|
||||||
94
src/BackOffice/Pages/Payment/Transactions.razor.cs
Normal file
94
src/BackOffice/Pages/Payment/Transactions.razor.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using BackOffice.Common.BaseComponents;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Payment;
|
||||||
|
|
||||||
|
public partial class Transactions
|
||||||
|
{
|
||||||
|
private BasePageComponent? _basePage;
|
||||||
|
|
||||||
|
private DateTime? _fromDate;
|
||||||
|
private DateTime? _toDate;
|
||||||
|
private int? _statusFilter;
|
||||||
|
private int? _typeFilter;
|
||||||
|
|
||||||
|
private async Task<GridData<TransactionModel>> LoadData(GridState<TransactionModel> state)
|
||||||
|
{
|
||||||
|
// TODO: Connect to BackOffice.BFF Transactions when API is ready
|
||||||
|
await Task.CompletedTask;
|
||||||
|
|
||||||
|
return new GridData<TransactionModel>
|
||||||
|
{
|
||||||
|
Items = Array.Empty<TransactionModel>(),
|
||||||
|
TotalItems = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task OnFilterSubmit()
|
||||||
|
{
|
||||||
|
_basePage?.ReloadGrid();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task OnFilterCleared()
|
||||||
|
{
|
||||||
|
_fromDate = null;
|
||||||
|
_toDate = null;
|
||||||
|
_statusFilter = null;
|
||||||
|
_typeFilter = null;
|
||||||
|
_basePage?.ReloadGrid();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenDetails(TransactionModel model)
|
||||||
|
{
|
||||||
|
// TODO: Open TransactionDetailsDialog with model
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetStatusText(int status)
|
||||||
|
{
|
||||||
|
return status switch
|
||||||
|
{
|
||||||
|
0 => "در انتظار",
|
||||||
|
1 => "موفق",
|
||||||
|
2 => "ناموفق",
|
||||||
|
3 => "بازگشت وجه",
|
||||||
|
_ => "نامشخص"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Color GetStatusColor(int status)
|
||||||
|
{
|
||||||
|
return status switch
|
||||||
|
{
|
||||||
|
0 => Color.Warning,
|
||||||
|
1 => Color.Success,
|
||||||
|
2 => Color.Error,
|
||||||
|
3 => Color.Info,
|
||||||
|
_ => Color.Default
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetTypeText(int type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
1 => "خرید پکیج",
|
||||||
|
2 => "پرداخت دستی",
|
||||||
|
3 => "شارژ کیفپول",
|
||||||
|
4 => "بازگشت وجه",
|
||||||
|
_ => "نامشخص"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TransactionModel
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public string? RefId { get; set; }
|
||||||
|
public long Amount { get; set; }
|
||||||
|
public int PaymentStatus { get; set; }
|
||||||
|
public DateTime PaymentDate { get; set; }
|
||||||
|
public int Type { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
using BackOffice.BFF.Products.Protobuf.Protos.Products;
|
using BackOffice.BFF.Products.Protobuf.Protos.Products;
|
||||||
using BackOffice.Common.BaseComponents;
|
using BackOffice.Common.BaseComponents;
|
||||||
using BackOffice.Common.Utilities;
|
using BackOffice.Common.Utilities;
|
||||||
using BackOffice.Pages.Tag.Components;
|
// TODO: Uncomment when Tag proto project is created
|
||||||
|
// using BackOffice.Pages.Tag.Components;
|
||||||
using BackOffice.Pages.Products.Components;
|
using BackOffice.Pages.Products.Components;
|
||||||
using Mapster;
|
using Mapster;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
@@ -239,16 +240,20 @@ public partial class ProductsMainPage
|
|||||||
Navigation.NavigateTo($"{RouteConstance.ProductCategories}{model.Id}");
|
Navigation.NavigateTo($"{RouteConstance.ProductCategories}{model.Id}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Enable when Tag proto project is created
|
||||||
public async Task OpenTagAssignment(DataModel model)
|
public async Task OpenTagAssignment(DataModel model)
|
||||||
{
|
{
|
||||||
var parameters = new DialogParameters<AssignTagsDialog>
|
// var parameters = new DialogParameters<AssignTagsDialog>
|
||||||
{
|
// {
|
||||||
{ x => x.ProductId, model.Id },
|
// { x => x.ProductId, model.Id },
|
||||||
{ x => x.ProductTitle, model.Title }
|
// { x => x.ProductTitle, model.Title }
|
||||||
};
|
// };
|
||||||
|
|
||||||
await DialogService.ShowAsync<AssignTagsDialog>("مدیریت تگهای محصول", parameters,
|
// await DialogService.ShowAsync<AssignTagsDialog>("مدیریت تگهای محصول", parameters,
|
||||||
new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small, FullWidth = true });
|
// new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small, FullWidth = true });
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
Snackbar.Add("قابلیت تگگذاری در حال توسعه است", Severity.Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OpenImagePreview(string imagePath, string title)
|
public async Task OpenImagePreview(string imagePath, string title)
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
@namespace BackOffice.Pages.Role.Components
|
||||||
|
|
||||||
|
@using MudBlazor
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudText Typo="Typo.h6">دسترسیهای نقش @RoleName</MudText>
|
||||||
|
|
||||||
|
@foreach (var category in _categories)
|
||||||
|
{
|
||||||
|
<MudExpansionPanels Elevation="0">
|
||||||
|
<MudExpansionPanel Text="@category.DisplayName" Expanded="false">
|
||||||
|
<MudStack Spacing="1">
|
||||||
|
@foreach (var permission in category.Permissions)
|
||||||
|
{
|
||||||
|
<MudCheckBox T="bool"
|
||||||
|
Label="@permission.DisplayName"
|
||||||
|
LabelPosition="LabelPosition.End"
|
||||||
|
Color="Color.Primary"
|
||||||
|
Value="@_selectedPermissions.Contains(permission.Key)"
|
||||||
|
ValueChanged="(checkedValue => OnPermissionChanged(checkedValue, permission.Key))" />
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</MudExpansionPanel>
|
||||||
|
</MudExpansionPanels>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton Color="Color.Default" Variant="Variant.Text" OnClick="Cancel">
|
||||||
|
انصراف
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="SaveAsync">
|
||||||
|
ذخیره
|
||||||
|
</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace BackOffice.Pages.Role.Components;
|
||||||
|
|
||||||
|
public partial class RolePermissionsDialog
|
||||||
|
{
|
||||||
|
[CascadingParameter]
|
||||||
|
private IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string RoleName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
private readonly List<PermissionCategory> _categories = new();
|
||||||
|
private readonly HashSet<string> _selectedPermissions = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
base.OnInitialized();
|
||||||
|
|
||||||
|
_categories.AddRange(GetPermissionCategories());
|
||||||
|
|
||||||
|
// پیشانتخاب براساس RoleName (همراستا با BFF RolePermissionConfig)
|
||||||
|
if (string.Equals(RoleName, "SuperAdmin", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
foreach (var permission in _categories.SelectMany(c => c.Permissions))
|
||||||
|
{
|
||||||
|
_selectedPermissions.Add(permission.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (string.Equals(RoleName, "Admin", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
foreach (var permission in _categories.SelectMany(c => c.Permissions))
|
||||||
|
{
|
||||||
|
if (!permission.Key.StartsWith("settings.", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
_selectedPermissions.Add(permission.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (string.Equals(RoleName, "Inspector", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
foreach (var permission in _categories.SelectMany(c => c.Permissions))
|
||||||
|
{
|
||||||
|
if (permission.Key.EndsWith(".view", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
_selectedPermissions.Add(permission.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPermissionChanged(bool isChecked, string permissionKey)
|
||||||
|
{
|
||||||
|
if (isChecked)
|
||||||
|
{
|
||||||
|
_selectedPermissions.Add(permissionKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_selectedPermissions.Remove(permissionKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task SaveAsync()
|
||||||
|
{
|
||||||
|
// TODO: اتصال به API واقعی RBAC در BFF/CMS برای ذخیره RolePermissions
|
||||||
|
MudDialog.Close(DialogResult.Ok(_selectedPermissions.ToList()));
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel()
|
||||||
|
{
|
||||||
|
MudDialog.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<PermissionCategory> GetPermissionCategories()
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
new PermissionCategory(
|
||||||
|
"orders",
|
||||||
|
"سفارشها",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new PermissionItem("orders.view", "مشاهده سفارشها"),
|
||||||
|
new PermissionItem("orders.create", "ایجاد سفارش"),
|
||||||
|
new PermissionItem("orders.update", "ویرایش سفارش"),
|
||||||
|
new PermissionItem("orders.delete", "حذف سفارش"),
|
||||||
|
new PermissionItem("orders.cancel", "لغو سفارش"),
|
||||||
|
new PermissionItem("orders.approve", "تایید سفارش / عملیات حساس")
|
||||||
|
}),
|
||||||
|
new PermissionCategory(
|
||||||
|
"products",
|
||||||
|
"محصولات",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new PermissionItem("products.view", "مشاهده محصولات"),
|
||||||
|
new PermissionItem("products.create", "ایجاد محصول"),
|
||||||
|
new PermissionItem("products.update", "ویرایش محصول"),
|
||||||
|
new PermissionItem("products.delete", "حذف محصول")
|
||||||
|
}),
|
||||||
|
new PermissionCategory(
|
||||||
|
"users",
|
||||||
|
"کاربران",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new PermissionItem("users.view", "مشاهده کاربران"),
|
||||||
|
new PermissionItem("users.update", "ویرایش کاربران"),
|
||||||
|
new PermissionItem("users.delete", "حذف کاربران")
|
||||||
|
}),
|
||||||
|
new PermissionCategory(
|
||||||
|
"commission",
|
||||||
|
"کمیسیون و برداشتها",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new PermissionItem("commission.view", "مشاهده گزارشهای کمیسیون"),
|
||||||
|
new PermissionItem("commission.approve_withdrawal", "تایید درخواست برداشت")
|
||||||
|
}),
|
||||||
|
new PermissionCategory(
|
||||||
|
"discountshop",
|
||||||
|
"فروشگاه تخفیفی",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new PermissionItem("discountshop.manage", "مدیریت فروشگاه تخفیفی (محصولات/سفارشها/گزارش فروش)")
|
||||||
|
}),
|
||||||
|
new PermissionCategory(
|
||||||
|
"publicmessages",
|
||||||
|
"پیامهای عمومی",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new PermissionItem("publicmessages.view", "مشاهده پیامهای عمومی"),
|
||||||
|
new PermissionItem("publicmessages.create", "ایجاد پیام عمومی"),
|
||||||
|
new PermissionItem("publicmessages.update", "ویرایش پیام عمومی"),
|
||||||
|
new PermissionItem("publicmessages.publish", "انتشار/بایگانی پیامها")
|
||||||
|
}),
|
||||||
|
new PermissionCategory(
|
||||||
|
"settings",
|
||||||
|
"تنظیمات و سیستم",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new PermissionItem("settings.view", "مشاهده تنظیمات"),
|
||||||
|
new PermissionItem("settings.manage_configuration", "مدیریت تنظیمات سیستم"),
|
||||||
|
new PermissionItem("settings.manage_vat", "مدیریت تنظیمات VAT"),
|
||||||
|
new PermissionItem("system.alerts.view", "مشاهده هشدارهای سیستم"),
|
||||||
|
new PermissionItem("system.health.view", "مشاهده وضعیت سلامت سیستم")
|
||||||
|
}),
|
||||||
|
new PermissionCategory(
|
||||||
|
"reports",
|
||||||
|
"گزارشها",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new PermissionItem("reports.view", "مشاهده گزارشها")
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private record PermissionCategory(string Key, string DisplayName, IReadOnlyList<PermissionItem> Permissions);
|
||||||
|
|
||||||
|
private record PermissionItem(string Key, string DisplayName);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -41,6 +41,15 @@
|
|||||||
<MudTooltip Text="آرشیو">
|
<MudTooltip Text="آرشیو">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.DeleteOutline" Size="Size.Small" ButtonType="ButtonType.Button" OnClick="@(() => OnDelete(context.Item))" Style="cursor:pointer;" />
|
<MudIconButton Icon="@Icons.Material.Filled.DeleteOutline" Size="Size.Small" ButtonType="ButtonType.Button" OnClick="@(() => OnDelete(context.Item))" Style="cursor:pointer;" />
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
|
|
||||||
|
<MudTooltip Text="ویرایش دسترسیها">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Security"
|
||||||
|
Size="Size.Small"
|
||||||
|
Color="Color.Primary"
|
||||||
|
ButtonType="ButtonType.Button"
|
||||||
|
OnClick="@(() => EditPermissions(context.Item))"
|
||||||
|
Style="cursor:pointer;" />
|
||||||
|
</MudTooltip>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</CellTemplate>
|
</CellTemplate>
|
||||||
</TemplateColumn>
|
</TemplateColumn>
|
||||||
@@ -56,4 +65,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -93,4 +93,22 @@ public partial class RoleMainPage
|
|||||||
_request = new() { Filter = new() { } };
|
_request = new() { Filter = new() { } };
|
||||||
ReLoadData();
|
ReLoadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task EditPermissions(DataModel model)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters<RolePermissionsDialog>
|
||||||
|
{
|
||||||
|
{ x => x.RoleName, model.Name }
|
||||||
|
};
|
||||||
|
|
||||||
|
var options = new DialogOptions
|
||||||
|
{
|
||||||
|
CloseButton = true,
|
||||||
|
FullWidth = true,
|
||||||
|
MaxWidth = MaxWidth.Medium
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<RolePermissionsDialog>($"دسترسیهای نقش {model.Title}", parameters, options);
|
||||||
|
await dialog.Result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,11 +30,11 @@
|
|||||||
<MudSelectItem Value="@("en")">English</MudSelectItem>
|
<MudSelectItem Value="@("en")">English</MudSelectItem>
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
|
|
||||||
<MudSwitch @bind-Value="_darkMode"
|
<MudSwitch T="bool" @bind-Value="_darkMode"
|
||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
Label="حالت تاریک" />
|
Label="حالت تاریک" />
|
||||||
|
|
||||||
<MudSwitch @bind-Value="_compactMode"
|
<MudSwitch T="bool" @bind-Value="_compactMode"
|
||||||
Color="Color.Secondary"
|
Color="Color.Secondary"
|
||||||
Label="حالت فشرده" />
|
Label="حالت فشرده" />
|
||||||
|
|
||||||
@@ -67,15 +67,15 @@
|
|||||||
</MudCardHeader>
|
</MudCardHeader>
|
||||||
<MudCardContent>
|
<MudCardContent>
|
||||||
<MudStack Spacing="3">
|
<MudStack Spacing="3">
|
||||||
<MudSwitch @bind-Value="_emailNotifications"
|
<MudSwitch T="bool" @bind-Value="_emailNotifications"
|
||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
Label="دریافت اعلانهای ایمیل" />
|
Label="دریافت اعلانهای ایمیل" />
|
||||||
|
|
||||||
<MudSwitch @bind-Value="_smsNotifications"
|
<MudSwitch T="bool" @bind-Value="_smsNotifications"
|
||||||
Color="Color.Secondary"
|
Color="Color.Secondary"
|
||||||
Label="دریافت اعلانهای پیامک" />
|
Label="دریافت اعلانهای پیامک" />
|
||||||
|
|
||||||
<MudSwitch @bind-Value="_systemNotifications"
|
<MudSwitch T="bool" @bind-Value="_systemNotifications"
|
||||||
Color="Color.Info"
|
Color="Color.Info"
|
||||||
Label="اعلانهای سیستمی" />
|
Label="اعلانهای سیستمی" />
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@
|
|||||||
</MudCardHeader>
|
</MudCardHeader>
|
||||||
<MudCardContent>
|
<MudCardContent>
|
||||||
<MudStack Spacing="3">
|
<MudStack Spacing="3">
|
||||||
<MudSwitch @bind-Value="_twoFactorEnabled"
|
<MudSwitch T="bool" @bind-Value="_twoFactorEnabled"
|
||||||
Color="Color.Success"
|
Color="Color.Success"
|
||||||
Label="فعالسازی احراز هویت دو مرحلهای" />
|
Label="فعالسازی احراز هویت دو مرحلهای" />
|
||||||
|
|
||||||
|
|||||||
@@ -81,13 +81,13 @@
|
|||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
||||||
<MudItem xs="12">
|
<MudItem xs="12">
|
||||||
<MudSwitch @bind-Value="_commissionConfig.AutoCalculationEnabled"
|
<MudSwitch T="bool" @bind-Value="_commissionConfig.AutoCalculationEnabled"
|
||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
Label="محاسبه خودکار هفتگی فعال باشد" />
|
Label="محاسبه خودکار هفتگی فعال باشد" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
||||||
<MudItem xs="12">
|
<MudItem xs="12">
|
||||||
<MudSwitch @bind-Value="_commissionConfig.AutoPayoutEnabled"
|
<MudSwitch T="bool" @bind-Value="_commissionConfig.AutoPayoutEnabled"
|
||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
Label="پرداخت خودکار فعال باشد" />
|
Label="پرداخت خودکار فعال باشد" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
@@ -158,19 +158,19 @@
|
|||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
||||||
<MudItem xs="12">
|
<MudItem xs="12">
|
||||||
<MudSwitch @bind-Value="_networkConfig.BinaryTreeEnabled"
|
<MudSwitch T="bool" @bind-Value="_networkConfig.BinaryTreeEnabled"
|
||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
Label="سیستم باینری فعال باشد" />
|
Label="سیستم باینری فعال باشد" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
||||||
<MudItem xs="12">
|
<MudItem xs="12">
|
||||||
<MudSwitch @bind-Value="_networkConfig.AutoPlacementEnabled"
|
<MudSwitch T="bool" @bind-Value="_networkConfig.AutoPlacementEnabled"
|
||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
Label="جایگذاری خودکار در درخت" />
|
Label="جایگذاری خودکار در درخت" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
||||||
<MudItem xs="12">
|
<MudItem xs="12">
|
||||||
<MudSwitch @bind-Value="_networkConfig.SpilloverEnabled"
|
<MudSwitch T="bool" @bind-Value="_networkConfig.SpilloverEnabled"
|
||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
Label="Spillover فعال باشد"
|
Label="Spillover فعال باشد"
|
||||||
HelperText="اعضای اضافی به پایین درخت منتقل شوند" />
|
HelperText="اعضای اضافی به پایین درخت منتقل شوند" />
|
||||||
@@ -244,19 +244,19 @@
|
|||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
||||||
<MudItem xs="12">
|
<MudItem xs="12">
|
||||||
<MudSwitch @bind-Value="_clubConfig.AutoRenewalEnabled"
|
<MudSwitch T="bool" @bind-Value="_clubConfig.AutoRenewalEnabled"
|
||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
Label="تمدید خودکار عضویت" />
|
Label="تمدید خودکار عضویت" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
||||||
<MudItem xs="12">
|
<MudItem xs="12">
|
||||||
<MudSwitch @bind-Value="_clubConfig.EmailNotificationsEnabled"
|
<MudSwitch T="bool" @bind-Value="_clubConfig.EmailNotificationsEnabled"
|
||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
Label="ارسال ایمیل یادآوری انقضا" />
|
Label="ارسال ایمیل یادآوری انقضا" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
||||||
<MudItem xs="12">
|
<MudItem xs="12">
|
||||||
<MudSwitch @bind-Value="_clubConfig.SmsNotificationsEnabled"
|
<MudSwitch T="bool" @bind-Value="_clubConfig.SmsNotificationsEnabled"
|
||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
Label="ارسال پیامک یادآوری انقضا" />
|
Label="ارسال پیامک یادآوری انقضا" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
@@ -372,6 +372,38 @@
|
|||||||
</MudStack>
|
</MudStack>
|
||||||
</MudCardContent>
|
</MudCardContent>
|
||||||
</MudCard>
|
</MudCard>
|
||||||
|
|
||||||
|
<MudCard Class="mt-4" Elevation="0">
|
||||||
|
<MudCardContent>
|
||||||
|
<MudText Typo="Typo.h6" Class="mb-2">تنظیمات مالیات بر ارزش افزوده (VAT)</MudText>
|
||||||
|
<MudText Typo="Typo.body2" Color="Color.Secondary" Class="mb-2">
|
||||||
|
درصد پیشفرض VAT که روی سفارشها اعمال میشود.
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<MudGrid>
|
||||||
|
<MudItem xs="12" md="4">
|
||||||
|
<MudNumericField @bind-Value="_vatConfig.DefaultVatPercentage"
|
||||||
|
Label="درصد VAT پیشفرض"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Adornment="Adornment.End"
|
||||||
|
AdornmentText="%"
|
||||||
|
Min="0"
|
||||||
|
Max="100" />
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
|
||||||
|
<MudDivider Class="my-4" />
|
||||||
|
|
||||||
|
<MudStack Row="true" Justify="Justify.FlexEnd" Spacing="2">
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
OnClick="SaveVatConfig"
|
||||||
|
StartIcon="@Icons.Material.Filled.Save">
|
||||||
|
ذخیره تنظیمات VAT
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
</MudCardContent>
|
||||||
|
</MudCard>
|
||||||
</MudTabPanel>
|
</MudTabPanel>
|
||||||
</MudTabs>
|
</MudTabs>
|
||||||
</MudContainer>
|
</MudContainer>
|
||||||
@@ -383,6 +415,7 @@
|
|||||||
private NetworkConfig _networkConfig = new();
|
private NetworkConfig _networkConfig = new();
|
||||||
private ClubConfig _clubConfig = new();
|
private ClubConfig _clubConfig = new();
|
||||||
private SystemConfig _systemConfig = new();
|
private SystemConfig _systemConfig = new();
|
||||||
|
private VatConfig _vatConfig = new();
|
||||||
|
|
||||||
private Dictionary<string, string> _configurations = new();
|
private Dictionary<string, string> _configurations = new();
|
||||||
|
|
||||||
@@ -453,6 +486,11 @@
|
|||||||
TwoFactorAuthEnabled = GetBoolConfig("System.TwoFactorAuth", true),
|
TwoFactorAuthEnabled = GetBoolConfig("System.TwoFactorAuth", true),
|
||||||
EmailVerificationRequired = GetBoolConfig("System.EmailVerification", true)
|
EmailVerificationRequired = GetBoolConfig("System.EmailVerification", true)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_vatConfig = new VatConfig
|
||||||
|
{
|
||||||
|
DefaultVatPercentage = GetIntConfig("DefaultVatPercentage", 10)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -543,6 +581,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SaveVatConfig()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await SaveConfig("DefaultVatPercentage", _vatConfig.DefaultVatPercentage.ToString(), 0);
|
||||||
|
Snackbar.Add("تنظیمات VAT با موفقیت ذخیره شد", Severity.Success);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا در ذخیره تنظیمات VAT: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveConfig(string key, string value, int scope)
|
private async Task SaveConfig(string key, string value, int scope)
|
||||||
{
|
{
|
||||||
var request = new BackOffice.BFF.Configuration.Protobuf.CreateOrUpdateConfigurationRequest
|
var request = new BackOffice.BFF.Configuration.Protobuf.CreateOrUpdateConfigurationRequest
|
||||||
@@ -631,4 +682,9 @@
|
|||||||
public bool TwoFactorAuthEnabled { get; set; }
|
public bool TwoFactorAuthEnabled { get; set; }
|
||||||
public bool EmailVerificationRequired { get; set; }
|
public bool EmailVerificationRequired { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class VatConfig
|
||||||
|
{
|
||||||
|
public int DefaultVatPercentage { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<MudTextField T="string" @bind-Value="Model.PostalCode" Disabled="_isLoading" Label="کد پستی" Variant="Variant.Outlined" />
|
<MudTextField T="string" @bind-Value="Model.PostalCode" Disabled="_isLoading" Label="کد پستی" Variant="Variant.Outlined" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
<MudItem xs="12">
|
<MudItem xs="12">
|
||||||
<MudSwitch @bind-Value="Model.IsDefault" Color="Color.Primary" Label="ایا پیش فرض است" LabelPosition="LabelPosition.End" />
|
<MudSwitch T="bool" @bind-Value="Model.IsDefault" Color="Color.Primary" Label="ایا پیش فرض است" LabelPosition="LabelPosition.End" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<MudTextField T="string" @bind-Value="Model.PostalCode" Disabled="_isLoading" Label="کد پستی" Variant="Variant.Outlined" />
|
<MudTextField T="string" @bind-Value="Model.PostalCode" Disabled="_isLoading" Label="کد پستی" Variant="Variant.Outlined" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
<MudItem xs="12">
|
<MudItem xs="12">
|
||||||
<MudSwitch @bind-Value="Model.IsDefault" Color="Color.Primary" Label="ایا پیش فرض است" LabelPosition="LabelPosition.End" />
|
<MudSwitch T="bool" @bind-Value="Model.IsDefault" Color="Color.Primary" Label="ایا پیش فرض است" LabelPosition="LabelPosition.End" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
Required="true"
|
Required="true"
|
||||||
Lines="3" />
|
Lines="3" />
|
||||||
|
|
||||||
<MudSwitch @bind-Checked="_refundPayment" Color="Color.Primary" />
|
<MudSwitch T="bool" @bind-Value="_refundPayment" Color="Color.Primary" />
|
||||||
<MudText Typo="Typo.body2">
|
<MudText Typo="Typo.body2">
|
||||||
بازگشت وجه به کاربر (در صورت پرداخت موفق)
|
بازگشت وجه به کاربر (در صورت پرداخت موفق)
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|||||||
@@ -51,6 +51,21 @@
|
|||||||
}
|
}
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
|
@if (_model.VatAmount > 0)
|
||||||
|
{
|
||||||
|
<MudDivider Class="my-2" />
|
||||||
|
<MudText Typo="Typo.subtitle2">جزئیات مالیات بر ارزش افزوده</MudText>
|
||||||
|
<MudText Typo="Typo.body2">
|
||||||
|
مبلغ کالا (قبل از مالیات): @_model.VatBaseAmount.ToString("N0") تومان
|
||||||
|
</MudText>
|
||||||
|
<MudText Typo="Typo.body2">
|
||||||
|
مالیات (@_model.VatPercentage.ToString("0")٪): @_model.VatAmount.ToString("N0") تومان
|
||||||
|
</MudText>
|
||||||
|
<MudText Typo="Typo.body2">
|
||||||
|
مبلغ نهایی (شامل مالیات): @_model.VatTotalAmount.ToString("N0") تومان
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
|
||||||
<MudDivider Class="my-2" />
|
<MudDivider Class="my-2" />
|
||||||
<MudText Typo="Typo.subtitle2">Timeline وضعیت</MudText>
|
<MudText Typo="Typo.subtitle2">Timeline وضعیت</MudText>
|
||||||
<MudStack Row="true" Spacing="3" AlignItems="AlignItems.Center">
|
<MudStack Row="true" Spacing="3" AlignItems="AlignItems.Center">
|
||||||
|
|||||||
@@ -131,6 +131,16 @@
|
|||||||
<Columns>
|
<Columns>
|
||||||
<PropertyColumn Property="x => x.Id" Title="شناسه"/>
|
<PropertyColumn Property="x => x.Id" Title="شناسه"/>
|
||||||
<PropertyColumn Property="x => x.Amount" Title="مبلغ"/>
|
<PropertyColumn Property="x => x.Amount" Title="مبلغ"/>
|
||||||
|
<PropertyColumn Property="x => x.VatAmount" Title="مبلغ VAT">
|
||||||
|
<CellTemplate>
|
||||||
|
@(context.Item.VatAmount > 0 ? context.Item.VatAmount.ToString("N0") : "-")
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
|
<PropertyColumn Property="x => x.VatPercentage" Title="درصد VAT">
|
||||||
|
<CellTemplate>
|
||||||
|
@(context.Item.VatPercentage > 0 ? $"{context.Item.VatPercentage:0}٪" : "-")
|
||||||
|
</CellTemplate>
|
||||||
|
</PropertyColumn>
|
||||||
<PropertyColumn Property="x => x.UserFullName" Title="نام کاربر"/>
|
<PropertyColumn Property="x => x.UserFullName" Title="نام کاربر"/>
|
||||||
<PropertyColumn Property="x => x.UserNationalCode" Title="کدملی"/>
|
<PropertyColumn Property="x => x.UserNationalCode" Title="کدملی"/>
|
||||||
<TemplateColumn Title="روش پرداخت">
|
<TemplateColumn Title="روش پرداخت">
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using Blazored.LocalStorage;
|
||||||
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
|
||||||
|
namespace BackOffice.Services.Authorization;
|
||||||
|
|
||||||
|
public class AuthorizationService : IAuthorizationService
|
||||||
|
{
|
||||||
|
private const string PermissionsCacheKey = "BackOffice.Permissions";
|
||||||
|
private readonly ILocalStorageService _localStorage;
|
||||||
|
private readonly AuthenticationStateProvider _authenticationStateProvider;
|
||||||
|
|
||||||
|
public AuthorizationService(
|
||||||
|
ILocalStorageService localStorage,
|
||||||
|
AuthenticationStateProvider authenticationStateProvider)
|
||||||
|
{
|
||||||
|
_localStorage = localStorage;
|
||||||
|
_authenticationStateProvider = authenticationStateProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HasPermissionAsync(string permission)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(permission))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cachedPermissions = await _localStorage.GetItemAsync<HashSet<string>>(PermissionsCacheKey);
|
||||||
|
if (cachedPermissions == null || cachedPermissions.Count == 0)
|
||||||
|
{
|
||||||
|
// فعلاً بر اساس Role ساده تصمیم میگیریم تا زمانی که BFF Permission API آماده شود
|
||||||
|
var role = await GetUserRoleAsync();
|
||||||
|
if (string.IsNullOrWhiteSpace(role))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuperAdmin: همه دسترسیها
|
||||||
|
if (string.Equals(role, "SuperAdmin", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Admin: اجازه دسترسی به بیشتر صفحات مدیریتی
|
||||||
|
if (string.Equals(role, "Admin", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// فعلاً همه permissionهای UI را برای Admin آزاد میکنیم
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inspector: فقط view
|
||||||
|
if (string.Equals(role, "Inspector", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return permission.EndsWith(".view", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedPermissions.Contains(permission, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string?> GetUserRoleAsync()
|
||||||
|
{
|
||||||
|
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
|
||||||
|
var user = authState.User;
|
||||||
|
|
||||||
|
if (user.Identity is not { IsAuthenticated: true })
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var roleClaim = user.FindFirst(ClaimTypes.Role) ?? user.FindFirst("role");
|
||||||
|
return roleClaim?.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace BackOffice.Services.Authorization;
|
||||||
|
|
||||||
|
public interface IAuthorizationService
|
||||||
|
{
|
||||||
|
Task<bool> HasPermissionAsync(string permission);
|
||||||
|
|
||||||
|
Task<string?> GetUserRoleAsync();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,20 +1,24 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Authorization
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
|
@inject BackOffice.Services.Authorization.IAuthorizationService AuthorizationService
|
||||||
|
|
||||||
<MudNavMenu Bordered="false" Class="nav-menu">
|
<MudNavMenu Bordered="false" Class="nav-menu">
|
||||||
<MudText Class="nav-menu__title" Typo="Typo.subtitle2">منوی اصلی</MudText>
|
<MudText Class="nav-menu__title" Typo="Typo.subtitle2">منوی اصلی</MudText>
|
||||||
<MudDivider Class="mb-2" />
|
<MudDivider Class="mb-2" />
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
@if (CanViewDashboard)
|
||||||
Href="/"
|
{
|
||||||
Icon="@Icons.Material.Filled.Dashboard">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
داشبورد
|
Href="/"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.Dashboard">
|
||||||
|
داشبورد
|
||||||
|
</MudNavLink>
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
Href="/dashboard/overview"
|
Href="/dashboard/overview"
|
||||||
Icon="@Icons.Material.Filled.ViewQuilt">
|
Icon="@Icons.Material.Filled.ViewQuilt">
|
||||||
نمای کلی سیستم
|
نمای کلی سیستم
|
||||||
</MudNavLink>
|
</MudNavLink>
|
||||||
|
}
|
||||||
|
|
||||||
<MudDivider Class="my-2" />
|
<MudDivider Class="my-2" />
|
||||||
<MudText Class="nav-menu__title" Typo="Typo.subtitle2">کمیسیون و شبکه</MudText>
|
<MudText Class="nav-menu__title" Typo="Typo.subtitle2">کمیسیون و شبکه</MudText>
|
||||||
@@ -83,47 +87,68 @@
|
|||||||
|
|
||||||
<AuthorizeView Roles="Administrator">
|
<AuthorizeView Roles="Administrator">
|
||||||
<Authorized>
|
<Authorized>
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
@if (CanManagePackages)
|
||||||
Href="@(RouteConstance.Package)"
|
{
|
||||||
Icon="@Icons.Material.Filled.Inventory2">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
مدیریت پکیج
|
Href="@(RouteConstance.Package)"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.Inventory2">
|
||||||
|
مدیریت پکیج
|
||||||
|
</MudNavLink>
|
||||||
|
}
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
@if (CanManageProducts)
|
||||||
Href="@(RouteConstance.Products)"
|
{
|
||||||
Icon="@Icons.Material.Filled.ShoppingBag">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
مدیریت محصول
|
Href="@(RouteConstance.Products)"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.ShoppingBag">
|
||||||
|
مدیریت محصول
|
||||||
|
</MudNavLink>
|
||||||
|
}
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
@if (CanViewOrders)
|
||||||
Href="@(RouteConstance.Orders)"
|
{
|
||||||
Icon="@Icons.Material.Filled.ReceiptLong">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
سفارشها
|
Href="@(RouteConstance.Orders)"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.ReceiptLong">
|
||||||
|
سفارشها
|
||||||
|
</MudNavLink>
|
||||||
|
}
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
@if (CanManageCategories)
|
||||||
Href="@(RouteConstance.Category)"
|
{
|
||||||
Icon="@Icons.Material.Filled.Category">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
مدیریت دستهبندی
|
Href="@(RouteConstance.Category)"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.Category">
|
||||||
|
مدیریت دستهبندی
|
||||||
|
</MudNavLink>
|
||||||
|
}
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
@if (CanManageTags)
|
||||||
Href="/tags"
|
{
|
||||||
Icon="@Icons.Material.Filled.Label">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
مدیریت تگها
|
Href="/tags"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.Label">
|
||||||
|
مدیریت تگها
|
||||||
|
</MudNavLink>
|
||||||
|
}
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
@if (CanManageUsers)
|
||||||
Href="@(RouteConstance.UserPage)"
|
{
|
||||||
Icon="@Icons.Material.Filled.People">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
مدیریت کاربر
|
Href="@(RouteConstance.UserPage)"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.People">
|
||||||
|
مدیریت کاربر
|
||||||
|
</MudNavLink>
|
||||||
|
}
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
@if (CanManageRoles)
|
||||||
Href="@(RouteConstance.Role)"
|
{
|
||||||
Icon="@Icons.Material.Filled.AdminPanelSettings">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
مدیریت نقش
|
Href="@(RouteConstance.Role)"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.AdminPanelSettings">
|
||||||
|
مدیریت نقش
|
||||||
|
</MudNavLink>
|
||||||
|
}
|
||||||
</Authorized>
|
</Authorized>
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
|
|
||||||
@@ -132,37 +157,43 @@
|
|||||||
|
|
||||||
<AuthorizeView Roles="Administrator">
|
<AuthorizeView Roles="Administrator">
|
||||||
<Authorized>
|
<Authorized>
|
||||||
<MudNavGroup Title="فروشگاه تخفیفی" Icon="@Icons.Material.Filled.Discount" Expanded="false">
|
@if (CanManageDiscountShop)
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
{
|
||||||
Href="/discount-products"
|
<MudNavGroup Title="فروشگاه تخفیفی" Icon="@Icons.Material.Filled.Discount" Expanded="false">
|
||||||
Icon="@Icons.Material.Filled.ShoppingBag">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
محصولات تخفیفی
|
Href="/discount-products"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.ShoppingBag">
|
||||||
|
محصولات تخفیفی
|
||||||
|
</MudNavLink>
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
Href="/discount-categories"
|
Href="/discount-categories"
|
||||||
Icon="@Icons.Material.Filled.Category">
|
Icon="@Icons.Material.Filled.Category">
|
||||||
دستهبندیهای فروشگاه
|
دستهبندیهای فروشگاه
|
||||||
</MudNavLink>
|
</MudNavLink>
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
Href="/discount-orders"
|
Href="/discount-orders"
|
||||||
Icon="@Icons.Material.Filled.ShoppingCart">
|
Icon="@Icons.Material.Filled.ShoppingCart">
|
||||||
سفارشات فروشگاه
|
سفارشات فروشگاه
|
||||||
</MudNavLink>
|
</MudNavLink>
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
Href="/discount-sales-reports"
|
Href="/discount-sales-reports"
|
||||||
Icon="@Icons.Material.Filled.BarChart">
|
Icon="@Icons.Material.Filled.BarChart">
|
||||||
گزارش فروش
|
گزارش فروش
|
||||||
</MudNavLink>
|
</MudNavLink>
|
||||||
</MudNavGroup>
|
</MudNavGroup>
|
||||||
|
}
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
@if (CanManagePublicMessages)
|
||||||
Href="/public-messages"
|
{
|
||||||
Icon="@Icons.Material.Filled.Campaign">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
پیامهای عمومی
|
Href="/public-messages"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.Campaign">
|
||||||
|
پیامهای عمومی
|
||||||
|
</MudNavLink>
|
||||||
|
}
|
||||||
</Authorized>
|
</Authorized>
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
|
|
||||||
@@ -171,23 +202,32 @@
|
|||||||
|
|
||||||
<AuthorizeView Roles="Administrator">
|
<AuthorizeView Roles="Administrator">
|
||||||
<Authorized>
|
<Authorized>
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
@if (CanViewSystemAlerts)
|
||||||
Href="/system/alerts"
|
{
|
||||||
Icon="@Icons.Material.Filled.NotificationsActive">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
مدیریت هشدارها
|
Href="/system/alerts"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.NotificationsActive">
|
||||||
|
مدیریت هشدارها
|
||||||
|
</MudNavLink>
|
||||||
|
}
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
@if (CanViewSystemHealth)
|
||||||
Href="/system/health"
|
{
|
||||||
Icon="@Icons.Material.Filled.MonitorHeart">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
سلامت سیستم
|
Href="/system/health"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.MonitorHeart">
|
||||||
|
سلامت سیستم
|
||||||
|
</MudNavLink>
|
||||||
|
}
|
||||||
|
|
||||||
<MudNavLink Match="NavLinkMatch.Prefix"
|
@if (CanManageSystemConfiguration)
|
||||||
Href="/system/configuration"
|
{
|
||||||
Icon="@Icons.Material.Filled.Tune">
|
<MudNavLink Match="NavLinkMatch.Prefix"
|
||||||
تنظیمات سیستم
|
Href="/system/configuration"
|
||||||
</MudNavLink>
|
Icon="@Icons.Material.Filled.Tune">
|
||||||
|
تنظیمات سیستم
|
||||||
|
</MudNavLink>
|
||||||
|
}
|
||||||
</Authorized>
|
</Authorized>
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
|
|
||||||
@@ -215,4 +255,50 @@
|
|||||||
await LocalStorageService.RemoveItemAsync("AuthToken");
|
await LocalStorageService.RemoveItemAsync("AuthToken");
|
||||||
Navigation.NavigateTo("/Login");
|
Navigation.NavigateTo("/Login");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool _initialized;
|
||||||
|
private bool CanViewDashboard;
|
||||||
|
private bool CanManagePackages;
|
||||||
|
private bool CanManageProducts;
|
||||||
|
private bool CanViewOrders;
|
||||||
|
private bool CanManageCategories;
|
||||||
|
private bool CanManageTags;
|
||||||
|
private bool CanManageUsers;
|
||||||
|
private bool CanManageRoles;
|
||||||
|
private bool CanManageDiscountShop;
|
||||||
|
private bool CanManagePublicMessages;
|
||||||
|
private bool CanViewSystemAlerts;
|
||||||
|
private bool CanViewSystemHealth;
|
||||||
|
private bool CanManageSystemConfiguration;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await InitializePermissionsAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitializePermissionsAsync()
|
||||||
|
{
|
||||||
|
if (_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanViewDashboard = await AuthorizationService.HasPermissionAsync("dashboard.view");
|
||||||
|
|
||||||
|
CanManagePackages = await AuthorizationService.HasPermissionAsync("packages.manage");
|
||||||
|
CanManageProducts = await AuthorizationService.HasPermissionAsync("products.view");
|
||||||
|
CanViewOrders = await AuthorizationService.HasPermissionAsync("orders.view");
|
||||||
|
CanManageCategories = await AuthorizationService.HasPermissionAsync("categories.manage");
|
||||||
|
CanManageTags = await AuthorizationService.HasPermissionAsync("tags.manage");
|
||||||
|
CanManageUsers = await AuthorizationService.HasPermissionAsync("users.view");
|
||||||
|
CanManageRoles = await AuthorizationService.HasPermissionAsync("roles.manage");
|
||||||
|
CanManageDiscountShop = await AuthorizationService.HasPermissionAsync("discountshop.manage");
|
||||||
|
CanManagePublicMessages = await AuthorizationService.HasPermissionAsync("publicmessages.view");
|
||||||
|
CanViewSystemAlerts = await AuthorizationService.HasPermissionAsync("system.alerts.view");
|
||||||
|
CanViewSystemHealth = await AuthorizationService.HasPermissionAsync("system.health.view");
|
||||||
|
CanManageSystemConfiguration = await AuthorizationService.HasPermissionAsync("settings.manage_configuration");
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user