Compare commits

..

3 Commits

Author SHA1 Message Date
masoodafar-web fe79089770 feat(user-page): enhance user main page with additional address and postal code fields in export
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 6m4s
- Added new columns for default address and postal code in the user export functionality.
- Updated the export logic to handle the new fields and changed the file format from CSV to Excel (.xls) for better compatibility.
- Bumped Foursat.CMSMicroservice.Protobuf version to 0.0.209 to align with recent changes.
- Made minor adjustments to the .gitignore for clarity.
2026-08-14 03:19:10 +03:30
masoodafar-web aa97d25302 feat(report): optimize customer package purchases report by removing pagination and enhancing filter application
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m55s
- Simplified the data retrieval process by eliminating pagination, allowing for all matching records to be fetched in a single request.
- Improved filter application logic to streamline the report generation process.
- Updated the handling of the result set to ensure compatibility with the new data retrieval method.
2026-08-09 00:40:36 +03:30
masoodafar-web 24ae4ec15c feat(date-range-picker): enhance date range picker functionality with external parameters and notifications
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 7m20s
- Added support for external From and To parameters to synchronize date range selections.
- Implemented OnParametersSet to handle updates from parent components without triggering unnecessary updates.
- Introduced NotifyRangeChangedAsync to streamline notifications for date range changes.
- Refactored date range application logic to improve clarity and maintainability.
2026-08-09 00:02:54 +03:30
6 changed files with 126 additions and 71 deletions
+1 -1
View File
@@ -11,7 +11,7 @@
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
+1 -1
View File
@@ -143,7 +143,7 @@
<ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj" />
</ItemGroup>
<ItemGroup Condition="!Exists('../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj')">
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.208" />
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.209" />
</ItemGroup>
<!-- ============================================================ -->
@@ -8,21 +8,46 @@ namespace BackOffice.Common.BaseComponents;
public partial class DateRangePicker
{
private DateRange _dateRange = new();
private MudDateRangePicker _picker = default!;
private DateTime? _lastSyncedFrom;
private DateTime? _lastSyncedTo;
private MudDateRangePicker _picker;
[Parameter] public string Label { get; set; } = "انتخاب بازه زمانی";
[Parameter] public DateTime? DefaultStart { get; set; }
[Parameter] public DateTime? DefaultEnd { get; set; }
[Parameter] public EventCallback<DateRange> OnChanged { get; set; }
[Parameter] public DateTime? From { get; set; }
[Parameter] public EventCallback<DateTime?> FromChanged { get; set; }
[Parameter] public DateTime? To { get; set; }
[Parameter] public EventCallback<DateTime?> ToChanged { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
if (DefaultStart.HasValue)
_dateRange.Start = DefaultStart.Value.Date;
if (DefaultEnd.HasValue)
_dateRange.End = DefaultEnd.Value.Date;
var start = From ?? DefaultStart;
var end = To ?? DefaultEnd;
ApplyExternalRange(start, end);
_lastSyncedFrom = From;
_lastSyncedTo = To;
}
protected override void OnParametersSet()
{
// فقط وقتی والد From/To را عوض کرده (مثلاً پاک کردن فیلتر)، نه هنگام انتخاب داخل پیکر
if (From == _lastSyncedFrom && To == _lastSyncedTo)
return;
_lastSyncedFrom = From;
_lastSyncedTo = To;
ApplyExternalRange(From, To);
}
private void ApplyExternalRange(DateTime? start, DateTime? end)
{
_dateRange = new DateRange(start?.Date, end?.Date);
}
public CultureInfo GetPersianCulture()
{
var culture = new CultureInfo("fa-IR");
@@ -53,19 +78,35 @@ public partial class DateRangePicker
culture.NumberFormat.NumberNegativePattern = 0;
return culture;
}
private async Task OnClickOK()
{
if (_picker.DateRange is not null)
{
await OnChanged.InvokeAsync(_picker.DateRange);
}
var range = _picker.DateRange ?? _dateRange;
_dateRange = range ?? new DateRange();
await NotifyRangeChangedAsync(_dateRange);
await _picker.CloseAsync();
}
private async Task OnClickClear()
{
_dateRange = new DateRange();
await OnChanged.InvokeAsync(_dateRange);
await NotifyRangeChangedAsync(_dateRange);
await _picker.CloseAsync();
}
private async Task NotifyRangeChangedAsync(DateRange range)
{
var start = range.Start?.Date;
var end = range.End?.Date;
_lastSyncedFrom = start;
_lastSyncedTo = end;
if (From != start)
await FromChanged.InvokeAsync(start);
if (To != end)
await ToChanged.InvokeAsync(end);
await OnChanged.InvokeAsync(range);
}
}
@@ -205,31 +205,19 @@
_exporting = true;
try
{
var filter = BuildFilter();
var all = new List<CustomerPackagePurchaseRollupModel>();
const int pageSize = 500;
var page = 1;
long totalCount;
do
{
var result = await PurchaseClient.GetCustomerPackagePurchaseRollupAsync(
new GetCustomerPackagePurchaseRollupRequest
// PageSize=0 → همهٔ ردیف‌های مطابق فیلتر، مستقل از صفحه‌بندی گرید
var result = await PurchaseClient.GetCustomerPackagePurchaseRollupAsync(
new GetCustomerPackagePurchaseRollupRequest
{
PaginationState = new CMSMicroservice.Protobuf.Protos.PaginationState
{
PaginationState = new CMSMicroservice.Protobuf.Protos.PaginationState
{
PageNumber = page,
PageSize = pageSize
},
Filter = filter
});
var batch = result?.Models?.ToList() ?? new();
all.AddRange(batch);
totalCount = result?.MetaData?.TotalCount ?? 0;
page++;
} while (all.Count < totalCount && page <= 50);
PageNumber = 1,
PageSize = 0
},
Filter = BuildFilter()
});
var all = result?.Models?.ToList() ?? new();
if (all.Count == 0)
{
Snackbar.Add("داده‌ای برای خروجی با فیلتر فعلی وجود ندارد.", Severity.Info);
+17 -2
View File
@@ -28,6 +28,8 @@
<col />
<col />
<col />
<col style="min-width: 180px;" />
<col />
<col style="width: 58px;" />
</ColGroup>
<ToolBarContent>
@@ -46,8 +48,21 @@
</CellTemplate>
</PropertyColumn>
<PropertyColumn Property="x => x.NationalCode" Title="کدملی" />
<TemplateColumn Title="آدرس پیش‌فرض">
<CellTemplate>
<MudText Typo="Typo.caption" Style="max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
Title="@(context.Item.DefaultAddress ?? "")">
@(string.IsNullOrWhiteSpace(context.Item.DefaultAddress) ? "-" : context.Item.DefaultAddress)
</MudText>
</CellTemplate>
</TemplateColumn>
<TemplateColumn Title="کدپستی پیش‌فرض">
<CellTemplate>
<MudText Typo="Typo.caption">
@(string.IsNullOrWhiteSpace(context.Item.DefaultPostalCode) ? "-" : context.Item.DefaultPostalCode)
</MudText>
</CellTemplate>
</TemplateColumn>
<TemplateColumn StickyLeft="true" Title="عملیات" CellStyle="text-wrap: nowrap;" HeaderStyle="text-wrap: nowrap;">
<CellTemplate>
+43 -32
View File
@@ -1,11 +1,10 @@
using CMSMicroservice.Protobuf.Protos.User;
using BackOffice.Common.BaseComponents;
using BackOffice.Common.Utilities;
using BackOffice.Pages.User.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using MudBlazor;
using System.Text;
using DateTimeConverterCL;
using DataModel = CMSMicroservice.Protobuf.Protos.User.GetAllUserByFilterResponseModel;
namespace BackOffice.Pages.User;
@@ -84,51 +83,63 @@ public partial class UserMainPage
{
try
{
var exportRequest = new GetAllUserByFilterRequest
var filter = _request.Filter?.Clone() ?? new();
var all = new List<DataModel>();
const int pageSize = 200;
var page = 1;
while (page <= 500)
{
Filter = _request.Filter?.Clone() ?? new(),
PaginationState = new() { PageNumber = 1, PageSize = 1000 }
};
var result = await UserContract.GetAllUserByFilterAsync(new GetAllUserByFilterRequest
{
Filter = filter,
PaginationState = new() { PageNumber = page, PageSize = pageSize }
});
var result = await UserContract.GetAllUserByFilterAsync(exportRequest);
var batch = result?.Models?.ToList() ?? new();
if (batch.Count == 0)
break;
if (result?.Models == null || !result.Models.Any())
all.AddRange(batch);
var hasNext = result?.MetaData?.HasNext == true;
if (!hasNext || batch.Count < pageSize)
break;
page++;
}
if (all.Count == 0)
{
Snackbar.Add("داده‌ای برای خروجی وجود ندارد.", Severity.Info);
return;
}
var sb = new StringBuilder();
sb.AppendLine("شناسه,موبایل,نام,نام خانوادگی,کدملی");
foreach (var u in result.Models)
var headers = new[]
{
sb.AppendLine(string.Join(",",
u.Id,
EscapeCsv(u.Mobile),
EscapeCsv(u.FirstName),
EscapeCsv(u.LastName),
EscapeCsv(u.NationalCode)));
}
"شناسه", "موبایل", "نام", "نام خانوادگی", "کدملی",
"آدرس پیش‌فرض", "کدپستی پیش‌فرض"
};
var bytes = Encoding.UTF8.GetPreamble().Concat(Encoding.UTF8.GetBytes(sb.ToString())).ToArray();
var base64 = Convert.ToBase64String(bytes);
var filename = $"users-{DateTime.Now:yyyyMMddHHmmss}.csv";
var rows = all.Select(u => new object?[]
{
u.Id,
u.Mobile,
u.FirstName,
u.LastName,
u.NationalCode,
u.DefaultAddress,
u.DefaultPostalCode
});
await JsRuntime.InvokeVoidAsync("jsSaveAsFile", filename, base64);
Snackbar.Add("خروجی Excel (CSV) آماده شد.", Severity.Success);
var filename = $"users-{DateTime.Now:yyyyMMddHHmmss}.xls";
await JsRuntime.InvokeVoidAsync("jsSaveAsFile", filename,
CsvExportHelper.ToExcelXmlBase64("کاربران", headers, rows));
Snackbar.Add($"خروجی Excel آماده شد ({all.Count} کاربر).", Severity.Success);
}
catch (Exception ex)
{
Snackbar.Add($"خطا در خروجی Excel: {ex.Message}", Severity.Error);
}
}
private static string EscapeCsv(string value)
{
if (string.IsNullOrEmpty(value)) return "";
if (value.Contains(',') || value.Contains('"') || value.Contains('\n'))
return $"\"{value.Replace("\"", "\"\"")}\"";
return value;
}
}