Implement quick add functionality for incorrect answers; return collapsed partial for quick suggestions in the answers table.
This commit is contained in:
@@ -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 %}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user