@@ -47,11 +51,14 @@
+
+
اگر مرکز در فهرست نیست، نامش را اینجا بنویس تا بهصورت «تأییدنشده» ساخته شود.
diff --git a/src/JobsMedical.Web/Pages/Admin/Review.cshtml.cs b/src/JobsMedical.Web/Pages/Admin/Review.cshtml.cs
index b2d8145..1832c05 100644
--- a/src/JobsMedical.Web/Pages/Admin/Review.cshtml.cs
+++ b/src/JobsMedical.Web/Pages/Admin/Review.cshtml.cs
@@ -27,9 +27,12 @@ public class ReviewModel : PageModel
public List Facilities { get; private set; } = new();
public List Roles { get; private set; } = new();
+ [TempData] public string? Error { get; set; }
+
// The editable form (prefilled from the parser, admin can override everything).
[BindProperty] public ListingKind Kind { get; set; }
[BindProperty] public int FacilityId { get; set; }
+ [BindProperty] public string? NewFacilityName { get; set; } // create a facility on the fly if none picked
[BindProperty] public int RoleId { get; set; }
[BindProperty] public string? Description { get; set; }
// Shift fields
@@ -77,6 +80,21 @@ public class ReviewModel : PageModel
Raw = await _db.RawListings.FirstOrDefaultAsync(r => r.Id == id);
if (Raw is null) return NotFound();
+ // Resolve the facility: prefer the picked one; otherwise create from the typed name.
+ // This prevents FK_Shifts_Facilities_FacilityId violations when no facility is selected
+ // (e.g. the dropdown is empty because no facilities exist yet).
+ var facilityId = await ResolveFacilityIdAsync();
+ if (facilityId is null)
+ {
+ Error = "یک مرکز درمانی معتبر انتخاب کن، یا در کادر «نام مرکز جدید» نام مرکز را وارد کن تا ساخته شود.";
+ return RedirectToPage(new { id });
+ }
+ if (!await _db.Roles.AnyAsync(r => r.Id == RoleId))
+ {
+ Error = "یک نقش معتبر انتخاب کن.";
+ return RedirectToPage(new { id });
+ }
+
Shift? createdShift = null;
JobOpening? createdJob = null;
if (Kind == ListingKind.Shift)
@@ -84,7 +102,7 @@ public class ReviewModel : PageModel
var role = await _db.Roles.FindAsync(RoleId);
var shift = new Shift
{
- FacilityId = FacilityId,
+ FacilityId = facilityId.Value,
RoleId = RoleId,
Date = ShiftDate,
StartTime = StartTime,
@@ -111,7 +129,7 @@ public class ReviewModel : PageModel
{
var job = new JobOpening
{
- FacilityId = FacilityId,
+ FacilityId = facilityId.Value,
RoleId = RoleId,
Title = string.IsNullOrWhiteSpace(Title) ? "موقعیت استخدامی" : Title.Trim(),
EmploymentType = EmploymentType,
@@ -150,6 +168,41 @@ public class ReviewModel : PageModel
_ => (new TimeOnly(8, 0), new TimeOnly(8, 0)),
};
+ ///
+ /// Returns a valid existing FacilityId, creating a new unverified facility from
+ /// when nothing valid is selected. Returns null when
+ /// neither a valid facility is picked nor a name is provided.
+ ///
+ private async Task ResolveFacilityIdAsync()
+ {
+ if (FacilityId > 0 && await _db.Facilities.AnyAsync(f => f.Id == FacilityId))
+ return FacilityId;
+
+ if (string.IsNullOrWhiteSpace(NewFacilityName))
+ return null;
+
+ // Reuse a same-named facility if one already exists, else create it.
+ var name = NewFacilityName.Trim();
+ var existing = await _db.Facilities.FirstOrDefaultAsync(f => f.Name == name);
+ if (existing is not null) return existing.Id;
+
+ var cityId = await _db.Cities.OrderByDescending(c => c.IsActive)
+ .Select(c => (int?)c.Id).FirstOrDefaultAsync();
+ if (cityId is null) return null; // no cities seeded — cannot create a facility
+
+ var facility = new Facility
+ {
+ Name = name,
+ CityId = cityId.Value,
+ Type = FacilityType.Hospital,
+ Verification = VerificationStatus.Unverified,
+ IsVerified = false,
+ };
+ _db.Facilities.Add(facility);
+ await _db.SaveChangesAsync();
+ return facility.Id;
+ }
+
private async Task LoadListsAsync()
{
Facilities = await _db.Facilities.Include(f => f.City).OrderBy(f => f.Name).ToListAsync();
diff --git a/src/JobsMedical.Web/wwwroot/css/site.css b/src/JobsMedical.Web/wwwroot/css/site.css
index d6f0f64..fa3a96c 100644
--- a/src/JobsMedical.Web/wwwroot/css/site.css
+++ b/src/JobsMedical.Web/wwwroot/css/site.css
@@ -399,6 +399,7 @@ label { font-size: 13px; }
.legal li { margin-bottom: 6px; }
.alert { padding: 12px 16px; border-radius: 10px; margin-bottom: 16px; font-weight: 600; }
.alert-success { background: var(--primary-soft); color: var(--primary-dark); }
+.alert-error { background: #fdecea; color: #b3261e; border: 1px solid #f5c2c0; }
/* notification bell badge */
.bell-badge { position:absolute; top:-6px; inset-inline-start:-8px; background:var(--accent); color:#fff;