Approximate-location map on aggregated listings (Divar coords)
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>
This commit is contained in:
+1635
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace JobsMedical.Web.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class ListingApproxCoords : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "Lat",
|
||||||
|
table: "TalentListings",
|
||||||
|
type: "double precision",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "Lng",
|
||||||
|
table: "TalentListings",
|
||||||
|
type: "double precision",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "Lat",
|
||||||
|
table: "Shifts",
|
||||||
|
type: "double precision",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "Lng",
|
||||||
|
table: "Shifts",
|
||||||
|
type: "double precision",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "Lat",
|
||||||
|
table: "JobOpenings",
|
||||||
|
type: "double precision",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "Lng",
|
||||||
|
table: "JobOpenings",
|
||||||
|
type: "double precision",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Lat",
|
||||||
|
table: "TalentListings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Lng",
|
||||||
|
table: "TalentListings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Lat",
|
||||||
|
table: "Shifts");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Lng",
|
||||||
|
table: "Shifts");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Lat",
|
||||||
|
table: "JobOpenings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Lng",
|
||||||
|
table: "JobOpenings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -664,6 +664,12 @@ namespace JobsMedical.Web.Migrations
|
|||||||
b.Property<int>("GenderRequirement")
|
b.Property<int>("GenderRequirement")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<double?>("Lat")
|
||||||
|
.HasColumnType("double precision");
|
||||||
|
|
||||||
|
b.Property<double?>("Lng")
|
||||||
|
.HasColumnType("double precision");
|
||||||
|
|
||||||
b.Property<string>("Requirements")
|
b.Property<string>("Requirements")
|
||||||
.HasMaxLength(1000)
|
.HasMaxLength(1000)
|
||||||
.HasColumnType("character varying(1000)");
|
.HasColumnType("character varying(1000)");
|
||||||
@@ -942,6 +948,12 @@ namespace JobsMedical.Web.Migrations
|
|||||||
b.Property<int>("GenderRequirement")
|
b.Property<int>("GenderRequirement")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<double?>("Lat")
|
||||||
|
.HasColumnType("double precision");
|
||||||
|
|
||||||
|
b.Property<double?>("Lng")
|
||||||
|
.HasColumnType("double precision");
|
||||||
|
|
||||||
b.Property<long?>("PayAmount")
|
b.Property<long?>("PayAmount")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
@@ -1020,6 +1032,12 @@ namespace JobsMedical.Web.Migrations
|
|||||||
b.Property<bool>("IsLicensed")
|
b.Property<bool>("IsLicensed")
|
||||||
.HasColumnType("boolean");
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<double?>("Lat")
|
||||||
|
.HasColumnType("double precision");
|
||||||
|
|
||||||
|
b.Property<double?>("Lng")
|
||||||
|
.HasColumnType("double precision");
|
||||||
|
|
||||||
b.Property<long?>("PayAmount")
|
b.Property<long?>("PayAmount")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ public class JobOpening
|
|||||||
[MaxLength(500)]
|
[MaxLength(500)]
|
||||||
public string? SourceUrl { get; set; }
|
public string? SourceUrl { get; set; }
|
||||||
|
|
||||||
|
// APPROXIMATE coords from the source ad (Divar) for aggregated openings without a facility address.
|
||||||
|
public double? Lat { get; set; }
|
||||||
|
public double? Lng { get; set; }
|
||||||
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
/// <summary>Contact channels harvested from the source ad (aggregated openings). When empty, the
|
/// <summary>Contact channels harvested from the source ad (aggregated openings). When empty, the
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ public class Shift
|
|||||||
[MaxLength(500)]
|
[MaxLength(500)]
|
||||||
public string? SourceUrl { get; set; } // لینک منبع در صورت جمعآوری از کانال
|
public string? SourceUrl { get; set; } // لینک منبع در صورت جمعآوری از کانال
|
||||||
|
|
||||||
|
// APPROXIMATE coords from the source ad (Divar's privacy-fuzzed center) for aggregated shifts
|
||||||
|
// whose facility has no address. Shown as a «محدودهٔ تقریبی» circle, never a precise pin.
|
||||||
|
public double? Lat { get; set; }
|
||||||
|
public double? Lng { get; set; }
|
||||||
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
public ICollection<Application> Applications { get; set; } = new List<Application>();
|
public ICollection<Application> Applications { get; set; } = new List<Application>();
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ public class TalentListing
|
|||||||
[MaxLength(500)]
|
[MaxLength(500)]
|
||||||
public string? SourceUrl { get; set; }
|
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;
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
// Transient: distance (km) when "near me" is active. Not persisted.
|
// Transient: distance (km) when "near me" is active. Not persisted.
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
var j = Model.Job!;
|
var j = Model.Job!;
|
||||||
var f = j.Facility!;
|
var f = j.Facility!;
|
||||||
var jobContacts = (j.Contacts ?? new List<JobsMedical.Web.Models.ContactMethod>()).ToList();
|
var jobContacts = (j.Contacts ?? new List<JobsMedical.Web.Models.ContactMethod>()).ToList();
|
||||||
|
// Map: listing's own approx coords (aggregated) then facility's; aggregated = approximate area.
|
||||||
|
var mapLat = j.Lat ?? f.Lat;
|
||||||
|
var mapLng = j.Lng ?? f.Lng;
|
||||||
|
var mapApprox = j.Source == JobsMedical.Web.Models.ShiftSource.Aggregated;
|
||||||
ViewData["Title"] = j.Title;
|
ViewData["Title"] = j.Title;
|
||||||
ViewData["Description"] = $"{j.Title} در {f.Name}، {f.City?.Name}. موقعیت استخدامی برای {j.Role?.Name}.";
|
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).
|
// Don't let Google index filled/expired openings (avoids dead "Job for jobs" results).
|
||||||
@@ -150,15 +154,15 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (j.Facility?.Lat is not null && j.Facility?.Lng is not null)
|
@if (mapLat is not null && mapLng is not null)
|
||||||
{
|
{
|
||||||
var latS = j.Facility.Lat.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
var latS = mapLat.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
var lngS = j.Facility.Lng.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
var lngS = mapLng.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
<div class="card card-pad" style="margin-top:16px;">
|
<div class="card card-pad" style="margin-top:16px;">
|
||||||
<h3 style="margin-top:0;">موقعیت مکانی</h3>
|
<h3 style="margin-top:0;">موقعیت مکانی</h3>
|
||||||
@if (!string.IsNullOrEmpty(Model.MapKey))
|
@if (!string.IsNullOrEmpty(Model.MapKey))
|
||||||
{
|
{
|
||||||
<div id="facmap" data-lat="@latS" data-lng="@lngS" style="height:200px; border-radius:10px; overflow:hidden; border:1px solid var(--line);"></div>
|
<div id="facmap" data-lat="@latS" data-lng="@lngS" data-approx="@(mapApprox ? "true" : "false")" style="height:200px; border-radius:10px; overflow:hidden; border:1px solid var(--line);"></div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -166,6 +170,10 @@
|
|||||||
🗺️<br /><small class="muted" dir="ltr">@latS، @lngS</small>
|
🗺️<br /><small class="muted" dir="ltr">@latS، @lngS</small>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@if (mapApprox)
|
||||||
|
{
|
||||||
|
<p class="muted" style="font-size:12px; margin:8px 0 0;">📍 محدودهٔ تقریبی (برگرفته از آگهی منبع)؛ موقعیت دقیق نیست.</p>
|
||||||
|
}
|
||||||
<a class="btn btn-outline btn-block" style="margin-top:8px;" target="_blank" rel="noopener"
|
<a class="btn btn-outline btn-block" style="margin-top:8px;" target="_blank" rel="noopener"
|
||||||
href="https://neshan.org/maps/@(latS),@(lngS),16z">مسیریابی در نشان</a>
|
href="https://neshan.org/maps/@(latS),@(lngS),16z">مسیریابی در نشان</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -183,7 +191,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (!string.IsNullOrEmpty(Model.MapKey) && Model.Job?.Facility?.Lat is not null)
|
@if (!string.IsNullOrEmpty(Model.MapKey) && mapLat is not null)
|
||||||
{
|
{
|
||||||
<partial name="_NeshanMap" model="Model.MapKey" />
|
<partial name="_NeshanMap" model="Model.MapKey" />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,17 @@
|
|||||||
if (!el || !window.L) return;
|
if (!el || !window.L) return;
|
||||||
var lat = parseFloat(el.dataset.lat), lng = parseFloat(el.dataset.lng);
|
var lat = parseFloat(el.dataset.lat), lng = parseFloat(el.dataset.lng);
|
||||||
if (isNaN(lat) || isNaN(lng)) return;
|
if (isNaN(lat) || isNaN(lng)) return;
|
||||||
|
// Approximate (aggregated) listings show a shaded AREA circle, not a precise pin.
|
||||||
|
var approx = el.dataset.approx === 'true';
|
||||||
var map = new L.Map('facmap', {
|
var map = new L.Map('facmap', {
|
||||||
key: '@Model', maptype: 'neshan', poi: true, traffic: false,
|
key: '@Model', maptype: 'neshan', poi: !approx, traffic: false,
|
||||||
center: [lat, lng], zoom: 15
|
center: [lat, lng], zoom: approx ? 14 : 15
|
||||||
});
|
});
|
||||||
L.marker([lat, lng]).addTo(map);
|
if (approx) {
|
||||||
|
var radius = parseInt(el.dataset.radius || '700', 10);
|
||||||
|
L.circle([lat, lng], { radius: radius, color: '#e07b39', weight: 1, fillColor: '#e07b39', fillOpacity: 0.18 }).addTo(map);
|
||||||
|
} else {
|
||||||
|
L.marker([lat, lng]).addTo(map);
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,6 +4,11 @@
|
|||||||
var s = Model.Shift!;
|
var s = Model.Shift!;
|
||||||
var f = s.Facility!;
|
var f = s.Facility!;
|
||||||
var shiftContacts = (s.Contacts ?? new List<JobsMedical.Web.Models.ContactMethod>()).ToList();
|
var shiftContacts = (s.Contacts ?? new List<JobsMedical.Web.Models.ContactMethod>()).ToList();
|
||||||
|
// Map: prefer the listing's own approx coords (aggregated ads) then the facility's. Aggregated =
|
||||||
|
// approximate → shown as an area circle with a disclaimer, never a precise pin.
|
||||||
|
var mapLat = s.Lat ?? f.Lat;
|
||||||
|
var mapLng = s.Lng ?? f.Lng;
|
||||||
|
var mapApprox = s.Source == JobsMedical.Web.Models.ShiftSource.Aggregated;
|
||||||
ViewData["Title"] = $"شیفت {s.SpecialtyRequired} - {f.Name}";
|
ViewData["Title"] = $"شیفت {s.SpecialtyRequired} - {f.Name}";
|
||||||
ViewData["Description"] = $"شیفت {s.SpecialtyRequired} در {f.Name}، {f.City?.Name}، تاریخ {JalaliDate.ToLongDate(s.Date)} از ساعت {JalaliDate.Time(s.StartTime)}.";
|
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.
|
// Past/filled shifts shouldn't stay in the index as dead pages.
|
||||||
@@ -166,13 +171,13 @@
|
|||||||
|
|
||||||
<div class="card card-pad" style="margin-top:16px;">
|
<div class="card card-pad" style="margin-top:16px;">
|
||||||
<h3 style="margin-top:0;">موقعیت مکانی</h3>
|
<h3 style="margin-top:0;">موقعیت مکانی</h3>
|
||||||
@if (f.Lat is not null && f.Lng is not null)
|
@if (mapLat is not null && mapLng is not null)
|
||||||
{
|
{
|
||||||
var latS = f.Lat.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
var latS = mapLat.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
var lngS = f.Lng.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
var lngS = mapLng.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
@if (!string.IsNullOrEmpty(Model.MapKey))
|
@if (!string.IsNullOrEmpty(Model.MapKey))
|
||||||
{
|
{
|
||||||
<div id="facmap" data-lat="@latS" data-lng="@lngS" style="height:200px; border-radius:10px; overflow:hidden; border:1px solid var(--line);"></div>
|
<div id="facmap" data-lat="@latS" data-lng="@lngS" data-approx="@(mapApprox ? "true" : "false")" style="height:200px; border-radius:10px; overflow:hidden; border:1px solid var(--line);"></div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -180,12 +185,16 @@
|
|||||||
🗺️<br /><small class="muted" dir="ltr">@latS، @lngS</small>
|
🗺️<br /><small class="muted" dir="ltr">@latS، @lngS</small>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@if (mapApprox)
|
||||||
|
{
|
||||||
|
<p class="muted" style="font-size:12px; margin:8px 0 0;">📍 محدودهٔ تقریبی (برگرفته از آگهی منبع)؛ موقعیت دقیق نیست.</p>
|
||||||
|
}
|
||||||
<a class="btn btn-outline btn-block" style="margin-top:8px;" target="_blank" rel="noopener"
|
<a class="btn btn-outline btn-block" style="margin-top:8px;" target="_blank" rel="noopener"
|
||||||
href="https://neshan.org/maps/@(latS),@(lngS),16z">مسیریابی در نشان</a>
|
href="https://neshan.org/maps/@(latS),@(lngS),16z">مسیریابی در نشان</a>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<p class="muted" style="margin:0;">مختصات این مرکز هنوز ثبت نشده است.</p>
|
<p class="muted" style="margin:0;">مختصات این آگهی ثبت نشده است.</p>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
@@ -201,7 +210,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (!string.IsNullOrEmpty(Model.MapKey) && Model.Shift?.Facility?.Lat is not null)
|
@if (!string.IsNullOrEmpty(Model.MapKey) && mapLat is not null)
|
||||||
{
|
{
|
||||||
<partial name="_NeshanMap" model="Model.MapKey" />
|
<partial name="_NeshanMap" model="Model.MapKey" />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,31 @@
|
|||||||
data-contact-type="talent" data-contact-id="@t.Id">📞 مشاهده راههای ارتباطی</button>
|
data-contact-type="talent" data-contact-id="@t.Id">📞 مشاهده راههای ارتباطی</button>
|
||||||
<p class="muted" style="font-size:12px; margin:10px 0 0;">با کلیک، شماره تماس و راههای ارتباطی نمایش داده میشود.</p>
|
<p class="muted" style="font-size:12px; margin:10px 0 0;">با کلیک، شماره تماس و راههای ارتباطی نمایش داده میشود.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (t.Lat is not null && t.Lng is not null)
|
||||||
|
{
|
||||||
|
var latS = t.Lat.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
var lngS = t.Lng.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
<div class="card card-pad" style="margin-top:16px;">
|
||||||
|
<h3 style="margin-top:0;">موقعیت تقریبی</h3>
|
||||||
|
@if (!string.IsNullOrEmpty(Model.MapKey))
|
||||||
|
{
|
||||||
|
<div id="facmap" data-lat="@latS" data-lng="@lngS" data-approx="true" style="height:200px; border-radius:10px; overflow:hidden; border:1px solid var(--line);"></div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div style="background:var(--primary-soft); border-radius:10px; height:140px; display:grid; place-items:center; color:var(--primary-dark); text-align:center; padding:10px;">
|
||||||
|
🗺️<br /><small class="muted" dir="ltr">@latS، @lngS</small>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<p class="muted" style="font-size:12px; margin:8px 0 0;">📍 محدودهٔ تقریبیِ فعالیت (برگرفته از آگهی منبع)؛ موقعیت دقیق نیست.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(Model.MapKey) && t.Lat is not null)
|
||||||
|
{
|
||||||
|
<partial name="_NeshanMap" model="Model.MapKey" />
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,9 +9,16 @@ namespace JobsMedical.Web.Pages.Talent;
|
|||||||
public class DetailsModel : PageModel
|
public class DetailsModel : PageModel
|
||||||
{
|
{
|
||||||
private readonly AppDbContext _db;
|
private readonly AppDbContext _db;
|
||||||
public DetailsModel(AppDbContext db) => _db = db;
|
private readonly JobsMedical.Web.Services.Scraping.SettingsService _settings;
|
||||||
|
|
||||||
|
public DetailsModel(AppDbContext db, JobsMedical.Web.Services.Scraping.SettingsService settings)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
public TalentListing? Item { get; private set; }
|
public TalentListing? Item { get; private set; }
|
||||||
|
public string? MapKey { get; private set; }
|
||||||
|
|
||||||
public async Task<IActionResult> OnGetAsync(int id)
|
public async Task<IActionResult> OnGetAsync(int id)
|
||||||
{
|
{
|
||||||
@@ -22,6 +29,7 @@ public class DetailsModel : PageModel
|
|||||||
.Include(t => t.Contacts)
|
.Include(t => t.Contacts)
|
||||||
.FirstOrDefaultAsync(t => t.Id == id);
|
.FirstOrDefaultAsync(t => t.Id == id);
|
||||||
if (Item is null) return NotFound();
|
if (Item is null) return NotFound();
|
||||||
|
MapKey = (await _settings.GetAsync()).NeshanMapKey;
|
||||||
return Page();
|
return Page();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,6 +334,7 @@ public class IngestionService
|
|||||||
Phone = !string.IsNullOrWhiteSpace(d?.Phone) ? d!.Phone!.Trim() : parsed.Phone,
|
Phone = !string.IsNullOrWhiteSpace(d?.Phone) ? d!.Phone!.Trim() : parsed.Phone,
|
||||||
Description = raw.RawText,
|
Description = raw.RawText,
|
||||||
Status = ShiftStatus.Open, Source = ShiftSource.Aggregated, SourceUrl = raw.SourceUrl,
|
Status = ShiftStatus.Open, Source = ShiftSource.Aggregated, SourceUrl = raw.SourceUrl,
|
||||||
|
Lat = raw.Lat, Lng = raw.Lng, // approx. area from the source ad (Divar)
|
||||||
Contacts = BuildContacts(d, parsed),
|
Contacts = BuildContacts(d, parsed),
|
||||||
Tags = BuildTags(parsed, d, role, city, extraRoleTags),
|
Tags = BuildTags(parsed, d, role, city, extraRoleTags),
|
||||||
});
|
});
|
||||||
@@ -381,6 +382,7 @@ public class IngestionService
|
|||||||
SalaryMin = parsed.PayAmount,
|
SalaryMin = parsed.PayAmount,
|
||||||
Description = raw.RawText, Status = ShiftStatus.Open, Source = ShiftSource.Aggregated,
|
Description = raw.RawText, Status = ShiftStatus.Open, Source = ShiftSource.Aggregated,
|
||||||
SourceUrl = raw.SourceUrl,
|
SourceUrl = raw.SourceUrl,
|
||||||
|
Lat = raw.Lat, Lng = raw.Lng, // approx. area from the source ad (Divar)
|
||||||
Contacts = BuildContacts(d, parsed), // the ad's OWN number(s) — fresh per listing
|
Contacts = BuildContacts(d, parsed), // the ad's OWN number(s) — fresh per listing
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -399,6 +401,7 @@ public class IngestionService
|
|||||||
: parsed.PayAmount is null ? PayType.Negotiable : PayType.PerShift,
|
: parsed.PayAmount is null ? PayType.Negotiable : PayType.PerShift,
|
||||||
PayAmount = parsed.PayAmount, SharePercent = parsed.SharePercent,
|
PayAmount = parsed.PayAmount, SharePercent = parsed.SharePercent,
|
||||||
Status = ShiftStatus.Open, Source = ShiftSource.Aggregated, SourceUrl = raw.SourceUrl,
|
Status = ShiftStatus.Open, Source = ShiftSource.Aggregated, SourceUrl = raw.SourceUrl,
|
||||||
|
Lat = raw.Lat, Lng = raw.Lng, // approx. area from the source ad (Divar)
|
||||||
Contacts = BuildContacts(d, parsed), // the ad's OWN number(s) — fresh per listing
|
Contacts = BuildContacts(d, parsed), // the ad's OWN number(s) — fresh per listing
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user