diff --git a/src/FrontOffice.Main/ConfigureServices.cs b/src/FrontOffice.Main/ConfigureServices.cs index 0f1c61a..702733a 100644 --- a/src/FrontOffice.Main/ConfigureServices.cs +++ b/src/FrontOffice.Main/ConfigureServices.cs @@ -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(); services.AddScoped(); services.AddScoped(); + // Site Page Settings Service (simplified page management) + services.AddScoped(); // Device detection: very light, dependency-free services.AddTransient(); // PDF generation (Chromium only) @@ -133,6 +136,7 @@ public static class ConfigureServices services.AddScoped(CreateAuthenticatedClient); services.AddScoped(CreateAuthenticatedClient); services.AddScoped(CreateAuthenticatedClient); + services.AddScoped(CreateAuthenticatedClient); services.AddScoped(CreateAuthenticatedClient); services.AddScoped(CreateAuthenticatedClient); // Image Resolver Service diff --git a/src/FrontOffice.Main/FrontOffice.Main.csproj b/src/FrontOffice.Main/FrontOffice.Main.csproj index 7339178..a4662dc 100644 --- a/src/FrontOffice.Main/FrontOffice.Main.csproj +++ b/src/FrontOffice.Main/FrontOffice.Main.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/FrontOffice.Main/Pages/About.razor b/src/FrontOffice.Main/Pages/About.razor index 57485ba..784f052 100644 --- a/src/FrontOffice.Main/Pages/About.razor +++ b/src/FrontOffice.Main/Pages/About.razor @@ -1,6 +1,6 @@ @attribute [Route(RouteConstants.About.Index)] @inject NavigationManager Navigation -@inject SitePageService SitePageService +@inject SitePageSettingsService PageSettingsService درباره ما | کارا بازار سلامت @@ -66,40 +66,22 @@ else - @(_visionSection?.Title ?? "چشم‌انداز") + @(_settings?.VisionTitle ?? "چشم‌انداز") + + + @(_settings?.VisionText ?? "تبدیل شدن به پیشروترین پلتفرم مدیریت تیم‌های فروش در منطقه، با ارائه راهکارهای هوشمند و کاربرپسند برای کسب‌وکارهای کوچک و بزرگ.") - @if (!string.IsNullOrWhiteSpace(_visionSection?.HtmlContent)) - { - - @((MarkupString)_visionSection.HtmlContent) - - } - else - { - - تبدیل شدن به پیشروترین پلتفرم مدیریت تیم‌های فروش در منطقه، با ارائه راهکارهای هوشمند و کاربرپسند برای کسب‌وکارهای کوچک و بزرگ. - - } - @(_missionSection?.Title ?? "مأموریت") + @(_settings?.MissionTitle ?? "مأموریت") + + + @(_settings?.MissionText ?? "توانمندسازی کسب‌وکارها از طریق فناوری‌های نوین، ساده‌سازی فرآیندهای پیچیده و ایجاد فرصت‌های جدید برای رشد و توسعه پایدار.") - @if (!string.IsNullOrWhiteSpace(_missionSection?.HtmlContent)) - { - - @((MarkupString)_missionSection.HtmlContent) - - } - else - { - - توانمندسازی کسب‌وکارها از طریق فناوری‌های نوین، ساده‌سازی فرآیندهای پیچیده و ایجاد کارا بازار سلامت‌های جدید برای رشد و توسعه پایدار. - - } @@ -111,21 +93,21 @@ else ارزش‌های ما - @if (_valueSections.Any()) + @if (_valueImages.Any()) { - @foreach (var value in _valueSections) + @foreach (var value in _valueImages) { @if (!string.IsNullOrWhiteSpace(value.IconName)) { - + } @value.Title - @if (!string.IsNullOrWhiteSpace(value.HtmlContent)) + @if (!string.IsNullOrWhiteSpace(value.Description)) { - @((MarkupString)value.HtmlContent) + @value.Description } @@ -199,9 +181,9 @@ else تیم ما - @if (_teamSections.Any()) + @if (_teamImages.Any()) { - @foreach (var member in _teamSections) + @foreach (var member in _teamImages) { @@ -222,10 +204,10 @@ else { @member.Subtitle } - @if (!string.IsNullOrWhiteSpace(member.HtmlContent)) + @if (!string.IsNullOrWhiteSpace(member.Description)) { - @((MarkupString)member.HtmlContent) + @member.Description } diff --git a/src/FrontOffice.Main/Pages/About.razor.cs b/src/FrontOffice.Main/Pages/About.razor.cs index af24e4b..f076c56 100644 --- a/src/FrontOffice.Main/Pages/About.razor.cs +++ b/src/FrontOffice.Main/Pages/About.razor.cs @@ -5,24 +5,22 @@ namespace FrontOffice.Main.Pages; public partial class About { private bool _loading = true; - private SitePageDto? _page; - private SitePageSectionDto? _visionSection; - private SitePageSectionDto? _missionSection; - private List _valueSections = new(); - private List _teamSections = new(); + private PageSettingsDto? _page; + private AboutSettings? _settings; + private List _valueImages = new(); + private List _teamImages = new(); protected override async Task OnInitializedAsync() { try { - _page = await SitePageService.GetByKeyAsync("about"); + _page = await PageSettingsService.GetPageAsync("about"); if (_page != null) { - _visionSection = _page.GetSection("vision"); - _missionSection = _page.GetSection("mission"); - _valueSections = _page.GetSections("value-").ToList(); - _teamSections = _page.GetSections("team-").ToList(); + _settings = _page.GetSettings(); + _valueImages = _page.GetImages("values"); + _teamImages = _page.GetImages("team"); } } catch @@ -34,4 +32,16 @@ public partial class About _loading = false; } } + + private class AboutSettings + { + public string? VisionTitle { get; set; } + public string? VisionText { get; set; } + public string? VisionIcon { get; set; } + public string? MissionTitle { get; set; } + public string? MissionText { get; set; } + public string? MissionIcon { get; set; } + public string? ValuesTitle { get; set; } + public string? TeamTitle { get; set; } + } } \ No newline at end of file diff --git a/src/FrontOffice.Main/Pages/Contact.razor b/src/FrontOffice.Main/Pages/Contact.razor index ed61fa4..af2968a 100644 --- a/src/FrontOffice.Main/Pages/Contact.razor +++ b/src/FrontOffice.Main/Pages/Contact.razor @@ -1,5 +1,5 @@ @attribute [Route(RouteConstants.Contact.Index)] -@inject SitePageService SitePageService +@inject SitePageSettingsService PageSettingsService ارتباط با ما | کارا بازار سلامت @@ -133,7 +133,7 @@
آدرس - @(_contactAddress ?? "کرج مهرویلا میدان مادر ساختمان بزرگمهر طبقه ۴ واحد ۱۶") + @(_settings?.Address ?? "کرج مهرویلا میدان مادر ساختمان بزرگمهر طبقه ۴ واحد ۱۶")
@@ -142,7 +142,7 @@
تلفن - @(_contactPhone ?? "026-34233563") + @(_settings?.Phone ?? "026-34233563")
@@ -150,7 +150,7 @@
ایمیل - @(_contactEmail ?? "info@kbs1.co") + @(_settings?.Email ?? "info@kbs1.co")
@@ -159,7 +159,7 @@
ساعات کاری - @((MarkupString)(_contactHours ?? "شنبه تا پنج‌شنبه
۹ صبح تا ۶ عصر")) + @((MarkupString)(_settings?.WorkingHours ?? "شنبه تا پنج‌شنبه
۹ صبح تا ۶ عصر"))
@@ -174,7 +174,7 @@ تلگرام @@ -183,7 +183,7 @@ اینستاگرام @@ -192,7 +192,7 @@ لینکدین @@ -201,7 +201,7 @@ واتس‌اپ @@ -226,7 +226,7 @@ نقشه موقعیت مکانی - @(_contactAddress ?? "کرج مهرویلا میدان مادر ساختمان بزرگمهر طبقه ۴ واحد ۱۶") + @(_settings?.Address ?? "کرج مهرویلا میدان مادر ساختمان بزرگمهر طبقه ۴ واحد ۱۶") diff --git a/src/FrontOffice.Main/Pages/Contact.razor.cs b/src/FrontOffice.Main/Pages/Contact.razor.cs index d44c34c..2e7f2d4 100644 --- a/src/FrontOffice.Main/Pages/Contact.razor.cs +++ b/src/FrontOffice.Main/Pages/Contact.razor.cs @@ -12,52 +12,19 @@ public partial class Contact private bool _isSubmitting; private readonly ContactFormValidator _contactFormValidator = new(); - // Dynamic CMS data - private SitePageDto? _page; - private string? _contactAddress; - private string? _contactPhone; - private string? _contactEmail; - private string? _contactHours; - private string? _socialTelegram; - private string? _socialInstagram; - private string? _socialLinkedin; - private string? _socialWhatsapp; + // Dynamic CMS data (new simplified system) + private PageSettingsDto? _page; + private ContactSettings? _settings; protected override async Task OnInitializedAsync() { try { - _page = await SitePageService.GetByKeyAsync("contact"); + _page = await PageSettingsService.GetPageAsync("contact"); if (_page != null) { - // Load contact info from CMS section - var contactInfoSection = _page.GetSection("contact-info"); - if (contactInfoSection != null) - { - var contactData = contactInfoSection.GetExtraData(); - if (contactData != null) - { - _contactAddress = contactData.Address; - _contactPhone = contactData.Phone; - _contactEmail = contactData.Email; - _contactHours = contactData.Hours; - } - } - - // Load social media links from CMS section - var socialSection = _page.GetSection("social-media"); - if (socialSection != null) - { - var socialData = socialSection.GetExtraData(); - if (socialData != null) - { - _socialTelegram = socialData.Telegram; - _socialInstagram = socialData.Instagram; - _socialLinkedin = socialData.Linkedin; - _socialWhatsapp = socialData.Whatsapp; - } - } + _settings = _page.GetSettings(); } } catch @@ -111,12 +78,12 @@ public partial class Contact private void CallSupport() { - Snackbar.Add($"شماره تماس: {_contactPhone ?? "026-34233563"}", Severity.Info); + Snackbar.Add($"شماره تماس: {_settings?.Phone ?? "026-34233563"}", Severity.Info); } private void SendEmail() { - Snackbar.Add($"ایمیل: {_contactEmail ?? "info@kbs1.co"}", Severity.Info); + Snackbar.Add($"ایمیل: {_settings?.Email ?? "info@kbs1.co"}", Severity.Info); } public class ContactForm @@ -142,20 +109,16 @@ public partial class Contact } } - // DTO classes for ExtraData JSON deserialization - private class ContactInfoData + // DTO class for SettingsJson deserialization + private class ContactSettings { public string? Address { get; set; } public string? Phone { get; set; } public string? Email { get; set; } - public string? Hours { get; set; } - } - - private class SocialMediaData - { - public string? Telegram { get; set; } - public string? Instagram { get; set; } - public string? Linkedin { get; set; } - public string? Whatsapp { get; set; } + public string? WorkingHours { get; set; } + public string? TelegramUrl { get; set; } + public string? InstagramUrl { get; set; } + public string? LinkedinUrl { get; set; } + public string? WhatsappUrl { get; set; } } } \ No newline at end of file diff --git a/src/FrontOffice.Main/Pages/DiscountStore/Cart.razor b/src/FrontOffice.Main/Pages/DiscountStore/Cart.razor index 57a6683..23d468b 100644 --- a/src/FrontOffice.Main/Pages/DiscountStore/Cart.razor +++ b/src/FrontOffice.Main/Pages/DiscountStore/Cart.razor @@ -33,7 +33,7 @@ + ImgWidth="64" ImgHeight="64" Class="rounded-lg" ObjectFit="ObjectFit.Cover" /> @context.Title @@ -82,7 +82,7 @@ + ImgWidth="50" ImgHeight="50" Class="rounded-lg" ObjectFit="ObjectFit.Cover" /> @item.Title diff --git a/src/FrontOffice.Main/Pages/DiscountStore/Checkout.razor b/src/FrontOffice.Main/Pages/DiscountStore/Checkout.razor index 079977b..949e630 100644 --- a/src/FrontOffice.Main/Pages/DiscountStore/Checkout.razor +++ b/src/FrontOffice.Main/Pages/DiscountStore/Checkout.razor @@ -80,7 +80,7 @@ + ImgWidth="40" ImgHeight="40" Class="rounded-lg" ObjectFit="ObjectFit.Cover" /> @item.Title @FormatPrice(item.FinalPrice) diff --git a/src/FrontOffice.Main/Pages/DiscountStore/ProductDetail.razor b/src/FrontOffice.Main/Pages/DiscountStore/ProductDetail.razor index 4d02723..0b582e4 100644 --- a/src/FrontOffice.Main/Pages/DiscountStore/ProductDetail.razor +++ b/src/FrontOffice.Main/Pages/DiscountStore/ProductDetail.razor @@ -24,8 +24,8 @@ + ObjectFit="ObjectFit.Cover" + Style="width:100%; aspect-ratio:1/1; border-radius:12px;" /> @if (_product.Images.Count > 1) { diff --git a/src/FrontOffice.Main/Pages/DiscountStore/Products.razor b/src/FrontOffice.Main/Pages/DiscountStore/Products.razor index 9c3c643..3527a69 100644 --- a/src/FrontOffice.Main/Pages/DiscountStore/Products.razor +++ b/src/FrontOffice.Main/Pages/DiscountStore/Products.razor @@ -77,9 +77,9 @@ + Style="cursor:pointer;"> -
+
@if (p.RemainingCount <= 0) {
diff --git a/src/FrontOffice.Main/Pages/Index.razor b/src/FrontOffice.Main/Pages/Index.razor index 2be5568..4ba4291 100644 --- a/src/FrontOffice.Main/Pages/Index.razor +++ b/src/FrontOffice.Main/Pages/Index.razor @@ -15,11 +15,11 @@ - رشد تیم، فروش واقعی، پاداش شفاف + @(_pageData?.HeroTitle ?? "رشد تیم، فروش واقعی، پاداش شفاف") - با دعوت از دوستان، از خریدهای واقعی محصولات سلامت پاداش بگیرید. ثبت‌نام سریع، داشبورد لحظه‌ای. + @(_pageData?.HeroSubtitle ?? "با دعوت از دوستان، از خریدهای واقعی محصولات سلامت پاداش بگیرید. ثبت‌نام سریع، داشبورد لحظه‌ای.") @@ -27,13 +27,13 @@ Class="rounded-pill hero-cta-primary" Size="Size.Large" OnClick="NavigateToRegistrationWizard"> - شروع رایگان + @(_settings?.HeroButtonPrimaryText ?? "شروع رایگان") - آخرین اخبار + @(_settings?.HeroButtonSecondaryText ?? "آخرین اخبار") @@ -99,7 +99,7 @@
- سه گام تا شروع + @(_settings?.StepsTitle ?? "سه گام تا شروع") بدون پیچیدگی، سریع و آسان @@ -128,7 +128,7 @@
- چرا کارا بازار سلامت؟ + @(_settings?.FeaturesTitle ?? "چرا کارا بازار سلامت؟") ابزارهایی ساده و قدرتمند برای رشد کسب‌وکار شما @@ -259,7 +259,7 @@
- اعتماد مشتریان + @(_settings?.TestimonialsTitle ?? "اعتماد مشتریان") بخشی از تجربه استفاده از «کارا بازار سلامت» @@ -299,7 +299,7 @@
- سوالات متداول + @(_settings?.FaqTitle ?? "سوالات متداول") پاسخ به سوالات رایج شما @@ -326,16 +326,16 @@
- آماده شروع هستید؟ + @(_settings?.CtaTitle ?? "آماده شروع هستید؟") - همین الان ثبت‌نام کنید و از مزایای کارا بازار سلامت بهره‌مند شوید. + @(_settings?.CtaDescription ?? "همین الان ثبت‌نام کنید و از مزایای کارا بازار سلامت بهره‌مند شوید.") - شروع رایگان + @(_settings?.CtaButtonText ?? "شروع رایگان")
diff --git a/src/FrontOffice.Main/Pages/Index.razor.cs b/src/FrontOffice.Main/Pages/Index.razor.cs index 595ff44..88fb346 100644 --- a/src/FrontOffice.Main/Pages/Index.razor.cs +++ b/src/FrontOffice.Main/Pages/Index.razor.cs @@ -9,21 +9,53 @@ public partial class Index : IDisposable { [Inject] private BlogPostService BlogPostService { get; set; } = default!; [Inject] private AuthService AuthService { get; set; } = default!; + [Inject] private SitePageSettingsService PageSettingsService { get; set; } = default!; + + // ── CMS page data ── + private PageSettingsDto? _pageData; + private LandingSettings? _settings; // ── Latest blog posts (loaded from CMS) ── private List _latestPosts = new(); + // ── Data lists (populated from DB or fallback) ── + private List<(string Icon, string Text)> _trustBadges = new(); + private List<(string Title, string Desc)> _steps = new(); + private List<(string Icon, string Title, string Desc, int Delay)> _features = new(); + private List _stats = new(); + private List _testimonials = new(); + private List _faqs = new(); + + // Track whether animations need re-initialization after data load + private bool _dataLoaded; + private bool _animationsInitialized; + protected override async Task OnInitializedAsync() { MainService.OnChangeHandler += OnStateChanged; + // Load landing page settings from CMS + try + { + _pageData = await PageSettingsService.GetPageAsync("landing"); + if (_pageData != null) + { + _settings = _pageData.GetSettings(); + } + } + catch + { + // Fallback: settings remain null → defaults below + } + + PopulateFromSettings(); + _dataLoaded = true; + // Load latest published blog posts - // اول پین‌شده‌ها، اگر کافی نبود آخرین پست‌ها اضافه شود try { _latestPosts = await BlogPostService.GetFeaturedPostsAsync(2); - - // اگر فقط ۱ پین‌شده داریم، ۱ پست آخر هم اضافه کن + if (_latestPosts.Count < 2) { var result = await BlogPostService.GetPublishedPostsAsync(page: 1, pageSize: 2); @@ -44,10 +76,162 @@ public partial class Index : IDisposable } } + private void PopulateFromSettings() + { + // ── Trust badges ── + if (_settings?.TrustBadges?.Any() == true) + { + _trustBadges = _settings.TrustBadges + .Select(b => (ResolveIcon(b.IconName), b.Text ?? "")) + .ToList(); + } + else + { + _trustBadges = new() + { + (Icons.Material.Outlined.Timer, "ثبت‌نام زیر ۲ دقیقه"), + (Icons.Material.Outlined.SupportAgent, "پشتیبانی ۷×۲۴"), + (Icons.Material.Outlined.Lock, "پرداخت ایمن"), + (Icons.Material.Outlined.Verified, "تضمین کیفیت"), + }; + } + + // ── Steps ── + if (_settings?.Steps?.Any() == true) + { + _steps = _settings.Steps + .Select(s => (s.Title ?? "", s.Description ?? "")) + .ToList(); + } + else + { + _steps = new() + { + ("ثبت‌نام و احراز هویت", "یک حساب بسازید، شماره موبایل را تأیید و اطلاعات هویتی را تکمیل کنید."), + ("دعوت دوستان", "لینک دعوت اختصاصی خود را با دوستان و آشنایان به اشتراک بگذارید."), + ("دریافت پاداش", "از خریدهای واقعی اعضای تیمتان پاداش شفاف و لحظه‌ای دریافت کنید."), + }; + } + + // ── Features ── + if (_settings?.Features?.Any() == true) + { + _features = _settings.Features + .Select((f, i) => (ResolveIcon(f.IconName), f.Title ?? "", f.Description ?? "", i * 100)) + .ToList(); + } + else + { + _features = new() + { + (Icons.Material.Outlined.VerifiedUser, "ثبت‌نام سریع و ساده", "در چند گام کوتاه حساب بسازید و شروع کنید.", 0), + (Icons.Material.Outlined.StarBorder, "پاداش‌های شفاف", "قوانین روشن، دسترسی آسان به سوابق و گزارش‌ها.", 100), + (Icons.Material.Outlined.Devices, "طراحی واکنش‌گرا", "تجربه‌ای روان در موبایل و دسکتاپ.", 200), + (Icons.Material.Outlined.Groups, "تیم‌سازی هوشمند", "ساختار درختی شبکه و مدیریت تیم‌های فروش.", 300), + (Icons.Material.Outlined.Insights, "گزارش‌های لحظه‌ای", "داشبورد پویا برای مشاهده عملکرد و کمیسیون.", 400), + (Icons.Material.Outlined.Lock, "امنیت بالا", "رمزنگاری اطلاعات و احراز هویت چندمرحله‌ای.", 500), + }; + } + + // ── Stats ── + if (_settings?.Stats?.Any() == true) + { + _stats = _settings.Stats + .Select((s, i) => new StatItem( + $"stat-{i}", + $"{s.Value}{s.Suffix}", + s.Value, + s.Suffix ?? "", + ResolveColor(s.Color), + s.Label ?? "")) + .ToList(); + } + else + { + _stats = new() + { + new("stat-growth", "+۵۰٪", 50, "%+", Color.Success, "رشد میانگین تیم"), + new("stat-uptime", "۹۹.۹٪", 99.9, "%", Color.Primary, "آپ‌تایم سرویس"), + new("stat-deploy", "۳ روز", 3, " روز", Color.Warning, "میانگین زمان استقرار"), + new("stat-coverage", "+۲۰ کشور", 20, "+", Color.Secondary, "پوشش ارسال کد"), + }; + } + + // ── Testimonials ── + if (_settings?.Testimonials?.Any() == true) + { + _testimonials = _settings.Testimonials + .Select((t, i) => new TestimonialItem(t.Quote ?? "", t.Name ?? "", t.Role ?? "", i * 150)) + .ToList(); + } + else + { + _testimonials = new() + { + new("با کارا بازار سلامت، محاسبه کارمزدها و پایش تیم‌ها بدون اکسل و دردسر انجام می‌شود.", "شرکت سینا نت", "مدیر عملیات", 0), + new("شجره‌نامه بصری و گزارش‌های دقیق باعث شد رشد تیم را لحظه‌ای ببینیم.", "هولدینگ آریانا", "مدیر فروش", 150), + new("سادگی ثبت‌نام و شفافیت پاداش‌ها مهم‌ترین مزیت این پلتفرم است.", "گروه بهداشتی نوین", "مدیر توسعه", 300), + }; + } + + // ── FAQs ── + if (_settings?.Faqs?.Any() == true) + { + _faqs = _settings.Faqs + .Select(f => new QA(f.Question ?? "", f.Answer ?? "")) + .ToList(); + } + else + { + _faqs = new() + { + new("دامنه اختصاصی دارم؛ قابل اتصال است؟", "بله، پشت دامنه و گواهی SSL خودتان مستقر می‌شود."), + new("با دیتابیس خودم کار می‌کند؟", "کاملاً. SQL Server، PostgreSQL و MySQL پشتیبانی می‌شود."), + new("چه درگاه‌هایی پشتیبانی می‌شود؟", "Stripe و PayPal یا درگاه اختصاصی از طریق وب‌هوک‌ها."), + new("می‌توانم داده‌ها را خروجی بگیرم؟", "هر زمان از داشبورد ادمین خروجی CSV/Excel بگیرید."), + }; + } + } + + /// + /// Resolve icon name from SettingsJson to MudBlazor icon string. + /// Falls back to a generic icon. + /// + private static string ResolveIcon(string? iconName) + { + if (string.IsNullOrWhiteSpace(iconName)) return Icons.Material.Outlined.Info; + + // Try to get from MudBlazor Icons via reflection + var field = typeof(Icons.Material.Outlined).GetField(iconName, + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + if (field != null) return (string)(field.GetValue(null) ?? Icons.Material.Outlined.Info); + + // Also try Filled + field = typeof(Icons.Material.Filled).GetField(iconName, + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + if (field != null) return (string)(field.GetValue(null) ?? Icons.Material.Outlined.Info); + + return Icons.Material.Outlined.Info; + } + + private static Color ResolveColor(string? colorName) => colorName?.ToLowerInvariant() switch + { + "primary" => Color.Primary, + "secondary" => Color.Secondary, + "success" => Color.Success, + "warning" => Color.Warning, + "error" => Color.Error, + "info" => Color.Info, + _ => Color.Default + }; + protected override async Task OnAfterRenderAsync(bool firstRender) { - if (firstRender) + // Init/re-init scroll animations after data is loaded and rendered + if ((firstRender || (_dataLoaded && !_animationsInitialized)) && _steps.Any()) { + _animationsInitialized = true; + // Init scroll-triggered fade-in animations await JS.InvokeVoidAsync("initScrollAnimations"); @@ -88,65 +272,40 @@ public partial class Index : IDisposable await InvokeAsync(StateHasChanged); } - // ── Trust badges ── - private readonly List<(string Icon, string Text)> _trustBadges = new() - { - (Icons.Material.Outlined.Timer, "ثبت‌نام زیر ۲ دقیقه"), - (Icons.Material.Outlined.SupportAgent, "پشتیبانی ۷×۲۴"), - (Icons.Material.Outlined.Lock, "پرداخت ایمن"), - (Icons.Material.Outlined.Verified, "تضمین کیفیت"), - }; - - // ── How it works steps ── - private readonly List<(string Title, string Desc)> _steps = new() - { - ("ثبت‌نام و احراز هویت", "یک حساب بسازید، شماره موبایل را تأیید و اطلاعات هویتی را تکمیل کنید."), - ("دعوت دوستان", "لینک دعوت اختصاصی خود را با دوستان و آشنایان به اشتراک بگذارید."), - ("دریافت پاداش", "از خریدهای واقعی اعضای تیمتان پاداش شفاف و لحظه‌ای دریافت کنید."), - }; - - // ── Feature cards data (6 cards) ── - private readonly List<(string Icon, string Title, string Desc, int Delay)> _features = new() - { - (Icons.Material.Outlined.VerifiedUser, "ثبت‌نام سریع و ساده", "در چند گام کوتاه حساب بسازید و شروع کنید.", 0), - (Icons.Material.Outlined.StarBorder, "پاداش‌های شفاف", "قوانین روشن، دسترسی آسان به سوابق و گزارش‌ها.", 100), - (Icons.Material.Outlined.Devices, "طراحی واکنش‌گرا", "تجربه‌ای روان در موبایل و دسکتاپ.", 200), - (Icons.Material.Outlined.Groups, "تیم‌سازی هوشمند", "ساختار درختی شبکه و مدیریت تیم‌های فروش.", 300), - (Icons.Material.Outlined.Insights, "گزارش‌های لحظه‌ای", "داشبورد پویا برای مشاهده عملکرد و کمیسیون.", 400), - (Icons.Material.Outlined.Lock, "امنیت بالا", "رمزنگاری اطلاعات و احراز هویت چندمرحله‌ای.", 500), - }; - - // ── Stats data (animated counters) ── - private readonly List _stats = new() - { - new("stat-growth", "+۵۰٪", 50, "%+", Color.Success, "رشد میانگین تیم"), - new("stat-uptime", "۹۹.۹٪", 99.9, "%", Color.Primary, "آپ‌تایم سرویس"), - new("stat-deploy", "۳ روز", 3, " روز", Color.Warning, "میانگین زمان استقرار"), - new("stat-coverage", "+۲۰ کشور", 20, "+", Color.Secondary, "پوشش ارسال کد"), - }; - - // ── Testimonials ── - private readonly List _testimonials = new() - { - new("با کارا بازار سلامت، محاسبه کارمزدها و پایش تیم‌ها بدون اکسل و دردسر انجام می‌شود.", "شرکت سینا نت", "مدیر عملیات", 0), - new("شجره‌نامه بصری و گزارش‌های دقیق باعث شد رشد تیم را لحظه‌ای ببینیم.", "هولدینگ آریانا", "مدیر فروش", 150), - new("سادگی ثبت‌نام و شفافیت پاداش‌ها مهم‌ترین مزیت این پلتفرم است.", "گروه بهداشتی نوین", "مدیر توسعه", 300), - }; - - // ── FAQ data ── - private readonly List _faqs = new() - { - new("دامنه اختصاصی دارم؛ قابل اتصال است؟", "بله، پشت دامنه و گواهی SSL خودتان مستقر می‌شود."), - new("با دیتابیس خودم کار می‌کند؟", "کاملاً. SQL Server، PostgreSQL و MySQL پشتیبانی می‌شود."), - new("چه درگاه‌هایی پشتیبانی می‌شود؟", "Stripe و PayPal یا درگاه اختصاصی از طریق وب‌هوک‌ها."), - new("می‌توانم داده‌ها را خروجی بگیرم؟", "هر زمان از داشبورد ادمین خروجی CSV/Excel بگیرید."), - }; - // ── Records ── private record QA(string Q, string A); private record StatItem(string ElementId, string Display, double Target, string Suffix, Color Color, string Label); private record TestimonialItem(string Quote, string Name, string Role, int Delay); + // ── SettingsJson DTO ── + private class LandingSettings + { + public string? HeroButtonPrimaryText { get; set; } + public string? HeroButtonSecondaryText { get; set; } + public List? TrustBadges { get; set; } + public string? StepsTitle { get; set; } + public List? Steps { get; set; } + public string? FeaturesTitle { get; set; } + public List? Features { get; set; } + public string? StatsTitle { get; set; } + public List? Stats { get; set; } + public string? TestimonialsTitle { get; set; } + public List? Testimonials { get; set; } + public string? FaqTitle { get; set; } + public List? Faqs { get; set; } + public string? CtaTitle { get; set; } + public string? CtaDescription { get; set; } + public string? CtaButtonText { get; set; } + public bool? FeaturedBlogEnabled { get; set; } + } + + private class TrustBadgeItem { public string? IconName { get; set; } public string? Text { get; set; } } + private class StepItem { public string? Title { get; set; } public string? Description { get; set; } } + private class FeatureItem { public string? IconName { get; set; } public string? Title { get; set; } public string? Description { get; set; } } + private class StatSettingItem { public string? Label { get; set; } public double Value { get; set; } public string? Suffix { get; set; } public string? Color { get; set; } } + private class TestimonialSettingItem { public string? Quote { get; set; } public string? Name { get; set; } public string? Role { get; set; } } + private class FaqItem { public string? Question { get; set; } public string? Answer { get; set; } } + public void Dispose() { MainService.OnChangeHandler -= OnStateChanged; diff --git a/src/FrontOffice.Main/Pages/Licenses.razor b/src/FrontOffice.Main/Pages/Licenses.razor new file mode 100644 index 0000000..bef2351 --- /dev/null +++ b/src/FrontOffice.Main/Pages/Licenses.razor @@ -0,0 +1,99 @@ +@attribute [Route(RouteConstants.Licenses.Index)] +@inject SitePageSettingsService PageSettingsService + +مجوزها و گواهینامه‌ها | کارا بازار سلامت + +@if (_loading) +{ + + + +} +else +{ + +
+ + + مجوزها و گواهینامه‌ها + + @(_pageData?.HeroTitle ?? "مجوزها و گواهینامه‌های رسمی") + + + @(_pageData?.HeroSubtitle ?? "ما با دریافت مجوزهای رسمی و تأییدیه‌های معتبر، اطمینان شما را در استفاده از خدمات خود جلب کرده‌ایم.") + + + +
+ + + + +
+ + مجوزهای ما + @if (_licenseImages.Any()) + { + + @foreach (var license in _licenseImages) + { + + + + + @if (!string.IsNullOrWhiteSpace(license.Title)) + { + @license.Title + } + @if (!string.IsNullOrWhiteSpace(license.Description)) + { + @license.Description + } + + @if (!string.IsNullOrWhiteSpace(license.LinkUrl)) + { + + + مشاهده جزئیات + + + } + + + } + + } + else + { + + اطلاعات مجوزها به‌زودی بارگذاری خواهد شد. + + } + +
+ + +
+ + + + نیاز به اطلاعات بیشتر دارید؟ + + برای دریافت اطلاعات بیشتر درباره مجوزها و گواهینامه‌های ما، با ما تماس بگیرید. + + + تماس با ما + + + +
+ +
+} diff --git a/src/FrontOffice.Main/Pages/Licenses.razor.cs b/src/FrontOffice.Main/Pages/Licenses.razor.cs new file mode 100644 index 0000000..89f8fa0 --- /dev/null +++ b/src/FrontOffice.Main/Pages/Licenses.razor.cs @@ -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 _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; + } + } +} diff --git a/src/FrontOffice.Main/Pages/Store/Cart.razor b/src/FrontOffice.Main/Pages/Store/Cart.razor index 60f8a38..16234fd 100644 --- a/src/FrontOffice.Main/Pages/Store/Cart.razor +++ b/src/FrontOffice.Main/Pages/Store/Cart.razor @@ -31,7 +31,7 @@ + ImgWidth="64" ImgHeight="64" Class="rounded-lg" ObjectFit="ObjectFit.Cover" /> @context.Title @if (context.Discount > 0 || !string.IsNullOrWhiteSpace(context.Created) || !string.IsNullOrWhiteSpace(context.Description)) @@ -87,7 +87,7 @@ + ImgWidth="50" ImgHeight="50" Class="rounded-lg" ObjectFit="ObjectFit.Cover" /> @item.Title @if (item.Discount > 0) diff --git a/src/FrontOffice.Main/Pages/Store/CheckoutSummary.razor b/src/FrontOffice.Main/Pages/Store/CheckoutSummary.razor index 95d7974..b894fb3 100644 --- a/src/FrontOffice.Main/Pages/Store/CheckoutSummary.razor +++ b/src/FrontOffice.Main/Pages/Store/CheckoutSummary.razor @@ -86,7 +86,7 @@ + ImgWidth="48" ImgHeight="48" Class="rounded-lg" ObjectFit="ObjectFit.Cover" /> @item.Title @FormatPrice(item.LineTotal) diff --git a/src/FrontOffice.Main/Pages/Store/OrderDetail.razor b/src/FrontOffice.Main/Pages/Store/OrderDetail.razor index 823f6a5..d94751d 100644 --- a/src/FrontOffice.Main/Pages/Store/OrderDetail.razor +++ b/src/FrontOffice.Main/Pages/Store/OrderDetail.razor @@ -47,7 +47,7 @@ else + ImgWidth="60" ImgHeight="60" Class="rounded-lg" ObjectFit="ObjectFit.Cover" /> @context.ProductTitle @@ -66,7 +66,7 @@ else + ImgWidth="60" ImgHeight="60" Class="rounded-lg" ObjectFit="ObjectFit.Cover" /> @it.ProductTitle diff --git a/src/FrontOffice.Main/Pages/Store/ProductDetail.razor b/src/FrontOffice.Main/Pages/Store/ProductDetail.razor index 3426a9a..2b6a0a0 100644 --- a/src/FrontOffice.Main/Pages/Store/ProductDetail.razor +++ b/src/FrontOffice.Main/Pages/Store/ProductDetail.razor @@ -33,7 +33,7 @@ else { + Style="width:100%; aspect-ratio:1/1; border-radius:12px;" /> } else { diff --git a/src/FrontOffice.Main/Pages/Store/Products.razor b/src/FrontOffice.Main/Pages/Store/Products.razor index b3cdb04..370abbc 100644 --- a/src/FrontOffice.Main/Pages/Store/Products.razor +++ b/src/FrontOffice.Main/Pages/Store/Products.razor @@ -120,11 +120,11 @@ + Style="cursor:pointer;"> -
+
@if (p.RemainingCount <= 0) {
diff --git a/src/FrontOffice.Main/Shared/Footer.razor b/src/FrontOffice.Main/Shared/Footer.razor index 4b88d89..71f1637 100644 --- a/src/FrontOffice.Main/Shared/Footer.razor +++ b/src/FrontOffice.Main/Shared/Footer.razor @@ -55,6 +55,7 @@ سوالات متداول ارتباط با ما درباره ما + مجوزها diff --git a/src/FrontOffice.Main/Shared/MainLayout.razor b/src/FrontOffice.Main/Shared/MainLayout.razor index 8766d9d..0a9f4b2 100644 --- a/src/FrontOffice.Main/Shared/MainLayout.razor +++ b/src/FrontOffice.Main/Shared/MainLayout.razor @@ -19,7 +19,7 @@
- + بلاگ ارتباط با ما درباره ما + مجوزها
@@ -191,6 +192,9 @@ ارتباط با ما + + مجوزها + @if (_isAuthenticated) @@ -281,9 +285,9 @@ else { @* ── Default bottom nav (outside stores) ── *@ - + - خانه + @(_isAuthenticated ? "داشبورد" : "خانه") @if (_isAuthenticated) { diff --git a/src/FrontOffice.Main/Utilities/RouteConstants.cs b/src/FrontOffice.Main/Utilities/RouteConstants.cs index f83d29c..7fd1f65 100644 --- a/src/FrontOffice.Main/Utilities/RouteConstants.cs +++ b/src/FrontOffice.Main/Utilities/RouteConstants.cs @@ -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"; diff --git a/src/FrontOffice.Main/Utilities/SitePageSettingsService.cs b/src/FrontOffice.Main/Utilities/SitePageSettingsService.cs new file mode 100644 index 0000000..86b5b29 --- /dev/null +++ b/src/FrontOffice.Main/Utilities/SitePageSettingsService.cs @@ -0,0 +1,125 @@ +using System.Text.Json; +using CMSMicroservice.Protobuf.Protos.SitePageSettings; + +namespace FrontOffice.Main.Utilities; + +/// +/// سرویس ساده‌شده مدیریت صفحات سایت +/// ۴ صفحه ثابت: landing, about, contact, licenses +/// +public class SitePageSettingsService +{ + private readonly SitePageSettingsContract.SitePageSettingsContractClient _client; + + public SitePageSettingsService(SitePageSettingsContract.SitePageSettingsContractClient client) + { + _client = client; + } + + /// + /// دریافت تنظیمات صفحه با کلید + /// + public async Task 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 Images { get; set; } = new(); + + /// + /// دریافت تصاویر یک گروه خاص + /// + public List GetImages(string group) => + Images.Where(i => i.ImageGroup == group && i.IsActive) + .OrderBy(i => i.SortOrder).ToList(); + + /// + /// دریافت تنظیمات JSON به عنوان یک نوع مشخص + /// + public T? GetSettings() where T : class + { + if (string.IsNullOrWhiteSpace(SettingsJson)) return null; + try + { + return JsonSerializer.Deserialize(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; } +} diff --git a/src/FrontOffice.Main/appsettings.Development.json b/src/FrontOffice.Main/appsettings.Development.json deleted file mode 100644 index 36c36cb..0000000 --- a/src/FrontOffice.Main/appsettings.Development.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "GwUrl": "https://cms.kbs2.ir", - "DownloadUrl": "", - "EncryptionSettings": { - "Key": "kmcQ3XTmH4mrdh8VHziuscyf8LLYjG//Kyni81nH/0E=", - "IV": "1wyF3Tt142MOkCpIyCxh/g==" - }, - "SignalR": { - "HubPath": "/hubs/token-notification" - }, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" -} diff --git a/src/FrontOffice.Main/appsettings.json b/src/FrontOffice.Main/appsettings.json index de0550d..6f287e6 100644 --- a/src/FrontOffice.Main/appsettings.json +++ b/src/FrontOffice.Main/appsettings.json @@ -1,5 +1,4 @@ { -// "GwUrl": "https://localhost:34781", "GwUrl": "https://localhost:32846", "DownloadUrl": "", "EncryptionSettings": {