[Demo] Add admin demo-mode toggle + generic website ingest source
CI/CD / CI · dotnet build (push) Failing after 1m40s
CI/CD / Deploy · hamkadr (push) Has been skipped

- AppSetting: DemoMode, WebsitesEnabled, WebsiteUrls
- Facility.IsDemo flag; SeedData split into SeedReferenceAsync (always)
  + SeedDemoAsync/ClearDemoAsync (idempotent, toggleable at runtime)
- WebsiteListingSource: scrape any admin-configured URL (og:title + content)
- Admin Settings: seed/clear demo card, demo-mode checkbox, website source
  fields; Program.cs seeds demo when DemoMode on (or in Development)
- EF migration DemoModeAndWebsites

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-04 13:43:07 +03:30
parent eae38373b9
commit 0c0449c2b9
11 changed files with 1341 additions and 149 deletions
@@ -1,3 +1,4 @@
using JobsMedical.Web.Data;
using JobsMedical.Web.Models;
using JobsMedical.Web.Services;
using JobsMedical.Web.Services.Scraping;
@@ -12,10 +13,12 @@ public class SettingsModel : PageModel
{
private readonly SettingsService _settings;
private readonly ISmsSender _sms;
public SettingsModel(SettingsService settings, ISmsSender sms)
private readonly AppDbContext _db;
public SettingsModel(SettingsService settings, ISmsSender sms, AppDbContext db)
{
_settings = settings;
_sms = sms;
_db = db;
}
[BindProperty] public IngestionMode Mode { get; set; }
@@ -48,8 +51,12 @@ public class SettingsModel : PageModel
[BindProperty] public string? VapidPrivateKey { get; set; }
[BindProperty] public string? VapidSubject { get; set; }
[BindProperty] public string? TestPhone { get; set; }
[BindProperty] public bool DemoMode { get; set; }
[BindProperty] public bool WebsitesEnabled { get; set; }
[BindProperty] public string? WebsiteUrls { get; set; }
[TempData] public string? Saved { get; set; }
[TempData] public string? SmsTest { get; set; }
[TempData] public string? DemoMsg { get; set; }
public async Task OnGetAsync()
{
@@ -78,6 +85,9 @@ public class SettingsModel : PageModel
SmsTemplate = s.SmsTemplate;
SmsSender = s.SmsSender;
NeshanMapKey = s.NeshanMapKey;
DemoMode = s.DemoMode;
WebsitesEnabled = s.WebsitesEnabled;
WebsiteUrls = s.WebsiteUrls;
PushEnabled = s.PushEnabled;
VapidPublicKey = s.VapidPublicKey;
VapidPrivateKey = s.VapidPrivateKey;
@@ -112,6 +122,9 @@ public class SettingsModel : PageModel
SmsTemplate = SmsTemplate,
SmsSender = SmsSender,
NeshanMapKey = NeshanMapKey,
DemoMode = DemoMode,
WebsitesEnabled = WebsitesEnabled,
WebsiteUrls = WebsiteUrls,
PushEnabled = PushEnabled,
VapidPublicKey = VapidPublicKey,
VapidPrivateKey = VapidPrivateKey,
@@ -121,6 +134,20 @@ public class SettingsModel : PageModel
return RedirectToPage();
}
public async Task<IActionResult> OnPostSeedDemoAsync()
{
var n = await SeedData.SeedDemoAsync(_db);
DemoMsg = n > 0 ? $"داده‌های نمونه ثبت شد ({n} مرکز + شیفت/استخدام)." : "داده‌های نمونه از قبل موجود است.";
return RedirectToPage();
}
public async Task<IActionResult> OnPostClearDemoAsync()
{
var n = await SeedData.ClearDemoAsync(_db);
DemoMsg = $"داده‌های نمونه حذف شد ({n} مرکز و آگهی‌های وابسته).";
return RedirectToPage();
}
public async Task<IActionResult> OnPostTestSmsAsync()
{
var s = await _settings.GetAsync();