Applicants: auto-tags + deep search w/ highlight; never delete (archive instead)
- Tags: parser extracts cert/skill keywords (mmt, ICU/CCU, دیالیز, اتاق عمل, اورژانس, مسئول فنی, پروانهدار…) + role + city into TalentListing.Tags (+ migration); shown as chips on cards. - Deep search on /Talent: «جستجوی عمیق» box does Postgres ILIKE across tags, description, person, area, role, city (every term must match); matches are highlighted with <mark> via SearchHighlight. - Never delete: ShiftStatus.Archived + the admin «بایگانی گروهی» action now ARCHIVES aggregated posts (hidden from site, kept in DB) and leaves the raw crawl rows intact — a permanent archive for future analytics. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
@{
|
||||
ViewData["Title"] = "آماده به کار — کادر درمان";
|
||||
ViewData["Description"] = "فهرست کادر درمان آماده همکاری (پزشک، پرستار، ماما، تکنسین و…) در تهران و سایر شهرها — مرکز درمانی میتواند مستقیم تماس بگیرد.";
|
||||
ViewData["q"] = Model.Q; // drives result highlighting in cards
|
||||
}
|
||||
|
||||
<div class="page-head">
|
||||
@@ -20,6 +21,14 @@
|
||||
<aside class="card card-pad filter-card">
|
||||
<h3>فیلترها</h3>
|
||||
<form method="get" id="filterForm">
|
||||
<div class="filter-group">
|
||||
<label>جستجوی عمیق</label>
|
||||
<div style="display:flex; gap:6px;">
|
||||
<input type="search" name="Q" value="@Model.Q" placeholder="مثلاً mmt پروانهدار تهران" style="flex:1;" />
|
||||
<button type="submit" class="btn btn-accent" style="padding:0 14px;">🔎</button>
|
||||
</div>
|
||||
<p class="muted" style="font-size:11px; margin:4px 0 0;">روی متن، تگها، نقش، شهر و نام جستجو میکند.</p>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label>شهر</label>
|
||||
<select name="CityId" onchange="this.form.submit()">
|
||||
|
||||
@@ -16,6 +16,7 @@ public class IndexModel : PageModel
|
||||
[BindProperty(SupportsGet = true)] public int? DistrictId { get; set; }
|
||||
[BindProperty(SupportsGet = true)] public int? RoleId { get; set; }
|
||||
[BindProperty(SupportsGet = true)] public Gender? GenderFilter { get; set; }
|
||||
[BindProperty(SupportsGet = true)] public string? Q { get; set; } // deep search
|
||||
|
||||
public List<TalentListing> Results { get; private set; } = new();
|
||||
public List<City> Cities { get; private set; } = new();
|
||||
@@ -42,6 +43,20 @@ public class IndexModel : PageModel
|
||||
if (GenderFilter is Gender g && g != Gender.Any)
|
||||
q = q.Where(t => t.Gender == Gender.Any || t.Gender == g);
|
||||
|
||||
// Deep search: every term must match somewhere (tags, role, city, person, area, description).
|
||||
if (!string.IsNullOrWhiteSpace(Q))
|
||||
foreach (var term in Q.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||||
{
|
||||
var like = $"%{term}%";
|
||||
q = q.Where(t =>
|
||||
EF.Functions.ILike(t.Tags ?? "", like) ||
|
||||
EF.Functions.ILike(t.Description ?? "", like) ||
|
||||
EF.Functions.ILike(t.PersonName ?? "", like) ||
|
||||
EF.Functions.ILike(t.AreaNote ?? "", like) ||
|
||||
EF.Functions.ILike(t.Role.Name, like) ||
|
||||
EF.Functions.ILike(t.City.Name, like));
|
||||
}
|
||||
|
||||
Results = await q.OrderByDescending(t => t.CreatedAt).ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user