feat: Add Licenses page + SitePageSettingsService
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 6m18s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 6m18s
- SitePageSettingsService: new gRPC client for simplified page settings - Licenses.razor: new page showing license certificates from CMS - RouteConstants: added Licenses route - Footer + MainLayout: added Licenses navigation link - Proto NuGet updated to 0.0.180
This commit is contained in:
@@ -22,6 +22,7 @@ using CMSMicroservice.Protobuf.Protos.NetworkMembership;
|
||||
using CMSMicroservice.Protobuf.Protos.Commission;
|
||||
using CMSMicroservice.Protobuf.Protos.AppVersion;
|
||||
using CMSMicroservice.Protobuf.Protos.SitePage;
|
||||
using CMSMicroservice.Protobuf.Protos.SitePageSettings;
|
||||
using CMSMicroservice.Protobuf.Protos.BlogPost;
|
||||
using CMSMicroservice.Protobuf.Protos.BlogCategory;
|
||||
using CMSMicroservice.Protobuf.Protos.DiscountProduct;
|
||||
@@ -83,6 +84,8 @@ public static class ConfigureServices
|
||||
services.AddScoped<DiscountProductService>();
|
||||
services.AddScoped<DiscountCartService>();
|
||||
services.AddScoped<DiscountOrderService>();
|
||||
// Site Page Settings Service (simplified page management)
|
||||
services.AddScoped<SitePageSettingsService>();
|
||||
// Device detection: very light, dependency-free
|
||||
services.AddTransient<IDeviceDetector, DeviceDetector>();
|
||||
// PDF generation (Chromium only)
|
||||
@@ -133,6 +136,7 @@ public static class ConfigureServices
|
||||
services.AddScoped(CreateAuthenticatedClient<CMSMicroservice.Protobuf.Protos.Commission.CommissionContract.CommissionContractClient>);
|
||||
services.AddScoped(CreateAuthenticatedClient<CMSMicroservice.Protobuf.Protos.AppVersion.AppVersionContract.AppVersionContractClient>);
|
||||
services.AddScoped(CreateAuthenticatedClient<CMSMicroservice.Protobuf.Protos.SitePage.SitePageContract.SitePageContractClient>);
|
||||
services.AddScoped(CreateAuthenticatedClient<SitePageSettingsContract.SitePageSettingsContractClient>);
|
||||
services.AddScoped(CreateAuthenticatedClient<CMSMicroservice.Protobuf.Protos.BlogPost.BlogPostContract.BlogPostContractClient>);
|
||||
services.AddScoped(CreateAuthenticatedClient<CMSMicroservice.Protobuf.Protos.BlogCategory.BlogCategoryContract.BlogCategoryContractClient>);
|
||||
// Image Resolver Service
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DateTimeConverterCL" Version="1.0.0" />
|
||||
<!-- Replace all FrontOffice.BFF protobuf packages with CMS protobuf -->
|
||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.179" />
|
||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.180" />
|
||||
<!--<ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj" />-->
|
||||
<PackageReference Include="MudBlazor" Version="8.14.0" />
|
||||
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
@attribute [Route(RouteConstants.Licenses.Index)]
|
||||
@inject SitePageSettingsService PageSettingsService
|
||||
|
||||
<PageTitle>مجوزها و گواهینامهها | کارا بازار سلامت</PageTitle>
|
||||
|
||||
@if (_loading)
|
||||
{
|
||||
<MudContainer MaxWidth="MaxWidth.Large" Class="py-16">
|
||||
<LoadingState />
|
||||
</MudContainer>
|
||||
}
|
||||
else
|
||||
{
|
||||
<!-- Hero Section -->
|
||||
<section class="licenses-hero-section py-16">
|
||||
<MudContainer MaxWidth="MaxWidth.Large">
|
||||
<MudStack Spacing="3" AlignItems="AlignItems.Center" Class="text-center">
|
||||
<MudChip T="string" Color="Color.Secondary" Variant="Variant.Filled">مجوزها و گواهینامهها</MudChip>
|
||||
<MudText Typo="Typo.h2" Class="mb-3">
|
||||
@(_pageData?.HeroTitle ?? "مجوزها و گواهینامههای رسمی")
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body1" Class="mud-text-secondary" Style="max-width: 700px;">
|
||||
@(_pageData?.HeroSubtitle ?? "ما با دریافت مجوزهای رسمی و تأییدیههای معتبر، اطمینان شما را در استفاده از خدمات خود جلب کردهایم.")
|
||||
</MudText>
|
||||
</MudStack>
|
||||
</MudContainer>
|
||||
</section>
|
||||
|
||||
<MudStack Spacing="8" Class="pb-12">
|
||||
|
||||
<!-- Licenses Gallery -->
|
||||
<section class="py-12">
|
||||
<MudContainer MaxWidth="MaxWidth.Large">
|
||||
<MudText Typo="Typo.h3" Align="Align.Center" Class="mb-8">مجوزهای ما</MudText>
|
||||
@if (_licenseImages.Any())
|
||||
{
|
||||
<MudGrid Spacing="4" Justify="Justify.Center">
|
||||
@foreach (var license in _licenseImages)
|
||||
{
|
||||
<MudItem xs="12" sm="6" md="4">
|
||||
<MudCard Elevation="2" Class="h-100">
|
||||
<MudCardMedia Image="@license.ImagePath" Height="250" />
|
||||
<MudCardContent>
|
||||
@if (!string.IsNullOrWhiteSpace(license.Title))
|
||||
{
|
||||
<MudText Typo="Typo.h6" Align="Align.Center" Class="mb-1">@license.Title</MudText>
|
||||
}
|
||||
@if (!string.IsNullOrWhiteSpace(license.Description))
|
||||
{
|
||||
<MudText Typo="Typo.body2" Align="Align.Center" Class="mud-text-secondary">@license.Description</MudText>
|
||||
}
|
||||
</MudCardContent>
|
||||
@if (!string.IsNullOrWhiteSpace(license.LinkUrl))
|
||||
{
|
||||
<MudCardActions Class="justify-center">
|
||||
<MudButton Variant="Variant.Text"
|
||||
Color="Color.Primary"
|
||||
Href="@license.LinkUrl"
|
||||
Target="_blank"
|
||||
StartIcon="@Icons.Material.Filled.OpenInNew">
|
||||
مشاهده جزئیات
|
||||
</MudButton>
|
||||
</MudCardActions>
|
||||
}
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
}
|
||||
</MudGrid>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Variant="Variant.Text" Class="my-8" Dense="true">
|
||||
اطلاعات مجوزها بهزودی بارگذاری خواهد شد.
|
||||
</MudAlert>
|
||||
}
|
||||
</MudContainer>
|
||||
</section>
|
||||
|
||||
<!-- CTA Section -->
|
||||
<section class="py-12">
|
||||
<MudContainer MaxWidth="MaxWidth.Medium">
|
||||
<MudPaper Elevation="3" Class="pa-8 rounded-xl text-center">
|
||||
<MudIcon Icon="@Icons.Material.Filled.VerifiedUser" Size="Size.Large" Color="Color.Primary" Class="mb-4" />
|
||||
<MudText Typo="Typo.h4" Class="mb-3">نیاز به اطلاعات بیشتر دارید؟</MudText>
|
||||
<MudText Typo="Typo.body1" Class="mud-text-secondary mb-6">
|
||||
برای دریافت اطلاعات بیشتر درباره مجوزها و گواهینامههای ما، با ما تماس بگیرید.
|
||||
</MudText>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
Size="Size.Large"
|
||||
OnClick="() => Navigation.NavigateTo(RouteConstants.Contact.Index)">
|
||||
تماس با ما
|
||||
</MudButton>
|
||||
</MudPaper>
|
||||
</MudContainer>
|
||||
</section>
|
||||
|
||||
</MudStack>
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using FrontOffice.Main.Utilities;
|
||||
|
||||
namespace FrontOffice.Main.Pages;
|
||||
|
||||
public partial class Licenses
|
||||
{
|
||||
private bool _loading = true;
|
||||
private PageSettingsDto? _pageData;
|
||||
private List<PageSettingsImageDto> _licenseImages = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_pageData = await PageSettingsService.GetPageAsync("licenses");
|
||||
|
||||
if (_pageData != null)
|
||||
{
|
||||
_licenseImages = _pageData.GetImages("licenses");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fallback: page remains null → hardcoded content will render
|
||||
}
|
||||
finally
|
||||
{
|
||||
_loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,7 @@
|
||||
<MudLink Href="@(RouteConstants.FAQ.Index)" Class="footer-link">سوالات متداول</MudLink>
|
||||
<MudLink Href="@(RouteConstants.Contact.Index)" Class="footer-link">ارتباط با ما</MudLink>
|
||||
<MudLink Href="@(RouteConstants.About.Index)" Class="footer-link">درباره ما</MudLink>
|
||||
<MudLink Href="@(RouteConstants.Licenses.Index)" Class="footer-link">مجوزها</MudLink>
|
||||
</MudStack>
|
||||
</MudItem>
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
<MudLink Href="@(RouteConstants.Blog.Index)" Typo="Typo.subtitle1" Class="mud-link">بلاگ</MudLink>
|
||||
<MudLink Href="@(RouteConstants.Contact.Index)" Typo="Typo.subtitle1" Class="mud-link">ارتباط با ما</MudLink>
|
||||
<MudLink Href="@(RouteConstants.About.Index)" Typo="Typo.subtitle1" Class="mud-link">درباره ما</MudLink>
|
||||
<MudLink Href="@(RouteConstants.Licenses.Index)" Typo="Typo.subtitle1" Class="mud-link">مجوزها</MudLink>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-center gap-2">
|
||||
@@ -191,6 +192,9 @@
|
||||
<MudLink Href="@(RouteConstants.Contact.Index)" Typo="Typo.subtitle1" OnClick="() => _drawerOpen=false">
|
||||
ارتباط با ما
|
||||
</MudLink>
|
||||
<MudLink Href="@(RouteConstants.Licenses.Index)" Typo="Typo.subtitle1" OnClick="() => _drawerOpen=false">
|
||||
مجوزها
|
||||
</MudLink>
|
||||
|
||||
<MudDivider Class="my-2"/>
|
||||
@if (_isAuthenticated)
|
||||
|
||||
@@ -67,6 +67,11 @@ public static class RouteConstants
|
||||
public const string Index = "/contact";
|
||||
}
|
||||
|
||||
public static class Licenses
|
||||
{
|
||||
public const string Index = "/licenses";
|
||||
}
|
||||
|
||||
public static class Blog
|
||||
{
|
||||
public const string Index = "/blog";
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
using System.Text.Json;
|
||||
using CMSMicroservice.Protobuf.Protos.SitePageSettings;
|
||||
|
||||
namespace FrontOffice.Main.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// سرویس سادهشده مدیریت صفحات سایت
|
||||
/// ۴ صفحه ثابت: landing, about, contact, licenses
|
||||
/// </summary>
|
||||
public class SitePageSettingsService
|
||||
{
|
||||
private readonly SitePageSettingsContract.SitePageSettingsContractClient _client;
|
||||
|
||||
public SitePageSettingsService(SitePageSettingsContract.SitePageSettingsContractClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت تنظیمات صفحه با کلید
|
||||
/// </summary>
|
||||
public async Task<PageSettingsDto?> GetPageAsync(string pageKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _client.GetPageSettingsAsync(
|
||||
new GetPageSettingsRequest { PageKey = pageKey });
|
||||
|
||||
if (response == null || response.Id <= 0) return null;
|
||||
|
||||
return MapToDto(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if DEBUG
|
||||
Console.WriteLine($"SitePageSettingsService.GetPageAsync error: {ex.Message}");
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static PageSettingsDto MapToDto(PageSettingsResponse response) => new()
|
||||
{
|
||||
Id = response.Id,
|
||||
PageKey = response.PageKey,
|
||||
Title = response.Title,
|
||||
MetaDescription = response.MetaDescription,
|
||||
HeroTitle = response.HeroTitle,
|
||||
HeroSubtitle = response.HeroSubtitle,
|
||||
HeroImagePath = response.HeroImagePath,
|
||||
IsActive = response.IsActive,
|
||||
SettingsJson = response.SettingsJson,
|
||||
Images = response.Images.Select(i => new PageSettingsImageDto
|
||||
{
|
||||
Id = i.Id,
|
||||
ImageGroup = i.ImageGroup,
|
||||
Title = i.Title,
|
||||
Subtitle = i.Subtitle,
|
||||
Description = i.Description,
|
||||
ImagePath = i.ImagePath,
|
||||
ThumbnailPath = i.ThumbnailPath,
|
||||
LinkUrl = i.LinkUrl,
|
||||
IconName = i.IconName,
|
||||
SortOrder = i.SortOrder,
|
||||
IsActive = i.IsActive
|
||||
}).OrderBy(i => i.SortOrder).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
// ── DTOs ──
|
||||
|
||||
public class PageSettingsDto
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string PageKey { get; set; } = string.Empty;
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string? MetaDescription { get; set; }
|
||||
public string? HeroTitle { get; set; }
|
||||
public string? HeroSubtitle { get; set; }
|
||||
public string? HeroImagePath { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public string? SettingsJson { get; set; }
|
||||
public List<PageSettingsImageDto> Images { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// دریافت تصاویر یک گروه خاص
|
||||
/// </summary>
|
||||
public List<PageSettingsImageDto> GetImages(string group) =>
|
||||
Images.Where(i => i.ImageGroup == group && i.IsActive)
|
||||
.OrderBy(i => i.SortOrder).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// دریافت تنظیمات JSON به عنوان یک نوع مشخص
|
||||
/// </summary>
|
||||
public T? GetSettings<T>() where T : class
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(SettingsJson)) return null;
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(SettingsJson, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PageSettingsImageDto
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string ImageGroup { get; set; } = string.Empty;
|
||||
public string? Title { get; set; }
|
||||
public string? Subtitle { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string ImagePath { get; set; } = string.Empty;
|
||||
public string? ThumbnailPath { get; set; }
|
||||
public string? LinkUrl { get; set; }
|
||||
public string? IconName { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user