Add supervisor search functionality with HTMX support for manual selection in bulk updates
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
{% if results %}
|
||||||
|
<ul style="list-style:none;padding:0;margin:0">
|
||||||
|
{% for item in results %}
|
||||||
|
<li style="margin:6px 0;">
|
||||||
|
<strong>{{ item.text|escape }}</strong>
|
||||||
|
<button type="button" onclick="setManualSupervisor({{ row }}, {{ item.id }}, '{{ item.text|escapejs }}')" style="margin-left:8px;">Select</button>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<div><em>No supervisors found</em></div>
|
||||||
|
{% endif %}
|
||||||
@@ -40,19 +40,37 @@
|
|||||||
<div>
|
<div>
|
||||||
<span style="color:red">No match</span>
|
<span style="color:red">No match</span>
|
||||||
<div>
|
<div>
|
||||||
<form hx-get="{% url 'generic:user_search' %}" hx-target="#user-results-{{ r.row_index }}" hx-swap="innerHTML" onsubmit="return false;">
|
<div class="d-inline-flex align-items-center" style="gap:6px">
|
||||||
|
<form hx-get="{% url 'generic:user_search' %}" hx-target="#user-results-{{ r.row_index }}" hx-swap="innerHTML" onsubmit="return false;" class="d-inline-flex align-items-center">
|
||||||
<input type="text" name="q" id="user-search-input-{{ r.row_index }}" placeholder="Search users..." style="width:220px" />
|
<input type="text" name="q" id="user-search-input-{{ r.row_index }}" placeholder="Search users..." style="width:220px" />
|
||||||
<input type="hidden" name="row" value="{{ r.row_index }}" />
|
<input type="hidden" name="row" value="{{ r.row_index }}" />
|
||||||
<button type="submit">Search</button>
|
<button type="submit" class="btn btn-sm btn-secondary">Search</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
<div id="user-results-{{ r.row_index }}"></div>
|
<div id="user-results-{{ r.row_index }}"></div>
|
||||||
|
</div>
|
||||||
<input type="hidden" name="manual_user_{{ r.row_index }}" id="manual_user_{{ r.row_index }}" />
|
<input type="hidden" name="manual_user_{{ r.row_index }}" id="manual_user_{{ r.row_index }}" />
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ r.supervisor_text }}</td>
|
<td>{{ r.supervisor_text }}</td>
|
||||||
<td>{% if r.matched_supervisor %}<a href="{% url 'generic:supervisor_detail' r.matched_supervisor.pk %}">{{ r.matched_supervisor.name }}</a>{% else %}<span style="color:red">No match</span>{% endif %}</td>
|
<td>
|
||||||
|
{% if r.matched_supervisor %}
|
||||||
|
<a href="{% url 'generic:supervisor_detail' r.matched_supervisor.pk %}">{{ r.matched_supervisor.name }}</a>
|
||||||
|
{% else %}
|
||||||
|
<div>
|
||||||
|
<span style="color:red">No match</span>
|
||||||
|
<div class="d-inline-flex align-items-center" style="gap:6px">
|
||||||
|
<form hx-get="{% url 'generic:supervisor_search' %}" hx-target="#supervisor-results-{{ r.row_index }}" hx-swap="innerHTML" onsubmit="return false;" class="d-inline-flex align-items-center">
|
||||||
|
<input type="text" name="q" id="supervisor-search-input-{{ r.row_index }}" placeholder="Search supervisors..." style="width:220px" />
|
||||||
|
<input type="hidden" name="row" value="{{ r.row_index }}" />
|
||||||
|
<button type="submit" class="btn btn-sm btn-secondary">Search</button>
|
||||||
|
</form>
|
||||||
|
<div id="supervisor-results-{{ r.row_index }}"></div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="manual_supervisor_{{ r.row_index }}" id="manual_supervisor_{{ r.row_index }}" />
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -80,6 +98,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setManualSupervisor(rowIndex, id, text) {
|
||||||
|
const hiddenInput = document.getElementById('manual_supervisor_' + rowIndex);
|
||||||
|
const resultsDiv = document.getElementById('supervisor-results-' + rowIndex);
|
||||||
|
if (hiddenInput) {
|
||||||
|
hiddenInput.value = id;
|
||||||
|
}
|
||||||
|
if (resultsDiv) {
|
||||||
|
resultsDiv.innerHTML = '<div>Selected: ' + escapeHtml(text) + '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function escapeHtml(unsafe) {
|
function escapeHtml(unsafe) {
|
||||||
if (!unsafe) return '';
|
if (!unsafe) return '';
|
||||||
return String(unsafe)
|
return String(unsafe)
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ urlpatterns = [
|
|||||||
name="supervisor-autocomplete",
|
name="supervisor-autocomplete",
|
||||||
),
|
),
|
||||||
path("user-search/", views.user_search, name="user_search"),
|
path("user-search/", views.user_search, name="user_search"),
|
||||||
|
path("supervisor-search/", views.supervisor_search, name="supervisor_search"),
|
||||||
path(
|
path(
|
||||||
"cids/manage/<int:pk>/update", views.CidUserUpdate.as_view(), name="update_cid"
|
"cids/manage/<int:pk>/update", views.CidUserUpdate.as_view(), name="update_cid"
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4423,6 +4423,26 @@ def user_search(request):
|
|||||||
return JsonResponse({"results": results})
|
return JsonResponse({"results": results})
|
||||||
|
|
||||||
|
|
||||||
|
@user_is_cid_user_manager
|
||||||
|
def supervisor_search(request):
|
||||||
|
"""Simple JSON/HTMX endpoint to search supervisors by name/email for manual matching."""
|
||||||
|
q = request.GET.get("q", "").strip()
|
||||||
|
results = []
|
||||||
|
if q:
|
||||||
|
qs = Supervisor.objects.filter(
|
||||||
|
Q(name__icontains=q) | Q(email__icontains=q)
|
||||||
|
).order_by("name")[:50]
|
||||||
|
|
||||||
|
for s in qs:
|
||||||
|
results.append({"id": s.pk, "text": f"{s.name} ({s.email})"})
|
||||||
|
|
||||||
|
row = request.GET.get("row")
|
||||||
|
if request.headers.get("HX-Request") == "true" or row is not None:
|
||||||
|
return render(request, "generic/partials/supervisor_search_results.html", {"results": results, "row": int(row) if row is not None else None})
|
||||||
|
|
||||||
|
return JsonResponse({"results": results})
|
||||||
|
|
||||||
|
|
||||||
def create_trainee(request, context=None):
|
def create_trainee(request, context=None):
|
||||||
return create_user(request, context, trainee=True)
|
return create_user(request, context, trainee=True)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user