a2fc70ae57
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>
155 lines
9.0 KiB
Plaintext
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>
|
|
}
|