Search typeahead: show total found count in the dropdown
The /search/suggest endpoint now returns { items, total } — each filtered query
is reused for both the Take(5) preview and a CountAsync — and the dropdown's
footer link reads «مشاهده همه N نتیجه برای «q»» (Persian digits) instead of a
bare «همه نتایج». The /Search page already showed counts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -254,15 +254,18 @@
|
||||
timer = setTimeout(function () {
|
||||
fetch('/search/suggest?q=' + encodeURIComponent(q))
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (items) {
|
||||
if (!items || !items.length) { hide(); return; }
|
||||
.then(function (data) {
|
||||
var items = (data && data.items) || [];
|
||||
var total = (data && data.total) || items.length;
|
||||
if (!items.length) { hide(); return; }
|
||||
function fa(n) { return String(n).replace(/[0-9]/g, function (d) { return '۰۱۲۳۴۵۶۷۸۹'[+d]; }); }
|
||||
var html = items.map(function (it) {
|
||||
var sub = it.sub ? '<span class="ns-sub">' + hi(it.sub, q) + '</span>' : '';
|
||||
return '<a href="' + it.url + '"><span class="ns-type">' + esc(it.type) +
|
||||
'</span><span class="ns-text"><span class="ns-label">' + hi(it.label, q) +
|
||||
'</span>' + sub + '</span></a>';
|
||||
}).join('');
|
||||
html += '<a class="ns-all" href="/Search?Q=' + encodeURIComponent(q) + '">همه نتایج برای «' + esc(q) + '» ←</a>';
|
||||
html += '<a class="ns-all" href="/Search?Q=' + encodeURIComponent(q) + '">مشاهده همه ' + fa(total) + ' نتیجه برای «' + esc(q) + '» ←</a>';
|
||||
box.innerHTML = html;
|
||||
box.style.display = 'block';
|
||||
}).catch(function () { hide(); });
|
||||
|
||||
@@ -473,30 +473,32 @@ app.MapGet("/search/suggest", async (string? q, AppDbContext db) =>
|
||||
return fallback;
|
||||
}
|
||||
|
||||
var shiftRows = await db.Shifts
|
||||
.Where(s => s.Status == ShiftStatus.Open && s.Date >= today &&
|
||||
(EF.Functions.ILike(s.Facility.Name, like) || EF.Functions.ILike(s.Role.Name, like)
|
||||
|| EF.Functions.ILike(s.SpecialtyRequired, like) || EF.Functions.ILike(s.Description ?? "", like)))
|
||||
.OrderByDescending(s => s.CreatedAt).Take(5)
|
||||
// Define each filtered query once, then reuse it for BOTH the Take(5) preview and the total count.
|
||||
var shiftQ = db.Shifts.Where(s => s.Status == ShiftStatus.Open && s.Date >= today &&
|
||||
(EF.Functions.ILike(s.Facility.Name, like) || EF.Functions.ILike(s.Role.Name, like)
|
||||
|| EF.Functions.ILike(s.SpecialtyRequired, like) || EF.Functions.ILike(s.Description ?? "", like)));
|
||||
var jobQ = db.JobOpenings.Where(j => j.Status == ShiftStatus.Open && j.CreatedAt >= jobCut &&
|
||||
(EF.Functions.ILike(j.Title, like) || EF.Functions.ILike(j.Facility.Name, like)
|
||||
|| EF.Functions.ILike(j.Role.Name, like) || EF.Functions.ILike(j.Description ?? "", like)));
|
||||
var talentQ = db.TalentListings.Where(t => t.Status == ShiftStatus.Open && t.CreatedAt >= talentCut &&
|
||||
(EF.Functions.ILike(t.Tags ?? "", like) || EF.Functions.ILike(t.Role.Name, like) || EF.Functions.ILike(t.City.Name, like)
|
||||
|| EF.Functions.ILike(t.PersonName ?? "", like) || EF.Functions.ILike(t.Description ?? "", like)));
|
||||
|
||||
var shiftRows = await shiftQ.OrderByDescending(s => s.CreatedAt).Take(5)
|
||||
.Select(s => new { s.Id, Role = s.Role.Name, Fac = s.Facility.Name, City = s.Facility.City.Name, s.Description }).ToListAsync();
|
||||
var shifts = shiftRows.Select(s => new SuggestItem("شیفت", s.Role + " — " + s.Fac, "/Shifts/Details/" + s.Id, Snip(s.Description, term, s.City))).ToList();
|
||||
|
||||
var jobRows = await db.JobOpenings
|
||||
.Where(j => j.Status == ShiftStatus.Open && j.CreatedAt >= jobCut &&
|
||||
(EF.Functions.ILike(j.Title, like) || EF.Functions.ILike(j.Facility.Name, like)
|
||||
|| EF.Functions.ILike(j.Role.Name, like) || EF.Functions.ILike(j.Description ?? "", like)))
|
||||
.OrderByDescending(j => j.CreatedAt).Take(5)
|
||||
var jobRows = await jobQ.OrderByDescending(j => j.CreatedAt).Take(5)
|
||||
.Select(j => new { j.Id, j.Title, Fac = j.Facility.Name, City = j.Facility.City.Name, j.Description }).ToListAsync();
|
||||
var jobs = jobRows.Select(j => new SuggestItem("استخدام", j.Title, "/Jobs/Details/" + j.Id, Snip(j.Description, term, j.Fac + " · " + j.City))).ToList();
|
||||
|
||||
var talentRows = await db.TalentListings
|
||||
.Where(t => t.Status == ShiftStatus.Open && t.CreatedAt >= talentCut &&
|
||||
(EF.Functions.ILike(t.Tags ?? "", like) || EF.Functions.ILike(t.Role.Name, like) || EF.Functions.ILike(t.City.Name, like)
|
||||
|| EF.Functions.ILike(t.PersonName ?? "", like) || EF.Functions.ILike(t.Description ?? "", like)))
|
||||
.OrderByDescending(t => t.CreatedAt).Take(5)
|
||||
var talentRows = await talentQ.OrderByDescending(t => t.CreatedAt).Take(5)
|
||||
.Select(t => new { t.Id, t.PersonName, Role = t.Role.Name, City = t.City.Name, t.Tags, t.Description }).ToListAsync();
|
||||
var talent = talentRows.Select(t => new SuggestItem("آمادهبهکار", (t.PersonName ?? t.Role) + " — " + t.City, "/Talent/Details/" + t.Id, Snip(t.Description ?? t.Tags, term, t.Tags))).ToList();
|
||||
|
||||
// Total matches across all three types (drives the result count shown in the dropdown).
|
||||
var total = await shiftQ.CountAsync() + await jobQ.CountAsync() + await talentQ.CountAsync();
|
||||
|
||||
// round-robin merge so all three types appear, capped at 5
|
||||
var merged = new List<SuggestItem>();
|
||||
for (var i = 0; i < 5 && merged.Count < 5; i++)
|
||||
@@ -505,7 +507,7 @@ app.MapGet("/search/suggest", async (string? q, AppDbContext db) =>
|
||||
if (merged.Count < 5 && i < jobs.Count) merged.Add(jobs[i]);
|
||||
if (merged.Count < 5 && i < talent.Count) merged.Add(talent[i]);
|
||||
}
|
||||
return Results.Json(merged.Take(5));
|
||||
return Results.Json(new { items = merged.Take(5), total });
|
||||
});
|
||||
|
||||
app.Run();
|
||||
|
||||
Reference in New Issue
Block a user