Add question reviews functionality with modal display and listing
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
<div id="question-reviews-list" class="mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="small text-muted">Review history</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-secondary" hx-get="{{ request.path }}" hx-target="#question-reviews-list" hx-swap="outerHTML">Refresh</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
{% if reviews %}
|
||||
<ul class="list-unstyled">
|
||||
{% for r in reviews %}
|
||||
<li class="mb-2">
|
||||
<div class="d-flex align-items-start gap-2">
|
||||
<span class="badge bg-info text-white">{{ r.get_status_display }}</span>
|
||||
<div>
|
||||
<div class="small">{{ r.comment|linebreaksbr }}</div>
|
||||
<div class="text-muted small">by {{ r.get_author_str }} on {{ r.created_on }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div class="small text-muted">No reviews yet</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -281,6 +281,11 @@ def generic_view_urls(generic_views: GenericViewBase):
|
||||
generic_views.question_set_review,
|
||||
name="question_set_review",
|
||||
),
|
||||
path(
|
||||
"question/<int:pk>/reviews",
|
||||
generic_views.question_reviews_list,
|
||||
name="question_reviews",
|
||||
),
|
||||
path("question/<int:pk>/thumbnail/fail", generic_views.question_thumbnail_fail, name="series_thumbnail_fail"),
|
||||
path("question/<int:pk>/thumbnail", generic_views.question_thumbnail, name="series_thumbnail"),
|
||||
path(
|
||||
|
||||
@@ -2998,6 +2998,21 @@ class GenericViewBase:
|
||||
else:
|
||||
return render(request, "generic/partials/question_review_form.html", {"question": question, "latest": latest, "app_name": self.app_name})
|
||||
|
||||
def question_reviews_list(self, request, pk):
|
||||
"""Return a partial listing all QuestionReview instances for a question."""
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from generic.models import QuestionReview
|
||||
|
||||
question = get_object_or_404(self.question_object, pk=pk)
|
||||
|
||||
ct = ContentType.objects.get_for_model(question)
|
||||
reviews = (
|
||||
QuestionReview.objects.filter(content_type=ct, object_id=question.pk)
|
||||
.order_by("-created_on")
|
||||
)
|
||||
|
||||
return render(request, "generic/partials/question_reviews_list.html", {"question": question, "reviews": reviews, "app_name": self.app_name})
|
||||
|
||||
@method_decorator(user_passes_test(lambda u: u.is_superuser))
|
||||
def user_answer_delete_multiple(self, request):
|
||||
print(request.POST["answer_ids"])
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
|
||||
{% partialdef links-partial %}
|
||||
|
||||
<div class="btn-group mt-3 mt-md-0" role="group" aria-label="Actions">
|
||||
<a class="btn btn-sm btn-outline-primary" href="{% url 'sbas:question_update' question.id %}">Edit</a>
|
||||
<a class="btn btn-sm btn-outline-secondary" href="{% url 'sbas:question_clone' question.id %}">Clone</a>
|
||||
<a class="btn btn-sm btn-outline-danger" href="{% url 'sbas:question_delete' pk=question.pk %}">Delete</a>
|
||||
<a class="btn btn-sm btn-outline-light" href="{% url 'admin:sbas_question_change' question.id %}">Admin</a>
|
||||
<button class="btn btn-sm btn-outline-info" onclick="return window.create_popup_window('{% url 'feedback_create' question_type='sbas' pk=question.pk %}')">Add Note</button>
|
||||
<button class="btn btn-sm btn-outline-success" hx-get="{% url 'sbas:question_set_review' question.pk %}?edit=1&new=1" hx-target="#question-review-block" hx-swap="innerHTML">Add Review</button>
|
||||
</div>
|
||||
<div class="btn-group mt-3 mt-md-0" role="group" aria-label="Actions">
|
||||
<a class="btn btn-sm btn-outline-primary" href="{% url 'sbas:question_update' question.id %}">Edit</a>
|
||||
<a class="btn btn-sm btn-outline-secondary" href="{% url 'sbas:question_clone' question.id %}">Clone</a>
|
||||
<a class="btn btn-sm btn-outline-danger" href="{% url 'sbas:question_delete' pk=question.pk %}">Delete</a>
|
||||
<a class="btn btn-sm btn-outline-light" href="{% url 'admin:sbas_question_change' question.id %}">Admin</a>
|
||||
<button class="btn btn-sm btn-outline-info" onclick="return window.create_popup_window('{% url 'feedback_create' question_type='sbas' pk=question.pk %}')">Add Note</button>
|
||||
<button class="btn btn-sm btn-outline-success" hx-get="{% url 'sbas:question_set_review' question.pk %}?edit=1&new=1" hx-target="#question-review-block" hx-swap="innerHTML">Add Review</button>
|
||||
<button class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#reviewsModal" hx-get="{% url 'sbas:question_reviews' question.pk %}" hx-target="#reviewsModalBody" hx-swap="innerHTML">History</button>
|
||||
</div>
|
||||
{% endpartialdef %}
|
||||
|
||||
{% block content %}
|
||||
@@ -22,16 +23,15 @@
|
||||
<h2 class="h4 mb-1">{{ question.title|default:"(No title set)" }}</h2>
|
||||
<div class="text-muted small">Created: {{ question.created_date|date:"d/m/Y" }}</div>
|
||||
</div>
|
||||
{% partial links-partial %}
|
||||
{% partial links-partial %}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-8">
|
||||
<div class="mb-3">
|
||||
<div>Review status:
|
||||
<div id="question-review-block" hx-get="{% url 'sbas:question_set_review' question.pk %}" hx-trigger="load" hx-swap="innerHTML">
|
||||
<div class="mb-2">Review status:</div>
|
||||
<div id="question-review-block" hx-get="{% url 'sbas:question_set_review' question.pk %}" hx-trigger="load" hx-swap="innerHTML">
|
||||
{# Placeholder while HTMX fetches review/form #}
|
||||
</div>
|
||||
</div>
|
||||
{% autoescape off %}
|
||||
<div class="card p-3 bg-body-secondary">{{ question.stem }}</div>
|
||||
@@ -175,4 +175,23 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
<!-- Reviews modal -->
|
||||
<div class="modal fade" id="reviewsModal" tabindex="-1" aria-labelledby="reviewsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="reviewsModalLabel">Review history</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="reviewsModalBody">
|
||||
<!-- HTMX will load the reviews list here -->
|
||||
<div class="text-center small text-muted">Loading…</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user