Files
hamkadr/src/JobsMedical.Web/Pages/Jobs/Details.cshtml.cs
T
soroush.asadi 38031cb189
CI/CD / CI · dotnet build (push) Successful in 1m3s
CI/CD / Deploy · hamkadr (push) Successful in 1m18s
Per-ad contacts for shifts/jobs, stale-applicant filter, review source link
Phone fix: shifts/jobs showed Facility.Phone, but unnamed ads all share one
placeholder facility, so every such listing displayed the same stale number
while the ad's real phone sat unused in the description. ContactMethod is now
attachable to a Shift/JobOpening (not just talent); ingestion stores the ad's
own number(s) on each listing and the detail pages render them (new
_ContactList partial), falling back to the facility phone only when the ad had
none. Migration ShiftJobContacts (nullable owner FKs) — auto-applies on deploy.

Stale applicants: skip «آماده به کار» posts older than 7 days at ingest, by the
source's real timestamp (Telegram <time>, Bale date) or a Persian time-ago
phrase in the text (Divar «۲ هفته پیش»). Recorded as Discarded; shifts/jobs
are not aged out.

Admin: Review page now shows a «مشاهده آگهی در منبع» link (RawListing.SourceUrl)
so the source post can be checked before publishing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:28:12 +03:30

74 lines
2.3 KiB
C#

using JobsMedical.Web.Data;
using JobsMedical.Web.Models;
using JobsMedical.Web.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
namespace JobsMedical.Web.Pages.Jobs;
public class DetailsModel : PageModel
{
private readonly AppDbContext _db;
private readonly InterestService _interest;
private readonly JobsMedical.Web.Services.Scraping.SettingsService _settings;
public DetailsModel(AppDbContext db, InterestService interest,
JobsMedical.Web.Services.Scraping.SettingsService settings)
{
_db = db;
_interest = interest;
_settings = settings;
}
public JobOpening? Job { get; private set; }
public string? MapKey { get; private set; }
public bool ShowContact { get; private set; }
public bool Saved { get; private set; }
public bool Reported { get; private set; }
public async Task<IActionResult> OnGetAsync(int id)
{
await LoadAsync(id);
if (Job is null) return NotFound();
MapKey = (await _settings.GetAsync()).NeshanMapKey;
Reported = Request.Query["reported"] == "1";
await _interest.LogJobAsync(InterestEventType.View, id);
return Page();
}
public async Task<IActionResult> OnPostInterestAsync(int id)
{
await LoadAsync(id);
if (Job is null) return NotFound();
await _interest.LogJobAsync(InterestEventType.Apply, id);
ShowContact = true;
return Page();
}
public async Task<IActionResult> OnPostSaveAsync(int id)
{
await LoadAsync(id);
if (Job is null) return NotFound();
await _interest.LogJobAsync(InterestEventType.Save, id);
Saved = true;
return Page();
}
public async Task<IActionResult> OnPostDismissAsync(int id)
{
await _interest.LogJobAsync(InterestEventType.Dismiss, id);
return RedirectToPage("/Jobs/Index");
}
private async Task LoadAsync(int id)
{
Job = await _db.JobOpenings
.Include(j => j.Facility).ThenInclude(f => f.City)
.Include(j => j.Facility).ThenInclude(f => f.District)
.Include(j => j.Role)
.Include(j => j.Contacts)
.FirstOrDefaultAsync(j => j.Id == id);
}
}