diff --git a/src/BackOffice/BackOffice.csproj b/src/BackOffice/BackOffice.csproj index 026414b..358c6ee 100644 --- a/src/BackOffice/BackOffice.csproj +++ b/src/BackOffice/BackOffice.csproj @@ -116,7 +116,7 @@ - + diff --git a/src/BackOffice/Common/Configure/ConfigureService.cs b/src/BackOffice/Common/Configure/ConfigureService.cs index b933d23..f2f9d62 100644 --- a/src/BackOffice/Common/Configure/ConfigureService.cs +++ b/src/BackOffice/Common/Configure/ConfigureService.cs @@ -12,6 +12,7 @@ using Foursat.BackOffice.BFF.ClubMembership.Protos; using Foursat.BackOffice.BFF.Configuration.Protos; using Foursat.BackOffice.BFF.NetworkMembership.Protos; using Foursat.BackOffice.BFF.Health.Protobuf; +using BackOffice.BFF.Configuration.Protobuf.Protos.AppVersion; // TODO: Create these proto projects - temporarily disabled // using BackOffice.BFF.DiscountProduct.Protobuf.Protos.DiscountProduct; @@ -68,6 +69,7 @@ public static class ConfigureServices // Application Services services.AddScoped(); + services.AddScoped(); // TODO: Re-enable when proto projects are created // services.AddScoped(); // services.AddScoped(); @@ -116,6 +118,7 @@ public static class ConfigureServices services.AddTransient(sp => new ClubMembershipContract.ClubMembershipContractClient(sp.GetRequiredService())); services.AddTransient(sp => new ConfigurationContract.ConfigurationContractClient(sp.GetRequiredService())); services.AddTransient(sp => new HealthContract.HealthContractClient(sp.GetRequiredService())); + services.AddTransient(sp => new AppVersionContract.AppVersionContractClient(sp.GetRequiredService())); // TODO: Re-enable when proto projects are created // Discount Shop Services diff --git a/src/BackOffice/Pages/Settings/AppVersions.razor b/src/BackOffice/Pages/Settings/AppVersions.razor new file mode 100644 index 0000000..3e815f0 --- /dev/null +++ b/src/BackOffice/Pages/Settings/AppVersions.razor @@ -0,0 +1,310 @@ +@page "/settings/app-versions" +@attribute [Authorize] + +@using BackOffice.Services.AppVersion +@using BackOffice.Pages.Settings.Components +@inject IAppVersionService AppVersionService +@inject IDialogService DialogService +@inject ISnackbar Snackbar + +مدیریت نسخه اپلیکیشن‌ها + + + + + مدیریت نسخه اپلیکیشن‌ها + + + مدیریت نسخه‌های اپلیکیشن‌های موبایل و کنترل به‌روزرسانی اجباری + + + + + + + + بارگذاری مجدد + + + + @if (_loading) + { + + } + + + + + + + + + + @context.Item.AppNameDisplay + + + + + + + + v@context.Item.CurrentVersion + + + + + + + @if (!string.IsNullOrEmpty(context.Item.MinRequiredVersion)) + { + + v@context.Item.MinRequiredVersion + + } + else + { + - + } + + + + + + @if (context.Item.RequiresFullCacheClear) + { + + } + else + { + + } + + + + + + @if (context.Item.IsActive) + { + فعال + } + else + { + غیرفعال + } + + + + + + + + + + + + + + + + @* Info Cards *@ + + @foreach (var version in _versions.Where(v => v.IsActive)) + { + + + + + + + + + + @version.AppNameDisplay + @version.AppName + + + + + + + + + نسخه فعلی: + v@version.CurrentVersion + + + + حداقل نسخه: + @if (!string.IsNullOrEmpty(version.MinRequiredVersion)) + { + v@version.MinRequiredVersion + } + else + { + تنظیم نشده + } + + + @if (version.RequiresFullCacheClear) + { + + نیاز به پاکسازی کامل کش دارد + + } + + @if (!string.IsNullOrEmpty(version.UpdateMessage)) + { + + پیام به‌روزرسانی: + @version.UpdateMessage + } + + + + + } + + + +@code { + private List _versions = new(); + private bool _loading = false; + private bool _includeInactive = false; + + protected override async Task OnInitializedAsync() + { + await LoadVersions(); + } + + private async Task LoadVersions() + { + _loading = true; + try + { + _versions = await AppVersionService.GetAllAsync(_includeInactive); + Snackbar.Add("اطلاعات نسخه‌ها بارگذاری شد", Severity.Success); + } + catch (Exception ex) + { + Snackbar.Add($"خطا در بارگذاری: {ex.Message}", Severity.Error); + } + finally + { + _loading = false; + } + } + + private async Task OpenEditDialog(AppVersionDto version) + { + var parameters = new DialogParameters + { + { x => x.Model, new UpdateAppVersionDto + { + AppName = version.AppName, + CurrentVersion = version.CurrentVersion, + MinRequiredVersion = version.MinRequiredVersion, + RequiresFullCacheClear = version.RequiresFullCacheClear, + UpdateMessage = version.UpdateMessage, + ReleaseNotes = version.ReleaseNotes + } + } + }; + + var options = new DialogOptions + { + CloseButton = true, + MaxWidth = MaxWidth.Medium, + FullWidth = true + }; + + var dialog = await DialogService.ShowAsync( + $"ویرایش نسخه {version.AppNameDisplay}", + parameters, + options); + + var result = await dialog.Result; + + if (result is { Canceled: false, Data: UpdateAppVersionDto dto }) + { + try + { + await AppVersionService.UpdateAsync(dto); + Snackbar.Add("نسخه با موفقیت به‌روزرسانی شد", Severity.Success); + await LoadVersions(); + } + catch (Exception ex) + { + Snackbar.Add($"خطا در به‌روزرسانی: {ex.Message}", Severity.Error); + } + } + } + + private void OpenDetailsDialog(AppVersionDto version) + { + var parameters = new DialogParameters + { + { "ContentText", BuildDetailsContent(version) }, + { "ButtonText", "بستن" }, + { "Color", Color.Primary } + }; + + DialogService.Show($"جزئیات {version.AppNameDisplay}", parameters); + } + + private string BuildDetailsContent(AppVersionDto version) + { + var lines = new List + { + $"نام اپلیکیشن: {version.AppName}", + $"نسخه فعلی: {version.CurrentVersion}", + $"حداقل نسخه: {version.MinRequiredVersion ?? "تنظیم نشده"}", + $"نیاز به پاکسازی کش: {(version.RequiresFullCacheClear ? "بله" : "خیر")}", + $"وضعیت: {(version.IsActive ? "فعال" : "غیرفعال")}", + "", + "پیام به‌روزرسانی:", + version.UpdateMessage ?? "ندارد", + "", + "یادداشت‌های انتشار:", + version.ReleaseNotes ?? "ندارد", + "", + $"تاریخ ایجاد: {version.Created?.ToString("yyyy/MM/dd HH:mm") ?? "-"}", + $"آخرین تغییر: {version.LastModified?.ToString("yyyy/MM/dd HH:mm") ?? "-"}" + }; + return string.Join(Environment.NewLine, lines); + } + + private string GetAppIcon(string appName) => appName switch + { + "FoursatMarketApp" => Icons.Material.Filled.ShoppingCart, + "FoursatClubApp" => Icons.Material.Filled.Groups, + _ => Icons.Material.Filled.PhoneAndroid + }; + + private Color GetAppColor(string appName) => appName switch + { + "FoursatMarketApp" => Color.Primary, + "FoursatClubApp" => Color.Secondary, + _ => Color.Default + }; +} diff --git a/src/BackOffice/Pages/Settings/Components/AppVersionEditDialog.razor b/src/BackOffice/Pages/Settings/Components/AppVersionEditDialog.razor new file mode 100644 index 0000000..e3231b9 --- /dev/null +++ b/src/BackOffice/Pages/Settings/Components/AppVersionEditDialog.razor @@ -0,0 +1,83 @@ +@using BackOffice.Services.AppVersion +@inject ISnackbar Snackbar + + + + + + + + + + + + + + + + + + + + + + + + + + انصراف + + + ذخیره + + + + +@code { + [CascadingParameter] + private IMudDialogInstance MudDialog { get; set; } = null!; + + [Parameter] + public UpdateAppVersionDto Model { get; set; } = new(); + + private MudForm? _form; + private bool _isValid; + + private void Cancel() => MudDialog.Cancel(); + + private void Submit() + { + if (_isValid) + { + MudDialog.Close(DialogResult.Ok(Model)); + } + } +} diff --git a/src/BackOffice/Services/AppVersion/AppVersionService.cs b/src/BackOffice/Services/AppVersion/AppVersionService.cs new file mode 100644 index 0000000..4a6b26d --- /dev/null +++ b/src/BackOffice/Services/AppVersion/AppVersionService.cs @@ -0,0 +1,90 @@ +using BackOffice.BFF.Configuration.Protobuf.Protos.AppVersion; +using Google.Protobuf.WellKnownTypes; + +namespace BackOffice.Services.AppVersion; + +public class AppVersionService : IAppVersionService +{ + private readonly AppVersionContract.AppVersionContractClient _client; + + public AppVersionService(AppVersionContract.AppVersionContractClient client) + { + _client = client; + } + + public async Task> GetAllAsync(bool includeInactive = false) + { + var request = new GetAllAppVersionsRequest + { + IncludeInactive = includeInactive + }; + + var response = await _client.GetAllAppVersionsAsync(request); + + return response.Items.Select(item => new AppVersionDto + { + Id = item.Id, + AppName = item.AppName, + CurrentVersion = item.CurrentVersion, + MinRequiredVersion = item.MinRequiredVersion, + RequiresFullCacheClear = item.RequiresFullCacheClear, + UpdateMessage = item.UpdateMessage, + ReleaseNotes = item.ReleaseNotes, + IsActive = item.IsActive, + Created = item.Created?.ToDateTime(), + LastModified = item.LastModified?.ToDateTime() + }).ToList(); + } + + public async Task GetByNameAsync(string appName) + { + var request = new GetAppVersionRequest + { + AppName = appName + }; + + var response = await _client.GetAppVersionAsync(request); + + if (!response.Found || response.Item == null) + return null; + + var item = response.Item; + return new AppVersionDto + { + Id = item.Id, + AppName = item.AppName, + CurrentVersion = item.CurrentVersion, + MinRequiredVersion = item.MinRequiredVersion, + RequiresFullCacheClear = item.RequiresFullCacheClear, + UpdateMessage = item.UpdateMessage, + ReleaseNotes = item.ReleaseNotes, + IsActive = item.IsActive, + Created = item.Created?.ToDateTime(), + LastModified = item.LastModified?.ToDateTime() + }; + } + + public async Task UpdateAsync(UpdateAppVersionDto dto) + { + var request = new UpdateAppVersionRequest + { + AppName = dto.AppName, + CurrentVersion = dto.CurrentVersion, + RequiresFullCacheClear = dto.RequiresFullCacheClear + }; + + if (!string.IsNullOrWhiteSpace(dto.MinRequiredVersion)) + request.MinRequiredVersion = dto.MinRequiredVersion; + + if (!string.IsNullOrWhiteSpace(dto.UpdateMessage)) + request.UpdateMessage = dto.UpdateMessage; + + if (!string.IsNullOrWhiteSpace(dto.ReleaseNotes)) + request.ReleaseNotes = dto.ReleaseNotes; + + if (!string.IsNullOrWhiteSpace(dto.UpdateReason)) + request.UpdateReason = dto.UpdateReason; + + await _client.UpdateAppVersionAsync(request); + } +} diff --git a/src/BackOffice/Services/AppVersion/IAppVersionService.cs b/src/BackOffice/Services/AppVersion/IAppVersionService.cs new file mode 100644 index 0000000..fa3dd37 --- /dev/null +++ b/src/BackOffice/Services/AppVersion/IAppVersionService.cs @@ -0,0 +1,41 @@ +namespace BackOffice.Services.AppVersion; + +public interface IAppVersionService +{ + Task> GetAllAsync(bool includeInactive = false); + Task GetByNameAsync(string appName); + Task UpdateAsync(UpdateAppVersionDto dto); +} + +public class AppVersionDto +{ + public long Id { get; set; } + public string AppName { get; set; } = string.Empty; + public string AppNameDisplay => GetDisplayName(AppName); + public string CurrentVersion { get; set; } = string.Empty; + public string MinRequiredVersion { get; set; } = string.Empty; + public bool RequiresFullCacheClear { get; set; } + public string UpdateMessage { get; set; } = string.Empty; + public string ReleaseNotes { get; set; } = string.Empty; + public bool IsActive { get; set; } + public DateTime? Created { get; set; } + public DateTime? LastModified { get; set; } + + private static string GetDisplayName(string appName) => appName switch + { + "FoursatMarketApp" => "اپ مارکت فورست", + "FoursatClubApp" => "اپ باشگاه فورست", + _ => appName + }; +} + +public class UpdateAppVersionDto +{ + public string AppName { get; set; } = string.Empty; + public string CurrentVersion { get; set; } = string.Empty; + public string? MinRequiredVersion { get; set; } + public bool RequiresFullCacheClear { get; set; } + public string? UpdateMessage { get; set; } + public string? ReleaseNotes { get; set; } + public string? UpdateReason { get; set; } +}