Implement quick add functionality for incorrect answers; return collapsed partial for quick suggestions in the answers table.

This commit is contained in:
Ross
2025-11-11 13:42:34 +00:00
parent e4bab4a509
commit d88a626a76
2 changed files with 102 additions and 0 deletions
@@ -50,6 +50,12 @@
<tr>
<td {% if answer.proposed %}class="proposed-answer" data-aid="{{answer.pk}}" data-question-type="anatomy"{% endif %}>
<pre class="mb-0">{{ answer.answer }}</pre>
{% if answer.status == "0" %}
<div class="mt-1">
<button type="button" class="btn btn-sm btn-outline-secondary toggle-words-btn" aria-expanded="false">Add words</button>
<div class="word-buttons d-none mt-2" aria-hidden="true"></div>
</div>
{% endif %}
</td>
<td>{{ answer.get_status_display|default:answer.status }}</td>
<td>
@@ -182,6 +188,98 @@
}
</script>
<script>
// HTMX quick-add helpers for word buttons inside the answers table
var SUGGEST_URL = "{% url 'anatomy:question_suggest_incorrect_answers' question.pk %}";
var CSRF_TOKEN = "{{ csrf_token }}";
function toggleWords(btn) {
var container = btn.nextElementSibling;
if (!container) return;
// populate if empty
if (!container.dataset.populated) {
var td = btn.closest('td');
var pre = td ? td.querySelector('pre') : null;
var text = pre ? pre.textContent || pre.innerText : '';
buildWordButtons(container, text);
container.dataset.populated = '1';
}
var expanded = !container.classList.contains('d-none');
if (expanded) {
container.classList.add('d-none');
btn.setAttribute('aria-expanded', 'false');
container.setAttribute('aria-hidden', 'true');
} else {
container.classList.remove('d-none');
btn.setAttribute('aria-expanded', 'true');
container.setAttribute('aria-hidden', 'false');
}
}
function buildWordButtons(container, text) {
if (!text) return;
var toks = (text.toLowerCase().match(/\w+/g) || []);
var seen = {};
toks.forEach(function(t){
if (t.length < 3) return;
if (seen[t]) return;
seen[t]=true;
var b = document.createElement('button');
b.type = 'button';
b.className = 'btn btn-sm btn-outline-primary me-1 mb-1';
b.textContent = t;
b.dataset.word = t;
// do not attach inline handlers here; use delegated listener
container.appendChild(b);
});
if (!Object.keys(seen).length) {
var span = document.createElement('div');
span.className = 'small text-muted';
span.textContent = 'No suitable words';
container.appendChild(span);
}
}
// Delegated click handler to keep markup unobtrusive and work for HTMX swaps
document.addEventListener('click', function(e){
var toggle = e.target.closest('.toggle-words-btn');
if (toggle) {
e.preventDefault();
toggleWords(toggle);
return;
}
var wordBtn = e.target.closest('.word-buttons button');
if (wordBtn) {
e.preventDefault();
var word = wordBtn.dataset.word || wordBtn.textContent.trim();
if (word) addQuickSuggestion(word);
return;
}
});
function addQuickSuggestion(word) {
if (!word) return;
if (typeof htmx === 'undefined') {
// Fallback: simple POST via fetch
fetch(SUGGEST_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-CSRFToken': CSRF_TOKEN },
body: new URLSearchParams({ suggestion: word, quick: '1' })
}).then(function(r){ return r.text(); }).then(function(html){
var wrapper = document.getElementById('incorrect-answers');
if (wrapper) wrapper.innerHTML = html;
});
return;
}
htmx.ajax('POST', SUGGEST_URL, {
values: { suggestion: word, quick: '1' },
headers: { 'X-CSRFToken': CSRF_TOKEN },
target: '#incorrect-answers',
swap: 'innerHTML'
});
}
</script>
{% endblock %}
{% block css %}
+4
View File
@@ -1469,6 +1469,10 @@ def question_suggest_incorrect_answers(request, pk):
existing.append(suggestion)
question.answer_suggest_incorrect = existing
question.save()
# If this is a quick add (from the answers table) return the collapsed partial
if request.POST.get("quick"):
return render(request, "anatomy/partials/suggest_incorrect_collapsed.html", {"question": question})
# Recompute candidates and render the edit fragment
form = SuggestIncorrectAnswerForm(instance=question)
candidates = build_candidates(question)