diff --git a/src/JobsMedical.Web/Pages/Index.cshtml.cs b/src/JobsMedical.Web/Pages/Index.cshtml.cs
index 9f11cec..3742815 100644
--- a/src/JobsMedical.Web/Pages/Index.cshtml.cs
+++ b/src/JobsMedical.Web/Pages/Index.cshtml.cs
@@ -60,7 +60,7 @@ public class IndexModel : PageModel
LatestTalent = await _db.TalentListings
.Include(t => t.City).Include(t => t.District).Include(t => t.Role)
.Where(t => t.Status == ShiftStatus.Open
- && t.CreatedAt >= JobsMedical.Web.Services.Scraping.ListingPolicy.JobCutoffUtc)
+ && t.CreatedAt >= JobsMedical.Web.Services.Scraping.ListingPolicy.TalentCutoffUtc)
.OrderByDescending(t => t.CreatedAt)
.Take(3)
.ToListAsync();
diff --git a/src/JobsMedical.Web/Pages/Jobs/Details.cshtml b/src/JobsMedical.Web/Pages/Jobs/Details.cshtml
index fde4abe..b93e239 100644
--- a/src/JobsMedical.Web/Pages/Jobs/Details.cshtml
+++ b/src/JobsMedical.Web/Pages/Jobs/Details.cshtml
@@ -5,6 +5,8 @@
var f = j.Facility!;
ViewData["Title"] = j.Title;
ViewData["Description"] = $"{j.Title} در {f.Name}، {f.City?.Name}. موقعیت استخدامی برای {j.Role?.Name}.";
+ // Don't let Google index filled/expired openings (avoids dead "Job for jobs" results).
+ if (j.Status != JobsMedical.Web.Models.ShiftStatus.Open) ViewData["NoIndex"] = true;
string empLabel = j.EmploymentType switch
{
EmploymentType.FullTime => "تماموقت",
diff --git a/src/JobsMedical.Web/Pages/Shifts/Details.cshtml b/src/JobsMedical.Web/Pages/Shifts/Details.cshtml
index 8b956f2..0c80eca 100644
--- a/src/JobsMedical.Web/Pages/Shifts/Details.cshtml
+++ b/src/JobsMedical.Web/Pages/Shifts/Details.cshtml
@@ -5,6 +5,9 @@
var f = s.Facility!;
ViewData["Title"] = $"شیفت {s.SpecialtyRequired} - {f.Name}";
ViewData["Description"] = $"شیفت {s.SpecialtyRequired} در {f.Name}، {f.City?.Name}، تاریخ {JalaliDate.ToLongDate(s.Date)} از ساعت {JalaliDate.Time(s.StartTime)}.";
+ // Past/filled shifts shouldn't stay in the index as dead pages.
+ if (s.Status != JobsMedical.Web.Models.ShiftStatus.Open || s.Date < DateOnly.FromDateTime(DateTime.UtcNow))
+ ViewData["NoIndex"] = true;
var (badgeClass, typeLabel) = s.ShiftType switch
{
ShiftType.Day => ("badge-day", "شیفت صبح"),
diff --git a/src/JobsMedical.Web/Pages/Talent/Index.cshtml.cs b/src/JobsMedical.Web/Pages/Talent/Index.cshtml.cs
index 9ab47dc..67b83b6 100644
--- a/src/JobsMedical.Web/Pages/Talent/Index.cshtml.cs
+++ b/src/JobsMedical.Web/Pages/Talent/Index.cshtml.cs
@@ -34,7 +34,7 @@ public class IndexModel : PageModel
.Include(t => t.City)
.Include(t => t.District)
.Include(t => t.Role)
- .Where(t => t.Status == ShiftStatus.Open && t.CreatedAt >= ListingPolicy.JobCutoffUtc);
+ .Where(t => t.Status == ShiftStatus.Open && t.CreatedAt >= ListingPolicy.TalentCutoffUtc);
if (CityId is not null) q = q.Where(t => t.CityId == CityId);
if (DistrictId is not null) q = q.Where(t => t.DistrictId == DistrictId);
diff --git a/src/JobsMedical.Web/Services/Scraping/ListingArchiver.cs b/src/JobsMedical.Web/Services/Scraping/ListingArchiver.cs
index 0a14ea3..0730bd8 100644
--- a/src/JobsMedical.Web/Services/Scraping/ListingArchiver.cs
+++ b/src/JobsMedical.Web/Services/Scraping/ListingArchiver.cs
@@ -14,7 +14,11 @@ public static class ListingPolicy
/// A job opening older than this (since posting) is treated as stale and hidden.
public const int JobFreshnessDays = 30;
+ /// «آماده به کار» goes stale faster — the person is usually hired within a few weeks.
+ public const int TalentFreshnessDays = 21;
+
public static DateTime JobCutoffUtc => DateTime.UtcNow.AddDays(-JobFreshnessDays);
+ public static DateTime TalentCutoffUtc => DateTime.UtcNow.AddDays(-TalentFreshnessDays);
}
/// Sweeps stale listings into the Expired state (archive). Idempotent and cheap.
@@ -42,8 +46,9 @@ public class ListingArchiver
.Where(j => j.Status == ShiftStatus.Open && j.CreatedAt < jobCutoff)
.ExecuteUpdateAsync(u => u.SetProperty(j => j.Status, ShiftStatus.Expired), ct);
+ var talentCutoff = ListingPolicy.TalentCutoffUtc;
var expiredTalent = await _db.TalentListings
- .Where(t => t.Status == ShiftStatus.Open && t.CreatedAt < jobCutoff)
+ .Where(t => t.Status == ShiftStatus.Open && t.CreatedAt < talentCutoff)
.ExecuteUpdateAsync(u => u.SetProperty(t => t.Status, ShiftStatus.Expired), ct);
if (expiredShifts + expiredJobs + expiredTalent > 0)