Files
hamkadr/src/JobsMedical.Web/Pages/Talent/Details.cshtml
T
soroush.asadi c1c914df9f
CI/CD / CI · dotnet build (push) Successful in 2m54s
CI/CD / Deploy · hamkadr (push) Successful in 2m48s
Add per-user Like (پسندیدن) with a liked page and counts
Logged-in users can like a listing (job/shift/talent); dislike is removed per request — only likes.
- Like model (polymorphic by TargetType+TargetId) + EF migration; unique per (user, listing).
- POST /like toggles the like (auth required) and returns {liked, count}.
- Detail pages: the old ♡ Save / ✕ Dismiss buttons are replaced by a single heart Like button that
  shows the live count and toggles in place; clicking while logged out redirects to login.
- New «❤️ پسندیده‌ها» page (/Me/Liked) lists everything the user liked (open listings only), with a
  nav entry shown only when authenticated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 12:25:10 +03:30

97 lines
5.5 KiB
Plaintext

@page "{id:int}"
@model JobsMedical.Web.Pages.Talent.DetailsModel
@{
var t = Model.Item!;
var heading = string.IsNullOrWhiteSpace(t.PersonName) ? (t.Role?.Name ?? "آماده به کار") : t.PersonName!;
ViewData["Title"] = $"{heading} — آماده به کار";
// Personal contact info: keep this page out of search indexes.
ViewData["NoIndex"] = true;
string comp;
if (t.PayType == JobsMedical.Web.Models.PayType.Percentage && t.SharePercent is int sp)
comp = $"{JalaliDate.ToPersianDigits(sp.ToString())}٪ سهم درآمد";
else if (t.PayAmount is long pa && pa > 0)
comp = JalaliDate.Toman(pa) + " مدنظر";
else
comp = "توافقی";
}
<div class="page-head">
<div class="container">
<h1>@heading</h1>
<p class="muted">آماده همکاری @(t.Role is not null ? "— " + t.Role.Name : "") · 📍 @t.City?.Name@(t.District?.Name is not null ? "، " + t.District.Name : (t.AreaNote is not null ? "، " + t.AreaNote : ""))</p>
</div>
</div>
<div class="container section">
<div class="detail-grid">
<div>
<div class="card card-pad">
<div class="row" style="gap:8px; flex-wrap:wrap;">
@if (t.Role is not null) { <span class="badge badge-type">@t.Role.Name</span> }
<span class="badge badge-talent">آماده به کار</span>
@if (t.YearsExperience is int yrs && yrs > 0) { <span class="badge badge-day">@JalaliDate.ToPersianDigits(yrs.ToString()) سال سابقه</span> }
@if (t.IsLicensed) { <span class="badge badge-verified">پروانه‌دار</span> }
@if (t.Gender != JobsMedical.Web.Models.Gender.Any) { <span class="badge badge-gender">@JalaliDate.GenderLabel(t.Gender)</span> }
@if (t.Availability is JobsMedical.Web.Models.EmploymentType emp)
{
<span class="badge badge-job">@(emp switch {
JobsMedical.Web.Models.EmploymentType.FullTime => "تمام‌وقت",
JobsMedical.Web.Models.EmploymentType.PartTime => "پاره‌وقت",
JobsMedical.Web.Models.EmploymentType.Contract => "قراردادی",
_ => "طرح" })</span>
}
</div>
@if (!string.IsNullOrWhiteSpace(t.AreaNote))
{
<p style="margin:12px 0 0;"><strong>محدوده کاری:</strong> @t.AreaNote</p>
}
<p style="margin:12px 0 0;"><strong>دستمزد مدنظر:</strong> @comp</p>
@if (!string.IsNullOrWhiteSpace(t.Description))
{
<hr style="border:none; border-top:1px solid var(--line); margin:16px 0;" />
<p style="white-space:pre-wrap; margin:0;">@t.Description</p>
}
</div>
</div>
<aside>
<div class="card card-pad">
<h3 style="margin-top:0;">راه‌های ارتباطی</h3>
<button type="button" class="btn btn-accent btn-block btn-lg contact-trigger"
data-contact-type="talent" data-contact-id="@t.Id">📞 مشاهده راه‌های ارتباطی</button>
<p class="muted" style="font-size:12px; margin:10px 0 0;">با کلیک، شماره تماس و راه‌های ارتباطی نمایش داده می‌شود.</p>
<button type="button" class="btn @(Model.IsLiked ? "btn-accent" : "btn-outline") btn-block like-trigger" style="margin-top:10px;"
data-like-type="talent" data-like-id="@t.Id" data-liked="@(Model.IsLiked ? "true" : "false")">
<span class="like-ico">@(Model.IsLiked ? "♥" : "♡")</span> پسندیدم
<span class="like-count">@JalaliDate.ToPersianDigits(Model.LikeCount.ToString())</span>
</button>
</div>
@if (t.Lat is not null && t.Lng is not null)
{
var latS = t.Lat.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
var lngS = t.Lng.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
<div class="card card-pad" style="margin-top:16px;">
<h3 style="margin-top:0;">موقعیت تقریبی</h3>
@if (!string.IsNullOrEmpty(Model.MapKey))
{
<div id="facmap" data-lat="@latS" data-lng="@lngS" data-approx="true" style="height:200px; border-radius:10px; overflow:hidden; border:1px solid var(--line);"></div>
}
else
{
<div style="background:var(--primary-soft); border-radius:10px; height:140px; display:grid; place-items:center; color:var(--primary-dark); text-align:center; padding:10px;">
🗺️<br /><small class="muted" dir="ltr">@latS، @lngS</small>
</div>
}
<p class="muted" style="font-size:12px; margin:8px 0 0;">📍 محدودهٔ تقریبیِ فعالیت (برگرفته از آگهی منبع)؛ موقعیت دقیق نیست.</p>
</div>
}
</aside>
</div>
</div>
@if (!string.IsNullOrEmpty(Model.MapKey) && t.Lat is not null)
{
<partial name="_NeshanMap" model="Model.MapKey" />
}