Enhance finding search functionality to support series findings and improve result display with badges
This commit is contained in:
@@ -209,7 +209,20 @@
|
|||||||
var timer = null;
|
var timer = null;
|
||||||
input.addEventListener('input', function(){ if(timer) clearTimeout(timer); timer = setTimeout(function(){ var q = encodeURIComponent(input.value || ''); var url = window.location.origin + '/atlas/search/findings/?q=' + q + (currentCaseF ? '&case=' + currentCaseF : ''); fetch(url, {credentials:'same-origin'}).then(function(r){ return r.text(); }).then(function(html){ results.innerHTML = html; }).catch(function(e){ console.error('finding picker fetch error', e); results.innerHTML = '<div class="alert alert-danger">Search failed</div>'; }); }, 300); });
|
input.addEventListener('input', function(){ if(timer) clearTimeout(timer); timer = setTimeout(function(){ var q = encodeURIComponent(input.value || ''); var url = window.location.origin + '/atlas/search/findings/?q=' + q + (currentCaseF ? '&case=' + currentCaseF : ''); fetch(url, {credentials:'same-origin'}).then(function(r){ return r.text(); }).then(function(html){ results.innerHTML = html; }).catch(function(e){ console.error('finding picker fetch error', e); results.innerHTML = '<div class="alert alert-danger">Search failed</div>'; }); }, 300); });
|
||||||
|
|
||||||
results.addEventListener('click', function(e){ var item = e.target.closest('.list-group-item'); if(!item) return; var pk = item.getAttribute('data-finding-pk'); if(!pk) return; insertAtCursor(textarea, '[['+'finding:'+pk+']]' ); if(overlay && overlay.parentNode) overlay.parentNode.removeChild(overlay); });
|
results.addEventListener('click', function(e){
|
||||||
|
var item = e.target.closest('.list-group-item');
|
||||||
|
if(!item) return;
|
||||||
|
var sfpk = item.getAttribute('data-seriesfinding-pk');
|
||||||
|
var fpk = item.getAttribute('data-finding-pk');
|
||||||
|
if(sfpk){
|
||||||
|
insertAtCursor(textarea, '[['+'seriesfinding:'+sfpk+']]' );
|
||||||
|
} else if(fpk){
|
||||||
|
insertAtCursor(textarea, '[['+'finding:'+fpk+']]' );
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(overlay && overlay.parentNode) overlay.parentNode.removeChild(overlay);
|
||||||
|
});
|
||||||
overlay.tabIndex = -1; overlay.focus();
|
overlay.tabIndex = -1; overlay.focus();
|
||||||
function fetchInitialFindings(){ var initFindingsUrl = window.location.origin + '/atlas/search/findings/' + (currentCaseF ? '?case=' + currentCaseF : ''); fetch(initFindingsUrl).then(function(r){ return r.text(); }).then(function(html){ results.innerHTML = html; }).catch(function(){}); }
|
function fetchInitialFindings(){ var initFindingsUrl = window.location.origin + '/atlas/search/findings/' + (currentCaseF ? '?case=' + currentCaseF : ''); fetch(initFindingsUrl).then(function(r){ return r.text(); }).then(function(html){ results.innerHTML = html; }).catch(function(){}); }
|
||||||
fetchInitialFindings();
|
fetchInitialFindings();
|
||||||
|
|||||||
@@ -1,16 +1,25 @@
|
|||||||
<div class="list-group">
|
<div class="list-group">
|
||||||
{% if findings %}
|
{% if findings %}
|
||||||
{% for f in findings %}
|
{% for f in findings %}
|
||||||
<div class="list-group-item d-flex justify-content-between align-items-center" data-finding-pk="{{ f.pk }}">
|
<div class="list-group-item d-flex justify-content-between align-items-center" data-finding-pk="{{ f.pk }}" data-seriesfinding-pk="{{ f.pk }}">
|
||||||
<div class="flex-fill">
|
<div class="flex-fill">
|
||||||
<div class="d-flex w-100 justify-content-between">
|
<div class="d-flex w-100 justify-content-between">
|
||||||
<strong class="mb-1">{{ f.name }}</strong>
|
<strong class="mb-1">{% if f.series %}{{ f.series.id }}{% else %}Series{% endif %}{% if f.description %}: {{ f.description|truncatechars:80 }}{% endif %}</strong>
|
||||||
<small class="text-muted">{{ f.pk }}</small>
|
<small class="text-muted">{{ f.pk }}</small>
|
||||||
</div>
|
</div>
|
||||||
<p class="mb-1 text-truncate">{{ f.description }}</p>
|
<p class="mb-1 text-truncate">
|
||||||
|
{% if f.findings.all %}
|
||||||
|
{% for sub in f.findings.all %}
|
||||||
|
<span class="badge bg-light text-dark me-1">{{ sub.name }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{{ f.description }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="ms-2">
|
<div class="ms-2">
|
||||||
<a class="btn btn-sm btn-outline-secondary" href="{% url 'atlas:finding_detail' f.pk %}">View</a>
|
{% if f.series %}
|
||||||
|
<a class="btn btn-sm btn-outline-secondary" href="{% url 'atlas:series_detail' pk=f.series.pk %}">View series</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
+8
-4
@@ -365,16 +365,20 @@ def finding_search(request):
|
|||||||
# Allow both HTMX and standard fetch/ajax requests (don't require the HTMX header)
|
# Allow both HTMX and standard fetch/ajax requests (don't require the HTMX header)
|
||||||
q = request.GET.get('q', '').strip()
|
q = request.GET.get('q', '').strip()
|
||||||
case_id = request.GET.get('case')
|
case_id = request.GET.get('case')
|
||||||
qs = Finding.objects.all()
|
# Use SeriesFinding objects for the widget (series-scoped findings)
|
||||||
|
qs = SeriesFinding.objects.select_related('series').prefetch_related('findings')
|
||||||
if case_id:
|
if case_id:
|
||||||
try:
|
try:
|
||||||
cid = int(case_id)
|
cid = int(case_id)
|
||||||
qs = qs.filter(case__pk=cid)
|
# Series are linked to cases through SeriesDetail; filter SeriesFinding by linked series' case
|
||||||
|
qs = qs.filter(series__seriesdetail__case__pk=cid)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
if q:
|
if q:
|
||||||
qs = qs.filter(name__icontains=q)
|
qs = qs.filter(
|
||||||
qs = qs.order_by('name')[:50]
|
Q(description__icontains=q) | Q(findings__name__icontains=q)
|
||||||
|
)
|
||||||
|
qs = qs.order_by('-pk').distinct()[:50]
|
||||||
html = render_to_string('atlas/partials/_finding_search_results.html', {'findings': qs}, request=request)
|
html = render_to_string('atlas/partials/_finding_search_results.html', {'findings': qs}, request=request)
|
||||||
return HttpResponse(html)
|
return HttpResponse(html)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user