people tags #387

This commit is contained in:
Hamid Reza Mohammadi 2026-10-03 21:22:12 +03:30
parent 3493225dc5
commit 15c8f8f526
3 changed files with 74 additions and 7 deletions

View File

@ -26,6 +26,12 @@ else
<p>@Html.Raw(Model.LastResult)</p>
}
<script>
// full catalog of every person except the subject ($Model.Person) - same shape/purpose as
// allGeoLocations/allGeoPeople in ReviewEdits.cshtml/Editor.cshtml, see OtherPeopleJson
var otherPeopleCatalog = @Html.Raw(Model.OtherPeopleJson);
</script>
<div class="up-card">
<form method="post" action="/User/SuggestNewPersonRelation?personId=@Model.Person.Id&amp;Handler=Suggest">
@Html.AntiForgeryToken()
@ -34,13 +40,28 @@ else
<tr>
<td>خویشاوند یا نامبردۀ دیگر:</td>
<td>
<select asp-for="OtherPersonId">
<option value="0">— انتخاب کنید —</option>
@foreach (var p in Model.OtherPeople)
{
<option value="@p.Id">@p.Name</option>
}
</select>
<div class="up-autocomplete-wrap">
<input type="text" id="other-person-search" autocomplete="off" placeholder="نام خویشاوند یا نامبردۀ دیگر را تایپ کنید..."
value="@(Model.OtherPersonId != 0 ? (Model.OtherPeople ?? new List<RMuseum.Models.Ganjoor.GanjoorRelatedPerson>()).Where(p => p.Id == Model.OtherPersonId).Select(p => p.Name).FirstOrDefault() : null)" />
<input type="hidden" asp-for="OtherPersonId" />
<div id="other-person-results" class="up-autocomplete-results" style="display:none;"></div>
</div>
<p class="up-hint"><small>
اگر بیش از یک نامبرده با نام مشابه در فهرست باشد (مثلاً شخصیت‌های شاهنامه)، پس از انتخاب از روی سال تولد/وفات مطمئن شوید
یا با کلیک روی 👁 اطلاعات کامل آن را ببینید.
</small></p>
<script>
setupPersonAutocomplete(
document.getElementById('other-person-search'),
document.getElementById('OtherPersonId'),
document.getElementById('other-person-results'),
otherPeopleCatalog,
null,
function (typedText) {
upToast('این نامبرده در فهرست نیست. طرف دیگر یک رابطه باید از قبل تعریف‌شده باشد؛ برای افزودن یک نامبردۀ تازه، ابتدا او را به‌طور جدا از صفحهٔ ویرایشگر تعریف کنید.', 'error');
}
);
</script>
</td>
</tr>
<tr>

View File

@ -40,6 +40,21 @@ namespace GanjooRazor.Areas.User.Pages
public List<GanjoorRelatedPerson> OtherPeople { get; set; }
/// <summary>
/// camelCase JSON of OtherPeople (id/name/birthYearInLHijri/deathYearInLHijri only), used
/// client-side for the search-as-you-type person picker replacing the old plain &lt;select&gt;
/// (see setupPersonAutocomplete in bk.js) - same shape/purpose as
/// ReviewEdits.cshtml.cs's/Editor.cshtml.cs's AllPeopleJson. The catalog can legitimately
/// contain more than one person sharing the same name (e.g. Shahnameh figures), so the
/// picker shows birth/death years as a disambiguating subtitle and a "👁 view full info" link
/// once a candidate is picked.
/// </summary>
public string OtherPeopleJson =>
JsonConvert.SerializeObject(
(OtherPeople ?? new List<GanjoorRelatedPerson>()).Select(p => new { p.Id, p.Name, p.BirthYearInLHijri, p.DeathYearInLHijri }),
new JsonSerializerSettings { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() }
);
[BindProperty]
public int OtherPersonId { get; set; }

View File

@ -2885,6 +2885,35 @@ function setupLocationAutocomplete(searchInput, hiddenInput, resultsBox, locatio
function setupPersonAutocomplete(searchInput, hiddenInput, resultsBox, people, onSelect, onNotFound) {
var MAX_RESULTS = 12;
// The birth/death-year subtitle in each dropdown row below helps tell two same-named people
// apart (the Shahnameh/mythological person catalog in particular has several), but it's not
// always enough - two people can share both a name and era, or the subtitle can be blank if
// neither year is recorded. So once a pick is made (or the field already carries one from the
// server, e.g. correcting an existing suggestion), show a small "👁 view full info" link right
// next to the box, opening that exact person's full profile (PersonWindow.open) so whoever is
// picking - or a moderator reviewing someone else's pick - can be sure it's the right person,
// not just one with a matching name/years.
var viewLink = document.createElement('a');
viewLink.href = 'javascript:void(0)';
viewLink.className = 'up-icon-btn';
viewLink.title = 'مشاهدهٔ اطلاعات کامل این شخصیت';
viewLink.setAttribute('aria-label', 'مشاهدهٔ اطلاعات کامل این شخصیت');
viewLink.style.marginRight = '4px';
viewLink.textContent = '👁';
viewLink.addEventListener('click', function () {
var id = parseInt(hiddenInput.value, 10);
if (id) PersonWindow.open(id);
});
if (searchInput.parentNode) {
searchInput.parentNode.insertBefore(viewLink, searchInput.nextSibling);
}
function updateViewLink() {
var id = parseInt(hiddenInput.value, 10);
viewLink.style.display = id ? 'inline' : 'none';
}
updateViewLink(); // the hidden input may already carry a value set by the server (editing/correcting an existing pick)
function hide() {
resultsBox.style.display = 'none';
resultsBox.innerHTML = '';
@ -2906,6 +2935,7 @@ function setupPersonAutocomplete(searchInput, hiddenInput, resultsBox, people, o
function selectPerson(p) {
searchInput.value = p.name;
hiddenInput.value = String(p.id);
updateViewLink();
hide();
if (onSelect) onSelect(p);
}
@ -2953,6 +2983,7 @@ function setupPersonAutocomplete(searchInput, hiddenInput, resultsBox, people, o
searchInput.addEventListener('input', function () {
hiddenInput.value = ''; // typing invalidates whatever was previously selected
updateViewLink();
var typed = searchInput.value.trim();
if (typed == '') {
hide();