-
سوالات متداول
+
@(_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..96875f8 100644
--- a/src/FrontOffice.Main/Pages/Index.razor.cs
+++ b/src/FrontOffice.Main/Pages/Index.razor.cs
@@ -9,21 +9,48 @@ 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();
+
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();
+
// 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,6 +71,155 @@ 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)
@@ -88,65 +264,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;