From b092a5cfe56fdef24b58b23f2e43dcf97f9355a2 Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Mon, 8 Jun 2026 08:36:12 +0330 Subject: [PATCH] Admin: bulk-delete published ingested posts; talent: point to source when no phone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /Admin/Ingested: "حذف گروهی همه‌ی منتشرشده‌ها" button removes, in one transaction, every aggregated Shift/Job/Talent published from ingestion plus the approved (Normalized) raw items that produced them. Confirms first and reports counts. Raw rows deleted before the posts (they hold the FKs); DB cascade clears applications/interest events. - Talent details: when the contact number couldn't be extracted (e.g. Divar's login-gated reveal), show a prominent "مشاهده شماره در دیوار/مدجابز ↗" link to the original ad instead of the call button. Co-Authored-By: Claude Opus 4.8 --- .../Pages/Admin/Ingested.cshtml | 18 ++++++++++++++++ .../Pages/Admin/Ingested.cshtml.cs | 21 +++++++++++++++++++ .../Pages/Talent/Details.cshtml | 18 ++++++++++++---- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/JobsMedical.Web/Pages/Admin/Ingested.cshtml b/src/JobsMedical.Web/Pages/Admin/Ingested.cshtml index e0ec31b..d520584 100644 --- a/src/JobsMedical.Web/Pages/Admin/Ingested.cshtml +++ b/src/JobsMedical.Web/Pages/Admin/Ingested.cshtml @@ -16,6 +16,24 @@
+ @if (Model.Message is not null) + { +
✓ @Model.Message
+ } + + @{ int publishedCount = Model.Counts.GetValueOrDefault(JobsMedical.Web.Models.RawListingStatus.Normalized); } + @if (publishedCount > 0) + { +
+ + آگهی‌های منتشرشده روی سایت را که از جمع‌آوری ساخته شده‌اند یکجا حذف می‌کند. +
+ } +
@Html.Raw(Pill("all", "همه", Model.Counts.Values.Sum())) @Html.Raw(Pill("new", "در صف", C(JobsMedical.Web.Models.RawListingStatus.New))) diff --git a/src/JobsMedical.Web/Pages/Admin/Ingested.cshtml.cs b/src/JobsMedical.Web/Pages/Admin/Ingested.cshtml.cs index 1cb0c05..bf01e67 100644 --- a/src/JobsMedical.Web/Pages/Admin/Ingested.cshtml.cs +++ b/src/JobsMedical.Web/Pages/Admin/Ingested.cshtml.cs @@ -1,5 +1,6 @@ using JobsMedical.Web.Data; using JobsMedical.Web.Models; +using JobsMedical.Web.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; @@ -18,6 +19,7 @@ public class IngestedModel : PageModel public List Items { get; private set; } = new(); public int Total { get; private set; } public Dictionary Counts { get; private set; } = new(); + [TempData] public string? Message { get; set; } [BindProperty(SupportsGet = true)] public string? Status { get; set; } // new|flagged|published|discarded|all [BindProperty(SupportsGet = true)] public string? Source { get; set; } @@ -43,4 +45,23 @@ public class IngestedModel : PageModel Total = await q.CountAsync(); Items = await q.OrderByDescending(r => r.FetchedAt).Take(200).ToListAsync(); } + + /// + /// Bulk-delete everything that was published from ingestion: the aggregated Shift/Job/Talent + /// posts on the site AND the approved (Normalized) raw items that produced them. Done in a + /// transaction; the linked raw rows are removed first since they hold FKs to the posts. + /// + public async Task OnPostDeletePublishedAsync() + { + await using var tx = await _db.Database.BeginTransactionAsync(); + var raws = await _db.RawListings.Where(r => r.Status == RawListingStatus.Normalized).ExecuteDeleteAsync(); + var shifts = await _db.Shifts.Where(s => s.Source == ShiftSource.Aggregated).ExecuteDeleteAsync(); + var jobs = await _db.JobOpenings.Where(j => j.Source == ShiftSource.Aggregated).ExecuteDeleteAsync(); + var talent = await _db.TalentListings.Where(t => t.Source == ShiftSource.Aggregated).ExecuteDeleteAsync(); + await tx.CommitAsync(); + + string P(int n) => JalaliDate.ToPersianDigits(n.ToString()); + Message = $"حذف شد: {P(shifts)} شیفت، {P(jobs)} استخدام، {P(talent)} آماده‌به‌کار و {P(raws)} آیتم جمع‌آوری."; + return RedirectToPage(new { Status }); + } } diff --git a/src/JobsMedical.Web/Pages/Talent/Details.cshtml b/src/JobsMedical.Web/Pages/Talent/Details.cshtml index 7f16949..6ccef77 100644 --- a/src/JobsMedical.Web/Pages/Talent/Details.cshtml +++ b/src/JobsMedical.Web/Pages/Talent/Details.cshtml @@ -19,6 +19,13 @@ var digits = new string(t.Phone.Where(char.IsDigit).ToArray()); if (digits.Length >= 7) telHref = "tel:" + digits; } + // Friendly source name (used to point users to the original ad when no number was extracted). + string? sourceName = null; + if (!string.IsNullOrWhiteSpace(t.SourceUrl)) + { + var host = System.Uri.TryCreate(t.SourceUrl, UriKind.Absolute, out var su) ? su.Host : t.SourceUrl!; + sourceName = host.Contains("divar") ? "دیوار" : host.Contains("medjobs") ? "مدجابز" : host; + } }
@@ -68,14 +75,17 @@ 📞 @t.Phone

با این فرد مستقیم تماس بگیرید.

} + else if (!string.IsNullOrWhiteSpace(t.SourceUrl)) + { + @* Number wasn't extractable (e.g. behind a login-gated reveal) — point to the source. *@ +

شماره مستقیم استخراج نشد.

+ مشاهده شماره در @sourceName ↗ +

این آگهی از @sourceName جمع‌آوری شده؛ برای دریافت شماره به آگهی اصلی مراجعه کن.

+ } else {

شماره تماس ثبت نشده است.

} - @if (!string.IsNullOrWhiteSpace(t.SourceUrl)) - { - منبع آگهی ↗ - }