4ab6ce29c9
We captured Divar's privacy-fuzzed coords on RawListing but discarded them for the listings that need them: unnamed-facility shifts/jobs dropped them (to avoid piling on the shared placeholder) and applicants had no coordinate field at all. - Add Lat/Lng to Shift, JobOpening, TalentListing (migration ListingApproxCoords). - Publish stores the source ad's approx coords on each aggregated listing. - Detail pages render the map from the listing's own coords (fallback: facility), and aggregated coords show as a shaded «محدودهٔ تقریبی» circle (not a precise pin) via _NeshanMap data-approx, with a disclaimer. Applicants get a map card (they had none) + the page now loads the Neshan key. Only Divar provides coords; the map needs NeshanMapKey set in admin settings. Existing rows get coords once reprocessed (RawListing already has them). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
72 lines
3.1 KiB
C#
72 lines
3.1 KiB
C#
using System.ComponentModel.DataAnnotations;
|
|
using System.ComponentModel.DataAnnotations.Schema;
|
|
|
|
namespace JobsMedical.Web.Models;
|
|
|
|
/// <summary>
|
|
/// «آماده به کار» — a healthcare worker advertising *themselves* as available for work
|
|
/// (the supply side), as opposed to a <see cref="Shift"/>/<see cref="JobOpening"/> posted by a
|
|
/// facility (the demand side). Very common in Iranian medical channels ("پرستار آماده همکاری…").
|
|
/// There is no facility; the valuable field is the contact <see cref="Phone"/>.
|
|
/// </summary>
|
|
public class TalentListing
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public int RoleId { get; set; }
|
|
public Role Role { get; set; } = null!;
|
|
|
|
[MaxLength(150)]
|
|
public string? PersonName { get; set; } // «دکتر سپیده علیزاده» (best-effort)
|
|
|
|
public int? YearsExperience { get; set; } // سابقه (سال)
|
|
public bool IsLicensed { get; set; } // پروانهدار / دارای پروانه
|
|
|
|
public int CityId { get; set; }
|
|
public City City { get; set; } = null!;
|
|
|
|
public int? DistrictId { get; set; }
|
|
public District? District { get; set; }
|
|
|
|
[MaxLength(150)]
|
|
public string? AreaNote { get; set; } // «فقط منطقه ۱» وقتی محله دقیق نگاشت نشد
|
|
|
|
/// <summary>Searchable keyword tags (space-separated): certs/skills (mmt, icu…), پروانهدار,
|
|
/// role, city. Drives deep search + tag chips.</summary>
|
|
[MaxLength(500)]
|
|
public string? Tags { get; set; }
|
|
|
|
public EmploymentType? Availability { get; set; } // تماموقت/پارهوقت/قراردادی...
|
|
public Gender Gender { get; set; } = Gender.Any; // جنسیت فرد
|
|
|
|
// Expected compensation — reuses the shift/job comp model.
|
|
public PayType PayType { get; set; } = PayType.Negotiable;
|
|
public long? PayAmount { get; set; } // مبلغ مدنظر (تومان)
|
|
public int? SharePercent { get; set; } // درصد/سهم درآمد مدنظر («۵۰٪ تسویه»)
|
|
|
|
[MaxLength(30)]
|
|
public string? Phone { get; set; } // primary phone (kept for cards/back-compat)
|
|
|
|
/// <summary>All contact channels (phones, email, Instagram, Telegram, Bale, website…).</summary>
|
|
public ICollection<ContactMethod> Contacts { get; set; } = new List<ContactMethod>();
|
|
|
|
[MaxLength(2000)]
|
|
public string? Description { get; set; }
|
|
|
|
public ShiftStatus Status { get; set; } = ShiftStatus.Open;
|
|
public ShiftSource Source { get; set; } = ShiftSource.Admin;
|
|
|
|
[MaxLength(500)]
|
|
public string? SourceUrl { get; set; }
|
|
|
|
// APPROXIMATE coords from the source ad (Divar) — an applicant has no facility, so this is the
|
|
// only location we have. Shown as a «محدودهٔ تقریبی» circle (the area they're available in).
|
|
public double? Lat { get; set; }
|
|
public double? Lng { get; set; }
|
|
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
|
|
// Transient: distance (km) when "near me" is active. Not persisted.
|
|
[NotMapped] public double? DistanceKm { get; set; }
|
|
}
|