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)