Files
hamkadr/src/JobsMedical.Web/Pages/Admin/Review.cshtml
T
soroush.asadi a2fc70ae57
CI/CD / CI · dotnet build (push) Successful in 1m31s
CI/CD / Deploy · hamkadr (push) Successful in 1m44s
Fix FK violation when publishing a crawled listing without a facility
OnPostPublishAsync inserted a Shift/Job with FacilityId=0 when no
facility was selected (e.g. the dropdown is empty because no facilities
exist yet), throwing FK_Shifts_Facilities_FacilityId and surfacing the
production error page.

- Resolve-or-create the facility before insert: use the picked one, else
  create an unverified Facility from a typed name (reusing same-named).
- Guard the role too; on missing facility/role redirect back with a
  Persian error message instead of 500.
- Review form: add "new facility name" input + "— none —" option +
  error alert; add .alert-error style.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 07:09:18 +03:30

155 lines
9.0 KiB
Plaintext

@page "{id:int}"
@model JobsMedical.Web.Pages.Admin.ReviewModel
@{
ViewData["Title"] = "بررسی و انتشار آگهی";
var r = Model.Raw!;
}
<div class="page-head">
<div class="container"><h1>بررسی و انتشار آگهی</h1><p class="muted">منبع: @r.SourceChannel</p></div>
</div>
<div class="container section">
@if (Model.Error is not null)
{
<div class="alert alert-error" style="margin-bottom:16px;">⚠ @Model.Error</div>
}
<div class="detail-grid">
<div>
<div class="card card-pad">
<h3 style="margin-top:0;">متن خام</h3>
<p style="white-space:pre-wrap; margin:0;">@r.RawText</p>
</div>
@if (Model.Parsed is not null)
{
<div class="card card-pad" style="margin-top:16px;">
<h3 style="margin-top:0;">🤖 تشخیص خودکار (پارسر)</h3>
<div class="rec-reasons">
@foreach (var note in Model.Parsed.Notes)
{
<span class="rec-reason">• @note</span>
}
@if (Model.Parsed.CityName is not null) { <span class="rec-reason">• شهر: @Model.Parsed.CityName</span> }
@if (Model.Parsed.DistrictName is not null) { <span class="rec-reason">• محله: @Model.Parsed.DistrictName</span> }
@if (Model.Parsed.Phone is not null) { <span class="rec-reason">• تلفن: @Model.Parsed.Phone</span> }
</div>
<p class="muted" style="font-size:12px; margin-bottom:0;">این‌ها فقط پیشنهاد هستند؛ قبل از انتشار بررسی و اصلاح کن.</p>
</div>
}
</div>
<aside>
<form method="post" class="card card-pad">
<div class="filter-group">
<label>نوع آگهی</label>
<select name="Kind" id="kindSelect">
<option value="0" selected="@(Model.Kind == JobsMedical.Web.Models.ListingKind.Shift)">شیفت</option>
<option value="1" selected="@(Model.Kind == JobsMedical.Web.Models.ListingKind.Job)">استخدام</option>
</select>
</div>
<div class="filter-group">
<label>مرکز درمانی</label>
<select name="FacilityId">
<option value="0">— انتخاب نشده —</option>
@foreach (var f in Model.Facilities)
{
<option value="@f.Id">@f.Name — @f.City?.Name</option>
}
</select>
<input type="text" name="NewFacilityName" placeholder="یا نام مرکز جدید را وارد کن…" style="margin-top:6px;" />
<p class="muted" style="font-size:11px; margin:4px 0 0;">اگر مرکز در فهرست نیست، نامش را اینجا بنویس تا به‌صورت «تأییدنشده» ساخته شود.</p>
</div>
<div class="filter-group">
<label>نقش</label>
<select name="RoleId">
@foreach (var role in Model.Roles)
{
<option value="@role.Id" selected="@(Model.RoleId == role.Id)">@role.Name</option>
}
</select>
</div>
<div class="filter-group">
<label>جنسیت مورد نیاز</label>
<select name="GenderRequirement">
<option value="0" selected="@(Model.GenderRequirement == JobsMedical.Web.Models.Gender.Any)">فرقی نمی‌کند</option>
<option value="1" selected="@(Model.GenderRequirement == JobsMedical.Web.Models.Gender.Male)">آقا</option>
<option value="2" selected="@(Model.GenderRequirement == JobsMedical.Web.Models.Gender.Female)">خانم</option>
</select>
</div>
<div id="shiftFields">
<div class="filter-group">
<label>تاریخ شیفت (میلادی)</label>
<input type="date" name="ShiftDate" value="@Model.ShiftDate.ToString("yyyy-MM-dd")" dir="ltr" />
</div>
<div class="filter-group">
<label>نوع شیفت</label>
<select name="ShiftType">
<option value="0" selected="@(Model.ShiftType == JobsMedical.Web.Models.ShiftType.Day)">صبح</option>
<option value="1" selected="@(Model.ShiftType == JobsMedical.Web.Models.ShiftType.Evening)">عصر</option>
<option value="2" selected="@(Model.ShiftType == JobsMedical.Web.Models.ShiftType.Night)">شب</option>
<option value="3" selected="@(Model.ShiftType == JobsMedical.Web.Models.ShiftType.OnCall)">آنکال</option>
</select>
</div>
<div class="filter-group" style="display:flex; gap:8px;">
<div style="flex:1;"><label>شروع</label><input type="time" name="StartTime" value="@Model.StartTime.ToString("HH:mm")" dir="ltr" /></div>
<div style="flex:1;"><label>پایان</label><input type="time" name="EndTime" value="@Model.EndTime.ToString("HH:mm")" dir="ltr" /></div>
</div>
<div class="filter-group" style="display:flex; gap:8px;">
<div style="flex:1;"><label>مبلغ مقطوع (تومان)</label><input type="number" name="PayAmount" value="@Model.PayAmount" dir="ltr" /></div>
<div style="flex:1;"><label>یا سهم درآمد (٪)</label><input type="number" name="SharePercent" value="@Model.SharePercent" min="1" max="100" dir="ltr" /></div>
</div>
</div>
<div id="jobFields" style="display:none;">
<div class="filter-group">
<label>عنوان موقعیت</label>
<input type="text" name="Title" value="@Model.Title" />
</div>
<div class="filter-group">
<label>نوع همکاری</label>
<select name="EmploymentType">
<option value="0" selected="@(Model.EmploymentType == JobsMedical.Web.Models.EmploymentType.FullTime)">تمام‌وقت</option>
<option value="1" selected="@(Model.EmploymentType == JobsMedical.Web.Models.EmploymentType.PartTime)">پاره‌وقت</option>
<option value="2" selected="@(Model.EmploymentType == JobsMedical.Web.Models.EmploymentType.Contract)">قراردادی</option>
<option value="3" selected="@(Model.EmploymentType == JobsMedical.Web.Models.EmploymentType.Plan)">طرح</option>
</select>
</div>
<div class="filter-group" style="display:flex; gap:8px;">
<div style="flex:1;"><label>حقوق از</label><input type="number" name="SalaryMin" value="@Model.SalaryMin" dir="ltr" /></div>
<div style="flex:1;"><label>تا</label><input type="number" name="SalaryMax" value="@Model.SalaryMax" dir="ltr" /></div>
</div>
</div>
<div class="filter-group">
<label style="display:flex; align-items:center; gap:8px; font-weight:600;">
<input type="checkbox" name="Negotiable" value="true" style="width:auto;" checked="@Model.Negotiable" /> توافقی
</label>
</div>
<div class="filter-group">
<label>توضیحات</label>
<textarea name="Description" rows="3">@Model.Description</textarea>
</div>
<button type="submit" asp-page-handler="Publish" asp-route-id="@r.Id" class="btn btn-accent btn-block btn-lg">انتشار</button>
<button type="submit" asp-page-handler="Discard" asp-route-id="@r.Id" class="btn btn-outline btn-block" style="margin-top:8px;">رد و حذف از صف</button>
</form>
</aside>
</div>
</div>
@section Scripts {
<script>
var kind = document.getElementById('kindSelect');
function toggleKind() {
var isJob = kind.value === '1';
document.getElementById('jobFields').style.display = isJob ? 'block' : 'none';
document.getElementById('shiftFields').style.display = isJob ? 'none' : 'block';
}
kind.addEventListener('change', toggleKind);
toggleKind();
</script>
}