using FrontOffice.Main.Utilities; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using MudBlazor; namespace FrontOffice.Main.Pages; 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!; [Inject] private ProductService ProductService { get; set; } = default!; [Inject] private DiscountProductService DiscountProductService { get; set; } = default!; [Inject] private CartService Cart { get; set; } = default!; [Inject] private DiscountCartService DiscountCart { get; set; } = default!; [Inject] private GuestActionGate GuestGate { get; set; } = default!; [Inject] private VATService VAT { get; set; } = default!; private bool _isAuthenticated; // ── CMS page data ── private PageSettingsDto? _pageData; private LandingSettings? _settings; // ── Top-selling product sections ── private const int LandingTopProductCount = 6; private List _topRegularProducts = new(); private List _topDiscountProducts = new(); private bool _loadingTopProducts = true; // ── 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; _isAuthenticated = await AuthService.IsAuthenticatedAsync(); // 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 top-selling in-stock products and blog posts in parallel var topRegTask = ProductService.GetTopSellingAsync(LandingTopProductCount, inStock: true); var topDiscTask = DiscountProductService.GetTopSellingAsync(LandingTopProductCount, inStock: true); var featuredPostsTask = BlogPostService.GetFeaturedPostsAsync(2); try { await Task.WhenAll(topRegTask, topDiscTask, featuredPostsTask); _topRegularProducts = topRegTask.Result.Products; _topDiscountProducts = topDiscTask.Result.Products; } catch { // Fallback: sections remain empty } _loadingTopProducts = false; // Load latest published blog posts try { _latestPosts = featuredPostsTask.IsCompletedSuccessfully ? featuredPostsTask.Result : await BlogPostService.GetFeaturedPostsAsync(2); if (_latestPosts.Count < 2) { var result = await BlogPostService.GetPublishedPostsAsync(page: 1, pageSize: 2); var existing = _latestPosts.Select(p => p.Id).ToHashSet(); foreach (var post in result.Posts) { if (!existing.Contains(post.Id)) { _latestPosts.Add(post); if (_latestPosts.Count >= 2) break; } } } } catch { _latestPosts = new(); } } 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) { // 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"); // Animate stat counters foreach (var stat in _stats) { await JS.InvokeVoidAsync("animateCounter", stat.ElementId, stat.Target, 2200, stat.Suffix); } } if (await AuthService.IsAuthenticatedAsync()) { if (await AuthService.IsCompleteRegisterAsync()) { Navigation.NavigateTo(RouteConstants.Profile.Index); } else { Navigation.NavigateTo(RouteConstants.Registration.Wizard); } } await base.OnAfterRenderAsync(firstRender); } private void NavigateToRegistrationWizard() { Navigation.NavigateTo(RouteConstants.Registration.Wizard); } private void NavigateToPost(string slug) { Navigation.NavigateTo($"/blog/{slug}"); } private void NavigateToRegularProduct(long id) => Navigation.NavigateTo(RouteConstants.Store.ProductDetail + id); private void NavigateToDiscountProduct(long id) => Navigation.NavigateTo(RouteConstants.DiscountStore.ProductDetail + id); private async Task AddRegularToCart(Product p) => await GuestGate.RunAsync(() => Cart.Add(p, 1)); private async Task AddDiscountToCart(DiscountProductCard p) => await GuestGate.RunAsync(() => DiscountCart.AddAsync(p.Id)); private string FormatPrice(long price) => $"{VAT.AddVAT(price):N0} تومان"; private string FormatDiscountPrice(long price) => $"{price:N0} تومان"; private async void OnStateChanged() { await InvokeAsync(StateHasChanged); } // ── 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; } }