From a4f4ba464ae1e1255386f3f60fd6ee2e0e2029aa Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 8 Dec 2025 11:15:03 +0000 Subject: [PATCH] Add advanced search help panel and functionality to ExamSearchSelectMultipleWidget --- generic/widgets.py | 61 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/generic/widgets.py b/generic/widgets.py index 8fa170fb..ca86d03c 100644 --- a/generic/widgets.py +++ b/generic/widgets.py @@ -347,8 +347,28 @@ class ExamSearchSelectMultipleWidget: {f'
{self.exam_model._meta.verbose_name.title()}
' if self.exam_model is not None else ''} +
+ +
+ Advanced search tips + +
@@ -364,6 +384,11 @@ class ExamSearchSelectMultipleWidget: const results = document.getElementById('{results_id}'); const select = document.getElementById('{field_id}'); const selectedList = document.getElementById('{selected_list_id}'); + const helpBtn = document.getElementById('exam_search_help_btn_{self.name}'); + const helpPanel = document.getElementById('exam_search_help_panel_{self.name}'); + + const defaultModel = '{self.exam_model._meta.label if self.exam_model is not None else ''}'; + const widgetFieldName = '{self.name}'; let timeout = null; function fetchResults(q) {{ @@ -371,7 +396,8 @@ class ExamSearchSelectMultipleWidget: results.innerHTML = ''; return; }} - const url = '{url}?field=' + encodeURIComponent('{self.name}') + '&q=' + encodeURIComponent(q) + ("&model=" + encodeURIComponent('{self.exam_model._meta.label}') || ''); + const modelParam = defaultModel ? '&model=' + encodeURIComponent(defaultModel) : ''; + const url = '{url}?field=' + encodeURIComponent(widgetFieldName) + '&q=' + encodeURIComponent(q) + modelParam; fetch(url) .then(r => r.text()) .then(html => {{ results.innerHTML = html; }}); @@ -382,14 +408,34 @@ class ExamSearchSelectMultipleWidget: timeout = setTimeout(() => fetchResults(search.value), 300); }}); + // Toggle help panel visibility + if (helpBtn && helpPanel) {{ + helpBtn.addEventListener('click', function(e) {{ + e.preventDefault(); + helpPanel.classList.toggle('d-none'); + const expanded = !helpPanel.classList.contains('d-none'); + helpBtn.setAttribute('aria-expanded', expanded ? 'true' : 'false'); + }}); + }} + + // Delegate add clicks from results results.addEventListener('click', function(e) {{ const btn = e.target.closest('.exam-add-btn'); if (!btn) return; const id = btn.getAttribute('data-exam-id'); const text = btn.getAttribute('data-exam-text') || btn.textContent.trim(); - addExamToField('{self.name}', id, text); + addExamToField(widgetFieldName, id, text); }}); + // Wire up "Add all results" button if present + const addAllBtn = results.parentElement.querySelector('.exam-add-all-btn'); + if (addAllBtn) {{ + addAllBtn.addEventListener('click', function(e) {{ + e.preventDefault(); + addAllExamsFromResults(widgetFieldName); + }}); + }} + window.addExamToField = function(fieldName, id, text) {{ const sel = document.getElementById('id_' + fieldName); if (!sel) return; @@ -414,6 +460,17 @@ class ExamSearchSelectMultipleWidget: const li = document.getElementById('selected_exam_' + fieldName + '_' + id); if (li && li.parentNode) li.parentNode.removeChild(li); }} + + window.addAllExamsFromResults = function(fieldName) {{ + const list = document.getElementById('exam_search_results_list_' + fieldName); + if (!list) return; + const items = list.querySelectorAll('li[data-exam-id]'); + items.forEach(function(it) {{ + const id = it.getAttribute('data-exam-id'); + const text = it.getAttribute('data-exam-text') || it.textContent.trim(); + addExamToField(fieldName, id, text); + }}); + }} }})();