feat: replace raw JSON editor with Shopify-style typed settings editors
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m7s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m7s
- Create PageSettingsModels.cs: typed DTOs for landing, about, contact, licenses - LandingSettingsEditor: accordion sections for hero buttons, trust badges, steps, features, stats, testimonials, FAQs, CTA — all with add/remove list management - AboutSettingsEditor: vision/mission text + icon fields + section titles - ContactSettingsEditor: address, phone, email, working hours + social media links - LicensesSettingsEditor: page description + display style selector - PageSettingsEditDialog: auto-switch editor by PageKey, JSON↔Model serialize/deserialize - Fallback to raw JSON textarea for unknown page keys
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
@using BackOffice.Pages.Content.Components.SettingsModels
|
||||
|
||||
<MudExpansionPanels MultiExpansion="true" Elevation="0">
|
||||
|
||||
@* ── Vision ── *@
|
||||
<MudExpansionPanel Text="چشمانداز" Icon="@Icons.Material.Filled.Visibility" IsInitiallyExpanded="true">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.VisionTitle"
|
||||
Label="عنوان"
|
||||
Placeholder="چشمانداز"
|
||||
Variant="Variant.Outlined" />
|
||||
<MudTextField @bind-Value="Model.VisionText"
|
||||
Label="متن چشمانداز"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="3" />
|
||||
<MudTextField @bind-Value="Model.VisionIcon"
|
||||
Label="نام آیکون"
|
||||
Placeholder="Visibility"
|
||||
Variant="Variant.Outlined"
|
||||
HelperText="نام آیکون از مجموعه Material Icons" />
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── Mission ── *@
|
||||
<MudExpansionPanel Text="مأموریت" Icon="@Icons.Material.Filled.TrackChanges" IsInitiallyExpanded="true">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.MissionTitle"
|
||||
Label="عنوان"
|
||||
Placeholder="مأموریت"
|
||||
Variant="Variant.Outlined" />
|
||||
<MudTextField @bind-Value="Model.MissionText"
|
||||
Label="متن مأموریت"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="3" />
|
||||
<MudTextField @bind-Value="Model.MissionIcon"
|
||||
Label="نام آیکون"
|
||||
Placeholder="TrackChanges"
|
||||
Variant="Variant.Outlined"
|
||||
HelperText="نام آیکون از مجموعه Material Icons" />
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── Section Titles ── *@
|
||||
<MudExpansionPanel Text="عناوین بخشها" Icon="@Icons.Material.Filled.Title">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.ValuesTitle"
|
||||
Label="عنوان بخش ارزشها"
|
||||
Placeholder="ارزشهای ما"
|
||||
Variant="Variant.Outlined" />
|
||||
<MudTextField @bind-Value="Model.TeamTitle"
|
||||
Label="عنوان بخش تیم"
|
||||
Placeholder="تیم ما"
|
||||
Variant="Variant.Outlined" />
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── Hint ── *@
|
||||
<MudAlert Severity="Severity.Info" Variant="Variant.Text" Dense="true" Class="mt-2">
|
||||
آیتمهای ارزشها و اعضای تیم از بخش «مدیریت تصاویر» قابل ویرایش هستند.
|
||||
</MudAlert>
|
||||
|
||||
</MudExpansionPanels>
|
||||
|
||||
@code {
|
||||
[Parameter] public AboutSettingsModel Model { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
@using BackOffice.Pages.Content.Components.SettingsModels
|
||||
|
||||
<MudExpansionPanels MultiExpansion="true" Elevation="0">
|
||||
|
||||
@* ── Contact Info ── *@
|
||||
<MudExpansionPanel Text="اطلاعات تماس" Icon="@Icons.Material.Filled.ContactPhone" IsInitiallyExpanded="true">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.Address"
|
||||
Label="آدرس"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="2"
|
||||
Placeholder="کرج مهرویلا میدان مادر..." />
|
||||
<MudGrid Spacing="2">
|
||||
<MudItem xs="12" md="6">
|
||||
<MudTextField @bind-Value="Model.Phone"
|
||||
Label="شماره تلفن"
|
||||
Placeholder="026-34233563"
|
||||
Variant="Variant.Outlined" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" md="6">
|
||||
<MudTextField @bind-Value="Model.Email"
|
||||
Label="ایمیل"
|
||||
Placeholder="info@kbs1.co"
|
||||
Variant="Variant.Outlined" />
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
<MudTextField @bind-Value="Model.WorkingHours"
|
||||
Label="ساعات کاری"
|
||||
Placeholder="شنبه تا پنجشنبه — ۹ صبح تا ۶ عصر"
|
||||
Variant="Variant.Outlined" />
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── Social Media ── *@
|
||||
<MudExpansionPanel Text="شبکههای اجتماعی" Icon="@Icons.Material.Filled.Share" IsInitiallyExpanded="true">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.TelegramUrl"
|
||||
Label="تلگرام"
|
||||
Placeholder="https://t.me/kbs1"
|
||||
Variant="Variant.Outlined"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@Icons.Custom.Brands.Telegram" />
|
||||
<MudTextField @bind-Value="Model.InstagramUrl"
|
||||
Label="اینستاگرام"
|
||||
Placeholder="https://instagram.com/kbs1"
|
||||
Variant="Variant.Outlined"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@Icons.Custom.Brands.Instagram" />
|
||||
<MudTextField @bind-Value="Model.LinkedinUrl"
|
||||
Label="لینکدین"
|
||||
Placeholder="https://linkedin.com/company/kbs1"
|
||||
Variant="Variant.Outlined"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@Icons.Custom.Brands.LinkedIn" />
|
||||
<MudTextField @bind-Value="Model.WhatsappUrl"
|
||||
Label="واتساپ"
|
||||
Placeholder="https://wa.me/989123456789"
|
||||
Variant="Variant.Outlined"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@Icons.Custom.Brands.WhatsApp" />
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
</MudExpansionPanels>
|
||||
|
||||
@code {
|
||||
[Parameter] public ContactSettingsModel Model { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
@using BackOffice.Pages.Content.Components.SettingsModels
|
||||
|
||||
<MudExpansionPanels MultiExpansion="true" Elevation="0">
|
||||
|
||||
@* ── Hero Buttons ── *@
|
||||
<MudExpansionPanel Text="دکمههای هیرو" Icon="@Icons.Material.Filled.TouchApp" IsInitiallyExpanded="true">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.HeroButtonPrimaryText"
|
||||
Label="متن دکمه اصلی"
|
||||
Placeholder="شروع رایگان"
|
||||
Variant="Variant.Outlined" />
|
||||
<MudTextField @bind-Value="Model.HeroButtonSecondaryText"
|
||||
Label="متن دکمه ثانویه"
|
||||
Placeholder="آخرین اخبار"
|
||||
Variant="Variant.Outlined" />
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── Trust Badges ── *@
|
||||
<MudExpansionPanel Text="@($"نشانهای اعتماد ({Model.TrustBadges.Count})")" Icon="@Icons.Material.Filled.Verified">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
@for (var i = 0; i < Model.TrustBadges.Count; i++)
|
||||
{
|
||||
var index = i;
|
||||
<MudPaper Elevation="0" Class="pa-3 rounded-lg" Style="border:1px solid var(--mud-palette-lines-default);">
|
||||
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary" Style="min-width:20px;">@(index + 1)</MudText>
|
||||
<MudTextField @bind-Value="Model.TrustBadges[index].IconName"
|
||||
Label="نام آیکون"
|
||||
Placeholder="Timer"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Style="flex:1;" />
|
||||
<MudTextField @bind-Value="Model.TrustBadges[index].Text"
|
||||
Label="متن"
|
||||
Placeholder="ثبتنام زیر ۲ دقیقه"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Style="flex:2;" />
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||
Color="Color.Error"
|
||||
Size="Size.Small"
|
||||
OnClick="() => Model.TrustBadges.RemoveAt(index)" />
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Add"
|
||||
Color="Color.Primary"
|
||||
Variant="Variant.Text"
|
||||
OnClick="() => Model.TrustBadges.Add(new TrustBadgeModel())">
|
||||
افزودن نشان
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── Steps ── *@
|
||||
<MudExpansionPanel Text="@($"مراحل ({Model.Steps.Count})")" Icon="@Icons.Material.Filled.FormatListNumbered">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.StepsTitle"
|
||||
Label="عنوان بخش مراحل"
|
||||
Placeholder="سه گام تا شروع"
|
||||
Variant="Variant.Outlined" />
|
||||
@for (var i = 0; i < Model.Steps.Count; i++)
|
||||
{
|
||||
var index = i;
|
||||
<MudPaper Elevation="0" Class="pa-3 rounded-lg" Style="border:1px solid var(--mud-palette-lines-default);">
|
||||
<MudStack Spacing="2">
|
||||
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
|
||||
<MudAvatar Size="Size.Small" Color="Color.Primary">
|
||||
<MudText Typo="Typo.caption" Style="color:#fff;">@(index + 1)</MudText>
|
||||
</MudAvatar>
|
||||
<MudTextField @bind-Value="Model.Steps[index].Title"
|
||||
Label="عنوان مرحله"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Style="flex:1;" />
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||
Color="Color.Error"
|
||||
Size="Size.Small"
|
||||
OnClick="() => Model.Steps.RemoveAt(index)" />
|
||||
</MudStack>
|
||||
<MudTextField @bind-Value="Model.Steps[index].Description"
|
||||
Label="توضیحات"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Lines="2" />
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Add"
|
||||
Color="Color.Primary"
|
||||
Variant="Variant.Text"
|
||||
OnClick="() => Model.Steps.Add(new StepModel())">
|
||||
افزودن مرحله
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── Features ── *@
|
||||
<MudExpansionPanel Text="@($"ویژگیها ({Model.Features.Count})")" Icon="@Icons.Material.Filled.Star">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.FeaturesTitle"
|
||||
Label="عنوان بخش ویژگیها"
|
||||
Placeholder="چرا کارا بازار سلامت؟"
|
||||
Variant="Variant.Outlined" />
|
||||
@for (var i = 0; i < Model.Features.Count; i++)
|
||||
{
|
||||
var index = i;
|
||||
<MudPaper Elevation="0" Class="pa-3 rounded-lg" Style="border:1px solid var(--mud-palette-lines-default);">
|
||||
<MudStack Spacing="2">
|
||||
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary" Style="min-width:20px;">@(index + 1)</MudText>
|
||||
<MudTextField @bind-Value="Model.Features[index].IconName"
|
||||
Label="نام آیکون"
|
||||
Placeholder="VerifiedUser"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Style="flex:1;" />
|
||||
<MudTextField @bind-Value="Model.Features[index].Title"
|
||||
Label="عنوان"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Style="flex:2;" />
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||
Color="Color.Error"
|
||||
Size="Size.Small"
|
||||
OnClick="() => Model.Features.RemoveAt(index)" />
|
||||
</MudStack>
|
||||
<MudTextField @bind-Value="Model.Features[index].Description"
|
||||
Label="توضیحات"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Lines="2" />
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Add"
|
||||
Color="Color.Primary"
|
||||
Variant="Variant.Text"
|
||||
OnClick="() => Model.Features.Add(new FeatureModel())">
|
||||
افزودن ویژگی
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── Stats ── *@
|
||||
<MudExpansionPanel Text="@($"آمار ({Model.Stats.Count})")" Icon="@Icons.Material.Filled.BarChart">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.StatsTitle"
|
||||
Label="عنوان بخش آمار"
|
||||
Placeholder="آمار پلتفرم"
|
||||
Variant="Variant.Outlined" />
|
||||
@for (var i = 0; i < Model.Stats.Count; i++)
|
||||
{
|
||||
var index = i;
|
||||
<MudPaper Elevation="0" Class="pa-3 rounded-lg" Style="border:1px solid var(--mud-palette-lines-default);">
|
||||
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary" Style="min-width:20px;">@(index + 1)</MudText>
|
||||
<MudTextField @bind-Value="Model.Stats[index].Label"
|
||||
Label="عنوان"
|
||||
Placeholder="رشد میانگین تیم"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Style="flex:2;" />
|
||||
<MudNumericField @bind-Value="Model.Stats[index].Value"
|
||||
Label="مقدار"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Style="flex:1;" />
|
||||
<MudTextField @bind-Value="Model.Stats[index].Suffix"
|
||||
Label="پسوند"
|
||||
Placeholder="%+"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Style="flex:1;" />
|
||||
<MudSelect @bind-Value="Model.Stats[index].Color"
|
||||
Label="رنگ"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Style="flex:1;">
|
||||
<MudSelectItem Value="@("Primary")">آبی</MudSelectItem>
|
||||
<MudSelectItem Value="@("Secondary")">بنفش</MudSelectItem>
|
||||
<MudSelectItem Value="@("Success")">سبز</MudSelectItem>
|
||||
<MudSelectItem Value="@("Warning")">زرد</MudSelectItem>
|
||||
<MudSelectItem Value="@("Error")">قرمز</MudSelectItem>
|
||||
<MudSelectItem Value="@("Info")">آبی روشن</MudSelectItem>
|
||||
</MudSelect>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||
Color="Color.Error"
|
||||
Size="Size.Small"
|
||||
OnClick="() => Model.Stats.RemoveAt(index)" />
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Add"
|
||||
Color="Color.Primary"
|
||||
Variant="Variant.Text"
|
||||
OnClick="() => Model.Stats.Add(new StatModel())">
|
||||
افزودن آمار
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── Testimonials ── *@
|
||||
<MudExpansionPanel Text="@($"نظرات مشتریان ({Model.Testimonials.Count})")" Icon="@Icons.Material.Filled.FormatQuote">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.TestimonialsTitle"
|
||||
Label="عنوان بخش نظرات"
|
||||
Placeholder="اعتماد مشتریان"
|
||||
Variant="Variant.Outlined" />
|
||||
@for (var i = 0; i < Model.Testimonials.Count; i++)
|
||||
{
|
||||
var index = i;
|
||||
<MudPaper Elevation="0" Class="pa-3 rounded-lg" Style="border:1px solid var(--mud-palette-lines-default);">
|
||||
<MudStack Spacing="2">
|
||||
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary" Style="min-width:20px;">@(index + 1)</MudText>
|
||||
<MudTextField @bind-Value="Model.Testimonials[index].Name"
|
||||
Label="نام"
|
||||
Placeholder="شرکت سینا نت"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Style="flex:1;" />
|
||||
<MudTextField @bind-Value="Model.Testimonials[index].Role"
|
||||
Label="سمت"
|
||||
Placeholder="مدیر عملیات"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Style="flex:1;" />
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||
Color="Color.Error"
|
||||
Size="Size.Small"
|
||||
OnClick="() => Model.Testimonials.RemoveAt(index)" />
|
||||
</MudStack>
|
||||
<MudTextField @bind-Value="Model.Testimonials[index].Quote"
|
||||
Label="متن نظر"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Lines="2" />
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Add"
|
||||
Color="Color.Primary"
|
||||
Variant="Variant.Text"
|
||||
OnClick="() => Model.Testimonials.Add(new TestimonialModel())">
|
||||
افزودن نظر
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── FAQs ── *@
|
||||
<MudExpansionPanel Text="@($"سوالات متداول ({Model.Faqs.Count})")" Icon="@Icons.Material.Filled.QuestionAnswer">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.FaqTitle"
|
||||
Label="عنوان بخش سوالات"
|
||||
Placeholder="سوالات متداول"
|
||||
Variant="Variant.Outlined" />
|
||||
@for (var i = 0; i < Model.Faqs.Count; i++)
|
||||
{
|
||||
var index = i;
|
||||
<MudPaper Elevation="0" Class="pa-3 rounded-lg" Style="border:1px solid var(--mud-palette-lines-default);">
|
||||
<MudStack Spacing="2">
|
||||
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary" Style="min-width:20px;">@(index + 1)</MudText>
|
||||
<MudTextField @bind-Value="Model.Faqs[index].Question"
|
||||
Label="سوال"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Style="flex:1;" />
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||
Color="Color.Error"
|
||||
Size="Size.Small"
|
||||
OnClick="() => Model.Faqs.RemoveAt(index)" />
|
||||
</MudStack>
|
||||
<MudTextField @bind-Value="Model.Faqs[index].Answer"
|
||||
Label="پاسخ"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Lines="2" />
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Add"
|
||||
Color="Color.Primary"
|
||||
Variant="Variant.Text"
|
||||
OnClick="() => Model.Faqs.Add(new FaqModel())">
|
||||
افزودن سوال
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── CTA Section ── *@
|
||||
<MudExpansionPanel Text="بنر فراخوان (CTA)" Icon="@Icons.Material.Filled.Campaign">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.CtaTitle"
|
||||
Label="عنوان CTA"
|
||||
Placeholder="آماده شروع هستید؟"
|
||||
Variant="Variant.Outlined" />
|
||||
<MudTextField @bind-Value="Model.CtaDescription"
|
||||
Label="توضیحات CTA"
|
||||
Placeholder="همین الان ثبتنام کنید..."
|
||||
Variant="Variant.Outlined"
|
||||
Lines="2" />
|
||||
<MudTextField @bind-Value="Model.CtaButtonText"
|
||||
Label="متن دکمه CTA"
|
||||
Placeholder="شروع رایگان"
|
||||
Variant="Variant.Outlined" />
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── Blog Section ── *@
|
||||
<MudExpansionPanel Text="بخش بلاگ" Icon="@Icons.Material.Filled.Article">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||
<MudSwitch T="bool" @bind-Value="_featuredBlogEnabled" Color="Color.Primary" />
|
||||
<MudText Typo="Typo.body2">نمایش آخرین مطالب بلاگ</MudText>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
</MudExpansionPanels>
|
||||
|
||||
@code {
|
||||
[Parameter] public LandingSettingsModel Model { get; set; } = new();
|
||||
|
||||
private bool _featuredBlogEnabled
|
||||
{
|
||||
get => Model.FeaturedBlogEnabled ?? true;
|
||||
set => Model.FeaturedBlogEnabled = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
@using BackOffice.Pages.Content.Components.SettingsModels
|
||||
|
||||
<MudExpansionPanels MultiExpansion="true" Elevation="0">
|
||||
|
||||
@* ── Page Settings ── *@
|
||||
<MudExpansionPanel Text="تنظیمات صفحه" Icon="@Icons.Material.Filled.Settings" IsInitiallyExpanded="true">
|
||||
<MudStack Spacing="2" Class="pa-2">
|
||||
<MudTextField @bind-Value="Model.PageDescription"
|
||||
Label="توضیحات صفحه"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="3"
|
||||
Placeholder="مجوزها و نمادهای اعتماد الکترونیکی ما..." />
|
||||
<MudSelect @bind-Value="Model.DisplayStyle"
|
||||
Label="نوع نمایش"
|
||||
Variant="Variant.Outlined">
|
||||
<MudSelectItem Value="@("grid")">گرید (Grid)</MudSelectItem>
|
||||
<MudSelectItem Value="@("list")">لیست (List)</MudSelectItem>
|
||||
<MudSelectItem Value="@("carousel")">اسلایدر (Carousel)</MudSelectItem>
|
||||
</MudSelect>
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
|
||||
@* ── Hint ── *@
|
||||
<MudAlert Severity="Severity.Info" Variant="Variant.Text" Dense="true" Class="mt-2">
|
||||
تصاویر مجوزها از بخش «مدیریت تصاویر» قابل افزودن و ویرایش هستند.
|
||||
</MudAlert>
|
||||
|
||||
</MudExpansionPanels>
|
||||
|
||||
@code {
|
||||
[Parameter] public LicensesSettingsModel Model { get; set; } = new();
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
@using BackOffice.Services.Content
|
||||
@using BackOffice.Pages.Content.Components.SettingsModels
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using BackOffice.Common.BaseComponents
|
||||
|
||||
@@ -63,12 +64,34 @@
|
||||
|
||||
<MudDivider Class="my-2" />
|
||||
|
||||
<MudText Typo="Typo.subtitle2">تنظیمات اختصاصی (JSON)</MudText>
|
||||
<MudTextField @bind-Value="Model.SettingsJson"
|
||||
Label="تنظیمات JSON"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="8"
|
||||
HelperText="تنظیمات اختصاصی هر صفحه — مثلاً خدمات، مراحل، FAQ و..." />
|
||||
<MudText Typo="Typo.subtitle1" Class="mb-1">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Tune" Size="Size.Small" Class="ml-1" />
|
||||
تنظیمات اختصاصی «@PageTitle»
|
||||
</MudText>
|
||||
|
||||
@* ── Typed Settings Editors ── *@
|
||||
@switch (Model.PageKey)
|
||||
{
|
||||
case "landing":
|
||||
<LandingSettingsEditor Model="_landingSettings" />
|
||||
break;
|
||||
case "about":
|
||||
<AboutSettingsEditor Model="_aboutSettings" />
|
||||
break;
|
||||
case "contact":
|
||||
<ContactSettingsEditor Model="_contactSettings" />
|
||||
break;
|
||||
case "licenses":
|
||||
<LicensesSettingsEditor Model="_licensesSettings" />
|
||||
break;
|
||||
default:
|
||||
<MudTextField @bind-Value="Model.SettingsJson"
|
||||
Label="تنظیمات JSON"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="8"
|
||||
HelperText="تنظیمات اختصاصی صفحه (JSON)" />
|
||||
break;
|
||||
}
|
||||
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||
<MudSwitch T="bool" @bind-Value="Model.IsActive" Color="Color.Primary" />
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json;
|
||||
using BackOffice.Pages.Content.Components.SettingsModels;
|
||||
using BackOffice.Services.Content;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
@@ -23,6 +25,70 @@ public partial class PageSettingsEditDialog
|
||||
private string? _imagePreview;
|
||||
private readonly long _maxAllowedSize = (1024 * 1024) * 5;
|
||||
|
||||
// ── Typed settings models ──
|
||||
private LandingSettingsModel _landingSettings = new();
|
||||
private AboutSettingsModel _aboutSettings = new();
|
||||
private ContactSettingsModel _contactSettings = new();
|
||||
private LicensesSettingsModel _licensesSettings = new();
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
DeserializeSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse existing SettingsJson into the typed model based on PageKey
|
||||
/// </summary>
|
||||
private void DeserializeSettings()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Model.SettingsJson)) return;
|
||||
|
||||
try
|
||||
{
|
||||
switch (Model.PageKey)
|
||||
{
|
||||
case "landing":
|
||||
_landingSettings = JsonSerializer.Deserialize<LandingSettingsModel>(Model.SettingsJson, JsonOptions) ?? new();
|
||||
break;
|
||||
case "about":
|
||||
_aboutSettings = JsonSerializer.Deserialize<AboutSettingsModel>(Model.SettingsJson, JsonOptions) ?? new();
|
||||
break;
|
||||
case "contact":
|
||||
_contactSettings = JsonSerializer.Deserialize<ContactSettingsModel>(Model.SettingsJson, JsonOptions) ?? new();
|
||||
break;
|
||||
case "licenses":
|
||||
_licensesSettings = JsonSerializer.Deserialize<LicensesSettingsModel>(Model.SettingsJson, JsonOptions) ?? new();
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If JSON is malformed, start with empty model
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the typed model back to JSON before saving
|
||||
/// </summary>
|
||||
private void SerializeSettings()
|
||||
{
|
||||
Model.SettingsJson = Model.PageKey switch
|
||||
{
|
||||
"landing" => JsonSerializer.Serialize(_landingSettings, JsonOptions),
|
||||
"about" => JsonSerializer.Serialize(_aboutSettings, JsonOptions),
|
||||
"contact" => JsonSerializer.Serialize(_contactSettings, JsonOptions),
|
||||
"licenses" => JsonSerializer.Serialize(_licensesSettings, JsonOptions),
|
||||
_ => Model.SettingsJson // keep raw JSON for unknown pages
|
||||
};
|
||||
}
|
||||
|
||||
private async Task OnImageSelected(IBrowserFile? file)
|
||||
{
|
||||
_imageFile = file;
|
||||
@@ -46,6 +112,9 @@ public partial class PageSettingsEditDialog
|
||||
|
||||
_loading = true;
|
||||
|
||||
// Serialize typed model → JSON before saving
|
||||
SerializeSettings();
|
||||
|
||||
if (_imageFile != null && _imageBuffer != null)
|
||||
{
|
||||
Model.ImageFile = _imageBuffer;
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
namespace BackOffice.Pages.Content.Components.SettingsModels;
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// LANDING PAGE
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
public class LandingSettingsModel
|
||||
{
|
||||
public string? HeroButtonPrimaryText { get; set; }
|
||||
public string? HeroButtonSecondaryText { get; set; }
|
||||
public List<TrustBadgeModel> TrustBadges { get; set; } = new();
|
||||
public string? StepsTitle { get; set; }
|
||||
public List<StepModel> Steps { get; set; } = new();
|
||||
public string? FeaturesTitle { get; set; }
|
||||
public List<FeatureModel> Features { get; set; } = new();
|
||||
public string? StatsTitle { get; set; }
|
||||
public List<StatModel> Stats { get; set; } = new();
|
||||
public string? TestimonialsTitle { get; set; }
|
||||
public List<TestimonialModel> Testimonials { get; set; } = new();
|
||||
public string? FaqTitle { get; set; }
|
||||
public List<FaqModel> Faqs { get; set; } = new();
|
||||
public string? CtaTitle { get; set; }
|
||||
public string? CtaDescription { get; set; }
|
||||
public string? CtaButtonText { get; set; }
|
||||
public bool? FeaturedBlogEnabled { get; set; }
|
||||
}
|
||||
|
||||
public class TrustBadgeModel
|
||||
{
|
||||
public string? IconName { get; set; }
|
||||
public string? Text { get; set; }
|
||||
}
|
||||
|
||||
public class StepModel
|
||||
{
|
||||
public string? Title { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
|
||||
public class FeatureModel
|
||||
{
|
||||
public string? IconName { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
|
||||
public class StatModel
|
||||
{
|
||||
public string? Label { get; set; }
|
||||
public double Value { get; set; }
|
||||
public string? Suffix { get; set; }
|
||||
public string? Color { get; set; }
|
||||
}
|
||||
|
||||
public class TestimonialModel
|
||||
{
|
||||
public string? Quote { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Role { get; set; }
|
||||
}
|
||||
|
||||
public class FaqModel
|
||||
{
|
||||
public string? Question { get; set; }
|
||||
public string? Answer { get; set; }
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// ABOUT PAGE
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
public class AboutSettingsModel
|
||||
{
|
||||
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; }
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// CONTACT PAGE
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
public class ContactSettingsModel
|
||||
{
|
||||
public string? Address { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Email { 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; }
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// LICENSES PAGE
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
public class LicensesSettingsModel
|
||||
{
|
||||
public string? PageDescription { get; set; }
|
||||
public string? DisplayStyle { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user