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,82 @@
@page
@model JobsMedical.Web.Pages.Employer.PostShiftModel
@{
ViewData["Title"] = "انتشار شیفت";
}
<div class="page-head"><div class="container"><h1>انتشار شیفت جدید</h1></div></div>
<div class="container section" style="max-width:560px;">
@if (Model.Error is not null)
{
<div class="alert" style="background:#fdeaea; color:var(--danger);">@Model.Error</div>
}
@if (Model.MyFacilities.Count == 0)
{
<div class="card empty-state">
ابتدا یک مرکز ثبت کن.
<a class="btn btn-accent" asp-page="/Employer/RegisterFacility">ثبت مرکز</a>
</div>
}
else
{
<form method="post" class="card card-pad">
<div class="filter-group">
<label>مرکز درمانی</label>
<select name="FacilityId">
@foreach (var f in Model.MyFacilities)
{
<option value="@f.Id">@f.Name — @f.City?.Name</option>
}
</select>
</div>
<div class="filter-group">
<label>نقش مورد نیاز</label>
<select name="RoleId">
@foreach (var r in Model.Roles)
{
<option value="@r.Id">@r.Name</option>
}
</select>
</div>
<div class="filter-group">
<label>تاریخ (میلادی)</label>
<input type="date" name="Date" value="@Model.Date.ToString("yyyy-MM-dd")" dir="ltr" />
</div>
<div class="filter-group">
<label>نوع شیفت</label>
<select name="ShiftType">
<option value="0">صبح</option>
<option value="1">عصر</option>
<option value="2">شب</option>
<option value="3">آنکال</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" placeholder="مثلاً ۵۰" />
</div>
</div>
<p class="muted" style="font-size:12px;">می‌توانی فقط مبلغ، فقط درصد، یا هر دو را وارد کنی؛ اگر هر دو پر شود به کاربر «به انتخاب شما» نمایش داده می‌شود.</p>
<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" class="btn btn-accent btn-block btn-lg">انتشار شیفت</button>
</form>
}
</div>