Add hiring, AI parser+admin, OTP auth, employer dashboard, profit-share pay

- Hiring (استخدام) listings: JobOpening + /Jobs browse/detail + home section
- Heuristic Persian listing-parser + admin queue (/Admin) → publish shift/job
- Phone-OTP cookie auth + visitor-history linking + profile; Admin role gate
- Employer side: self-serve facility registration, dashboard, post/manage shifts & jobs, applicants list with contact
- Compensation models: fixed / hourly / profit-share (درصدی) / negotiable / choice (به انتخاب شما); SharePercent + JalaliDate.PayLabel; parser + filter

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-03 06:26:54 +03:30
parent 2fb86a435e
commit 563a40d1f4
30 changed files with 1761 additions and 27 deletions
@@ -0,0 +1,68 @@
@page
@model JobsMedical.Web.Pages.Employer.IndexModel
@{
ViewData["Title"] = "پنل کارفرما";
string TypeLabel(FacilityType t) => t switch
{
FacilityType.Hospital => "بیمارستان",
FacilityType.Clinic => "کلینیک",
_ => "درمانگاه",
};
}
<div class="page-head">
<div class="container">
<h1>پنل مرکز درمانی</h1>
<p class="muted">شیفت‌ها و موقعیت‌های استخدامی مرکز خود را مدیریت کن.</p>
</div>
</div>
<div class="container section">
@if (Model.Facilities.Count == 0)
{
<div class="card empty-state">
<p>هنوز مرکزی ثبت نکرده‌ای.</p>
<a class="btn btn-accent btn-lg" asp-page="/Employer/RegisterFacility">ثبت مرکز درمانی</a>
</div>
}
else
{
<div class="rec-banner">
<div>
<h2 style="margin:0 0 4px;">انتشار فرصت جدید</h2>
<span style="opacity:.9; font-size:14px;">شیفت یا موقعیت استخدامی منتشر کن</span>
</div>
<div style="display:flex; gap:8px;">
<a class="btn btn-outline" asp-page="/Employer/PostShift">+ شیفت</a>
<a class="btn btn-outline" asp-page="/Employer/PostJob">+ استخدام</a>
<a class="btn btn-outline" asp-page="/Employer/RegisterFacility">+ مرکز</a>
</div>
</div>
<div class="grid grid-3">
@foreach (var c in Model.Facilities)
{
<div class="card card-pad">
<div class="row" style="display:flex; justify-content:space-between; align-items:center;">
<span class="facility" style="font-weight:800; font-size:16px;">@c.Facility.Name</span>
@if (c.Facility.IsVerified)
{
<span class="badge badge-verified">✓ تأیید شده</span>
}
else
{
<span class="badge badge-type">در انتظار تأیید</span>
}
</div>
<p class="muted" style="margin:8px 0;">
@TypeLabel(c.Facility.Type) — 📍 @c.Facility.City?.Name@(c.Facility.District is not null ? "، " + c.Facility.District.Name : "")
</p>
<div class="info-row"><span class="k">شیفت‌های باز</span><span class="v">@JalaliDate.ToPersianDigits(c.OpenShifts.ToString())</span></div>
<div class="info-row"><span class="k">موقعیت‌های استخدامی</span><span class="v">@JalaliDate.ToPersianDigits(c.OpenJobs.ToString())</span></div>
<div class="info-row"><span class="k">اعلام تمایل‌ها</span><span class="v" style="color:var(--accent)">@JalaliDate.ToPersianDigits(c.Applicants.ToString())</span></div>
<a class="btn btn-outline btn-block" style="margin-top:12px;" asp-page="/Employer/Listings" asp-route-facilityId="@c.Facility.Id">مدیریت آگهی‌ها و متقاضیان</a>
</div>
}
</div>
}
</div>