Skip to content

Commit 5f62cdf

Browse files
authored
Proper bulk action confirm dialogs on submission all view (#4832)
The bulk actions only had js alerts, this PR implements proper confirm dialogs as we have for submission detail view.
1 parent 48e3c95 commit 5f62cdf

8 files changed

Lines changed: 171 additions & 29 deletions

File tree

hypha/apply/funds/templates/submissions/all.html

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -480,9 +480,10 @@
480480

481481
{% if can_bulk_archive %}
482482
<button
483-
hx-post="{% url 'apply:submissions:bulk-archive' %}"
483+
hx-get="{% url 'apply:submissions:bulk-archive-confirm' %}"
484484
hx-include="[name='selectedSubmissionIds']"
485-
hx-confirm="{% trans 'Are you sure you want to archive the selected submissions?' %}"
485+
hx-target="#htmx-modal"
486+
hx-swap="innerHTML"
486487
class="btn btn-sm"
487488
>
488489
{% heroicon_outline "lock-closed" aria_hidden="true" size=14 class="stroke-base-content/80" %}
@@ -492,19 +493,21 @@
492493

493494
{% if can_bulk_delete %}
494495
<button
495-
hx-post="{% url 'apply:submissions:bulk-delete' %}"
496+
hx-get="{% url 'apply:submissions:bulk-delete-confirm' %}"
496497
hx-include="[name='selectedSubmissionIds']"
497-
hx-confirm="{% trans 'Are you sure you want to delete the selected submissions? This action cannot be undone.' %}"
498+
hx-target="#htmx-modal"
499+
hx-swap="innerHTML"
498500
class="btn btn-sm"
499501
>
500502
{% heroicon_outline "trash" aria_hidden="true" size=14 class="stroke-base-content/80" %}
501503
{% trans "Delete" %}
502504
</button>
503505
{% if SUBMISSION_ANONYMIZATION_ENABLED %}
504506
<button
505-
hx-post="{% url 'apply:submissions:bulk-anonymize' %}"
507+
hx-get="{% url 'apply:submissions:bulk-anonymize-confirm' %}"
506508
hx-include="[name='selectedSubmissionIds']"
507-
hx-confirm="{% trans 'Are you sure you want to anonymize the selected submissions? All draft submission will be deleted. This action cannot be undone.' %}"
509+
hx-target="#htmx-modal"
510+
hx-swap="innerHTML"
508511
class="btn btn-sm"
509512
>
510513
{% heroicon_outline "user-minus" aria_hidden="true" size=14 class="stroke-base-content/80" %}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{% load i18n %}
2+
3+
<c-modal.confirm
4+
title="{% trans 'Anonymize applications' %}"
5+
action="{% url 'apply:submissions:bulk-anonymize' %}"
6+
initial_mode="anonymize"
7+
>
8+
<p class="text-sm text-fg-muted">
9+
{% blocktranslate trimmed with count=submission_ids|length %}
10+
Are you sure you want to anonymize {{ count }} submission(s)?
11+
Personal data will be removed and applications will only be accessible
12+
via statistics in the Results view. All draft submissions will be deleted.
13+
This action cannot be undone.
14+
{% endblocktranslate %}
15+
</p>
16+
{% for id in submission_ids %}
17+
<input type="hidden" name="selectedSubmissionIds" value="{{ id }}">
18+
{% endfor %}
19+
</c-modal.confirm>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{% load i18n heroicons %}
2+
3+
<form class="px-4 pt-5 pb-4 sm:p-6 form" action="{% url 'apply:submissions:bulk-archive' %}" method="post">
4+
{% csrf_token %}
5+
6+
<div class="sm:flex sm:items-start">
7+
<div class="flex justify-center items-center mx-auto w-12 h-12 rounded-full sm:mx-0 sm:w-10 sm:h-10 bg-warning/10 shrink-0">
8+
{% heroicon_outline "exclamation-triangle" class="w-6 h-6 text-warning" stroke_width="1.5" aria_hidden="true" %}
9+
</div>
10+
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
11+
<h3 class="text-base font-semibold leading-6 text-base-content" id="modal-title">{% trans "Archive submissions" %}</h3>
12+
<div class="mt-2">
13+
<p class="text-sm text-fg-muted">
14+
{% blocktranslate trimmed with count=submission_ids|length %}
15+
Are you sure you want to archive {{ count }} submission(s)?
16+
Archived submissions will be hidden from the main list and cannot be edited.
17+
Submissions can be unarchived at any time.
18+
{% endblocktranslate %}
19+
</p>
20+
</div>
21+
</div>
22+
</div>
23+
24+
{% for id in submission_ids %}
25+
<input type="hidden" name="selectedSubmissionIds" value="{{ id }}">
26+
{% endfor %}
27+
28+
<div class="mt-5 sm:flex-row-reverse sm:mt-4 card-actions">
29+
<button
30+
type="submit"
31+
class="w-full sm:w-auto btn btn-warning"
32+
>{% trans "Archive" %}</button>
33+
<button
34+
type="button"
35+
class="w-full sm:w-auto btn btn-secondary btn-outline btn-soft"
36+
@click="show = false"
37+
>{% trans "Cancel" %}</button>
38+
</div>
39+
</form>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{% load i18n %}
2+
3+
<c-modal.confirm
4+
title="{% trans 'Delete applications' %}"
5+
action="{% url 'apply:submissions:bulk-delete' %}"
6+
>
7+
<p class="text-sm text-fg-muted">
8+
{% blocktranslate trimmed with count=submission_ids|length %}
9+
Are you sure you want to permanently delete {{ count }} submission(s)?
10+
All data will be permanently removed. This action cannot be undone.
11+
{% endblocktranslate %}
12+
</p>
13+
{% for id in submission_ids %}
14+
<input type="hidden" name="selectedSubmissionIds" value="{{ id }}">
15+
{% endfor %}
16+
</c-modal.confirm>

hypha/apply/funds/urls.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111
)
1212
from .views.all import (
1313
bulk_anonymize_submissions,
14+
bulk_anonymize_submissions_confirm,
1415
bulk_archive_submissions,
16+
bulk_archive_submissions_confirm,
1517
bulk_delete_submissions,
18+
bulk_delete_submissions_confirm,
1619
bulk_update_submissions_status,
1720
submissions_all,
1821
)
@@ -98,8 +101,23 @@
98101
),
99102
path("success/<int:pk>/", submission_success, name="success"),
100103
path("all/", submissions_all, name="list"),
104+
path(
105+
"all/bulk_archive_confirm/",
106+
bulk_archive_submissions_confirm,
107+
name="bulk-archive-confirm",
108+
),
101109
path("all/bulk_archive/", bulk_archive_submissions, name="bulk-archive"),
110+
path(
111+
"all/bulk_delete_confirm/",
112+
bulk_delete_submissions_confirm,
113+
name="bulk-delete-confirm",
114+
),
102115
path("all/bulk_delete/", bulk_delete_submissions, name="bulk-delete"),
116+
path(
117+
"all/bulk_anonymize_confirm/",
118+
bulk_anonymize_submissions_confirm,
119+
name="bulk-anonymize-confirm",
120+
),
103121
path("all/bulk_anonymize/", bulk_anonymize_submissions, name="bulk-anonymize"),
104122
path(
105123
"all/bulk_update_status/",

hypha/apply/funds/views/all.py

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,9 @@
99
from django.core.exceptions import PermissionDenied
1010
from django.core.paginator import Paginator
1111
from django.db import models
12-
from django.http import (
13-
HttpRequest,
14-
HttpResponse,
15-
HttpResponseForbidden,
16-
)
17-
from django.shortcuts import render
18-
from django.urls import reverse_lazy
12+
from django.http import HttpRequest, HttpResponse, HttpResponseForbidden
13+
from django.shortcuts import redirect, render
14+
from django.urls import reverse
1915
from django.utils.translation import gettext as _
2016
from django.views.decorators.http import require_http_methods
2117
from django_htmx.http import HttpResponseClientRedirect, HttpResponseClientRefresh
@@ -343,6 +339,20 @@ def submissions_all(
343339
return render(request, template_name, ctx)
344340

345341

342+
@login_required
343+
@require_http_methods(["GET"])
344+
def bulk_archive_submissions_confirm(request):
345+
if not permissions.can_bulk_archive_submissions(request.user):
346+
return HttpResponseForbidden()
347+
348+
submission_ids = request.GET.getlist("selectedSubmissionIds")
349+
return render(
350+
request,
351+
"submissions/bulk_archive_confirm.html",
352+
{"submission_ids": submission_ids},
353+
)
354+
355+
346356
@login_required
347357
@require_http_methods(["POST"])
348358
def bulk_archive_submissions(request):
@@ -358,7 +368,23 @@ def bulk_archive_submissions(request):
358368
request=request,
359369
)
360370

361-
return HttpResponseClientRefresh()
371+
if request.htmx:
372+
return HttpResponseClientRefresh()
373+
return redirect(reverse("apply:submissions:list"))
374+
375+
376+
@login_required
377+
@require_http_methods(["GET"])
378+
def bulk_delete_submissions_confirm(request):
379+
if not permissions.can_bulk_delete_submissions(request.user):
380+
return HttpResponseForbidden()
381+
382+
submission_ids = request.GET.getlist("selectedSubmissionIds")
383+
return render(
384+
request,
385+
"submissions/bulk_delete_confirm.html",
386+
{"submission_ids": submission_ids},
387+
)
362388

363389

364390
@login_required
@@ -376,28 +402,47 @@ def bulk_delete_submissions(request):
376402
request=request,
377403
)
378404

379-
return HttpResponseClientRefresh()
405+
if request.htmx:
406+
return HttpResponseClientRefresh()
407+
return redirect(reverse("apply:submissions:list"))
408+
409+
410+
@login_required
411+
@require_http_methods(["GET"])
412+
def bulk_anonymize_submissions_confirm(request):
413+
if not settings.SUBMISSION_ANONYMIZATION_ENABLED:
414+
return HttpResponseForbidden()
415+
if not permissions.can_bulk_delete_submissions(request.user):
416+
return HttpResponseForbidden()
417+
418+
submission_ids = request.GET.getlist("selectedSubmissionIds")
419+
return render(
420+
request,
421+
"submissions/bulk_anonymize_confirm.html",
422+
{"submission_ids": submission_ids},
423+
)
380424

381425

382426
@login_required
383427
@require_http_methods(["POST"])
384428
def bulk_anonymize_submissions(request):
385-
if settings.SUBMISSION_ANONYMIZATION_ENABLED:
386-
if not permissions.can_bulk_delete_submissions(request.user):
387-
return HttpResponseForbidden()
429+
if not settings.SUBMISSION_ANONYMIZATION_ENABLED:
430+
return HttpResponseForbidden()
431+
if not permissions.can_bulk_delete_submissions(request.user):
432+
return HttpResponseForbidden()
388433

389-
submission_ids = request.POST.getlist("selectedSubmissionIds")
390-
submissions = ApplicationSubmission.objects.filter(id__in=submission_ids)
434+
submission_ids = request.POST.getlist("selectedSubmissionIds")
435+
submissions = ApplicationSubmission.objects.filter(id__in=submission_ids)
391436

392-
services.bulk_anonymize_submissions(
393-
submissions=submissions,
394-
user=request.user,
395-
request=request,
396-
)
437+
services.bulk_anonymize_submissions(
438+
submissions=submissions,
439+
user=request.user,
440+
request=request,
441+
)
397442

443+
if request.htmx:
398444
return HttpResponseClientRefresh()
399-
400-
return HttpResponseForbidden()
445+
return redirect(reverse("apply:submissions:list"))
401446

402447

403448
@login_required
@@ -441,7 +486,7 @@ def bulk_update_submissions_status(request: HttpRequest) -> HttpResponse:
441486
return HttpResponseClientRefresh()
442487
action = outcome_from_actions(transitions)
443488
return HttpResponseClientRedirect(
444-
reverse_lazy("apply:submissions:determinations:batch")
489+
reverse("apply:submissions:determinations:batch")
445490
+ "?action="
446491
+ action
447492
+ "&submissions="

hypha/templates/cotton/modal/confirm.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<form
44
class="px-4 pt-5 pb-4 sm:p-6"
5-
action="{{ request.path }}"
5+
action="{% if action %}{{ action }}{% else %}{{ request.path }}{% endif %}"
66
method="post"
77
x-data="{userConfirmationInput: '', delConfirmText: `{% trans 'delete' %}`, anonConfirmText: `{% trans 'anonymize' %}`, mode: '{{ initial_mode|default:'delete' }}'}"
88
>

hypha/templates/includes/_modal-placeholder.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<div
1010
x-cloak
1111
x-data="{ show: false }"
12+
x-init="$watch('show', v => { if (!v) $nextTick(() => { $refs.modalContent.innerHTML = '' }) })"
1213
@keydown.window.escape="show = false"
1314
x-show="show"
1415
class="relative z-10"
@@ -45,6 +46,7 @@
4546
x-description="Modal panel, show/hide based on modal state."
4647
class="relative w-full text-left rounded-lg border shadow-xl transition-all transform sm:my-8 sm:max-w-lg bg-base-100"
4748
id="htmx-modal"
49+
x-ref="modalContent"
4850
hx-target="this"
4951
@htmx:after-swap="show = true"
5052
@htmx:before-swap="if(!$event.detail.xhr.response) { show = false; $event.detail.shouldSwap = false;}"

0 commit comments

Comments
 (0)