Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite confirmation modals #1985

Merged
merged 75 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from 74 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
2e985ee
Vision
niklasmohrin Jul 17, 2023
37e8a9d
grade semester view
niklasmohrin Nov 20, 2023
fb7c93a
semester import
niklasmohrin Nov 20, 2023
9c188d5
semester preparation reminder
niklasmohrin Nov 20, 2023
407a55c
format
niklasmohrin Nov 20, 2023
4c1d3c6
delete semesters
niklasmohrin Nov 20, 2023
0218b57
add `submit_with_modal`
niklasmohrin Nov 20, 2023
64650cc
another test
niklasmohrin Nov 20, 2023
0cea998
another test
niklasmohrin Nov 20, 2023
b1c9c6b
another test
niklasmohrin Nov 20, 2023
b9e2f2b
another test
niklasmohrin Nov 20, 2023
615c825
formatting, linting
niklasmohrin Nov 20, 2023
e379b5d
User delete modal
niklasmohrin Dec 11, 2023
b436595
User resend email modal
niklasmohrin Dec 11, 2023
f706eab
reward event deletion
niklasmohrin Dec 11, 2023
c782a22
remove empty block
niklasmohrin Dec 11, 2023
914b79e
indent loop
niklasmohrin Dec 11, 2023
d4d5921
questionnaire deletion modal
niklasmohrin Dec 11, 2023
e025f68
pull form out like in the other view
niklasmohrin Dec 11, 2023
3090a92
import participants modal
niklasmohrin Dec 11, 2023
1b454e3
import replace participants modal
niklasmohrin Dec 11, 2023
c6dc60f
remove duplicated button in user import
niklasmohrin Dec 11, 2023
a9b1633
fix tests
niklasmohrin Dec 11, 2023
e020774
copy participants modal
niklasmohrin Dec 18, 2023
aa46f6d
copy replace participants
niklasmohrin Dec 18, 2023
a58fd38
import contributors modal
niklasmohrin Dec 18, 2023
03d5866
import replace contributors modal
niklasmohrin Dec 18, 2023
fd1717b
copy contributors modal
niklasmohrin Dec 18, 2023
9b2a1e9
copy replace contributor modal
niklasmohrin Dec 18, 2023
2cf2f51
make semester active modal
niklasmohrin Dec 18, 2023
f5ff584
archive participations modal
niklasmohrin Dec 18, 2023
639db54
fix tooltip (never shown on disabled elements)
niklasmohrin Dec 18, 2023
3846286
delete grade documents modal
niklasmohrin Dec 18, 2023
8b37a93
fix typo
niklasmohrin Dec 18, 2023
5005dab
archive results modal
niklasmohrin Dec 18, 2023
3bc5aba
evaluation deletion modal
niklasmohrin Dec 18, 2023
1684c0f
course deletion modal
niklasmohrin Dec 18, 2023
a2926dd
remove double semicolon
niklasmohrin Dec 18, 2023
e41de6f
`rm evap/evaluation/templates/confirmation_modal.html`
niklasmohrin Dec 18, 2023
f56d999
fix tests
niklasmohrin Dec 18, 2023
42f0d41
fix remaining todos
niklasmohrin Jan 8, 2024
51620dc
Remove double empty line
niklasmohrin Jan 8, 2024
6c4e820
Clean up scss a bit
niklasmohrin Jan 8, 2024
5ae441d
fix a button group
niklasmohrin Jan 8, 2024
6463449
Use bootstrap z-index variables
niklasmohrin Jan 8, 2024
e226f4b
Feedback grades course view modal
niklasmohrin Jan 22, 2024
3f7c632
Fix copy paste error
niklasmohrin Jan 22, 2024
43acd2c
Replace `defer` with `async`
niklasmohrin Jan 22, 2024
84f7bdb
Introduce custom success forms
niklasmohrin Jan 22, 2024
cc3f4b5
remove todo, it already submits
niklasmohrin Jan 22, 2024
ce5fc90
fix evaluation deletion submit behavior
niklasmohrin Jan 22, 2024
86d4240
dont emit `confirmed` event if `type=submit`
niklasmohrin Jan 22, 2024
c2c69e7
Use `reload-on-success` for `set_no_grades`
niklasmohrin Jan 22, 2024
d9b37f6
Restore `staff/views.py` to before this PR
niklasmohrin Jan 22, 2024
cfe31fc
Use `reload-on-success` for the staff views
niklasmohrin Jan 22, 2024
be31c45
Use `custom-success` for grade document deletion
niklasmohrin Jan 22, 2024
b1141c0
Undo unneeded changes in `staff/tests/test_views.py`
niklasmohrin Jan 22, 2024
4ab3ad8
Replace `async` with `type="module"`
niklasmohrin Jan 29, 2024
2818ad6
Center the modal
niklasmohrin Jan 29, 2024
5588fa5
autofocus cancel button in submit row
niklasmohrin Feb 5, 2024
33c7e3c
remove margin in modal header
niklasmohrin Feb 5, 2024
13e274e
Apply suggestions from code review
niklasmohrin Feb 5, 2024
7731b48
use `blocktrans trimmed` everywhere
niklasmohrin Feb 5, 2024
1e454bb
Fix semester archival buttons
niklasmohrin Feb 12, 2024
e052f16
Fix semester header margin
niklasmohrin Feb 12, 2024
eb0cce5
Sync modal disabled state with show button for style reasons
niklasmohrin Feb 12, 2024
00f5940
make semester name confirmation box in semester deletion modal look n…
niklasmohrin Feb 12, 2024
a6bdd98
Fix bug in chrome where clicking with an open modal triggers a click …
niklasmohrin Feb 12, 2024
ad3663e
Use `form=` instead of wrapping table in form for grade document dele…
niklasmohrin Feb 22, 2024
c057e8d
Fix leftover async script tags
niklasmohrin Feb 22, 2024
7804728
Remove dot
niklasmohrin Feb 25, 2024
1ad8c7e
`git checkout main -- evap/contributor/templates/contributor_index.html`
niklasmohrin Feb 25, 2024
216472f
Fixes for the old modal
niklasmohrin Feb 25, 2024
4dbbaec
`./manage.py format`
niklasmohrin Feb 25, 2024
d1bcd08
Merge branch 'main' into rewrite-modals
niklasmohrin Feb 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 12 additions & 19 deletions evap/contributor/templates/contributor_evaluation_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,18 @@ <h5 class="card-title me-auto">{% trans 'Evaluation data' %}</h5>
{% if editable %}
<button name="operation" value="preview" type="submit" class="btn btn-light">{% trans 'Preview' %}</button>
<button name="operation" value="save" type="submit" class="btn btn-primary">{% trans 'Save' %}</button>
{# webtest does not allow submission with value "approve" if no such button exists #}
<button style="display: none" name="operation" value="approve" type="submit"></button>
<button type="button" onclick="approveEvaluationModalShow(0, '');" class="btn btn-success">{% trans 'Save and approve' %}</button>

<confirmation-modal type="submit" name="operation" value="approve">
<span slot="title">{% trans 'Approve evaluation' %}</span>
<span slot="action-text">{% trans 'Approve evaluation' %}</span>
<span slot="question">
{% blocktrans trimmed %}
Do you want to approve this evaluation? This will allow the evaluation team to proceed with the preparation, but you won't be able to make any further changes.
{% endblocktrans %}
</span>

<button slot="show-button" type="button" class="btn btn-success">{% trans 'Save and approve' %}</button>
</confirmation-modal>
{% endif %}
<a href="{% url 'contributor:index' %}" class="btn btn-light">{% if edit %}{% trans 'Cancel' %}{% else %}{% trans 'Back' %}{% endif %}</a>
</div>
Expand Down Expand Up @@ -121,22 +130,6 @@ <h5 class="modal-title" id="previewModalLabel">{% trans 'Preview' %}</h5>

{% block modals %}
{{ block.super }}
{% trans 'Approve evaluation' as title %}
{% blocktrans asvar question%}Do you want to approve this evaluation? This will allow the evaluation team to proceed with the preparation, but you won't be able to make any further changes.{% endblocktrans %}
{% trans 'Approve evaluation' as action_text %}
{% include 'confirmation_modal.html' with modal_id='approveEvaluationModal' title=title question=question action_text=action_text btn_type='primary' %}
<script type="text/javascript">
function approveEvaluationModalAction(dataId) {
const input = document.createElement("input");
input.type = "hidden";
input.name = "operation";
input.value = "approve";

const form = document.getElementById("evaluation-form");
form.appendChild(input);
form.requestSubmit();
};
</script>

{% blocktrans asvar title with evaluation_name=evaluation.full_name %}Request account creation for {{ evaluation_name }}{% endblocktrans %}
{% trans 'Please tell us which new account we should create. We need the name and email for all new accounts.' as teaser %}
Expand Down
37 changes: 21 additions & 16 deletions evap/contributor/templates/contributor_index.html
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,13 @@
<span class="fas fa-pencil"></span>
</a>
{% if not evaluation|has_nonresponsible_editor %}
<a href="#" class="btn btn-sm btn-dark" data-bs-toggle="tooltip"
<button class="btn btn-sm btn-dark" data-bs-toggle="tooltip"
data-bs-placement="top" title="{% trans 'Delegate preparation' %}"
onclick="delegateSelectionModalShow(`{{ evaluation.full_name }}`, `{% url 'contributor:evaluation_direct_delegation' evaluation.id %}`);return false;"
data-evaluation-name="{{ evaluation.full_name }}"
data-delegation-url="{% url 'contributor:evaluation_direct_delegation' evaluation.id %}"
>
<span class="fas fa-hand-point-left"></span>
</a>
</button>
{% endif %}
{% elif evaluation.state == evaluation.State.EDITOR_APPROVED or evaluation.state == evaluation.State.APPROVED %}
<a href="{% url 'contributor:evaluation_view' evaluation.id %}" class="btn btn-sm btn-light"
Expand Down Expand Up @@ -226,22 +227,26 @@ <h5 class="modal-title" id="{{ modal_id }}Label">{% trans 'Delegate preparation'
</div>
</div>

<script type="text/javascript">
function {{ modal_id }}Show(evaluationName, action) {
const modal = document.getElementById("{{ modal_id }}");
// set form's action location
modal.querySelectorAll("form").forEach(form => form.action = action);
<script type="module">
document.querySelectorAll("[data-evaluation-name][data-delegation-url]").forEach(showButton => {
showButton.addEventListener("click", event => {
event.stopPropagation();

// put the correct evaluation name in the modal
modal.querySelectorAll('[data-label=""]').forEach(el => el.innerText = evaluationName);
const modal = document.getElementById("{{ modal_id }}");
// set form's action location
modal.querySelectorAll("form").forEach(form => form.action = showButton.dataset.delegationUrl);

// unselect any previously selected options in the modal
modal.querySelectorAll("select").forEach(select => select.tomselect.clear());
// put the correct evaluation name in the modal
modal.querySelectorAll('[data-label=""]').forEach(el => el.innerText = showButton.dataset.evaluationName);

// show modal
var {{ modal_id }} = new bootstrap.Modal(document.getElementById('{{ modal_id }}'));
{{ modal_id }}.show();
}
// unselect any previously selected options in the modal
modal.querySelectorAll("select").forEach(select => select.tomselect.clear());

// show modal
var {{ modal_id }} = new bootstrap.Modal(document.getElementById('{{ modal_id }}'));
{{ modal_id }}.show();
});
});
</script>
{% endwith %}
{% endblock %}
9 changes: 7 additions & 2 deletions evap/contributor/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
from model_bakery import baker

from evap.evaluation.models import Contribution, Course, Evaluation, Questionnaire, UserProfile
from evap.evaluation.tests.tools import WebTestWith200Check, create_evaluation_with_responsible_and_editor, render_pages
from evap.evaluation.tests.tools import (
WebTestWith200Check,
create_evaluation_with_responsible_and_editor,
render_pages,
submit_with_modal,
)


class TestContributorDirectDelegationView(WebTest):
Expand Down Expand Up @@ -197,7 +202,7 @@ def test_contributor_evaluation_edit(self):
self.evaluation = Evaluation.objects.get(pk=self.evaluation.pk)
self.assertEqual(self.evaluation.state, Evaluation.State.PREPARED)

form.submit(name="operation", value="approve")
submit_with_modal(page, form, name="operation", value="approve")
self.evaluation = Evaluation.objects.get(pk=self.evaluation.pk)
self.assertEqual(self.evaluation.state, Evaluation.State.EDITOR_APPROVED)

Expand Down
8 changes: 8 additions & 0 deletions evap/evaluation/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
<body>
<script type="text/javascript" src="{% static 'bootstrap/dist/js/bootstrap.bundle.min.js' %}"></script>

{% include "custom_elements.html" %}

{% block modals %}
{% if user.is_authenticated %}
{% trans 'Feedback' as title %}
Expand Down Expand Up @@ -199,6 +201,12 @@

</script>

<script type="module">
import { setupForms } from "{% static 'js/custom-success-form.js' %}";

setupForms();
</script>

{% block additional_javascript %}{% endblock %}
</body>
</html>
33 changes: 0 additions & 33 deletions evap/evaluation/templates/confirmation_modal.html

This file was deleted.

33 changes: 33 additions & 0 deletions evap/evaluation/templates/confirmation_modal_template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{% load static %}

<template id="confirmation-modal-template">
<link rel="stylesheet" href="{% static 'css/evap.css' %}" />

<dialog class="evap-modal-dialog">
<form method="dialog">
<div class="evap-modal-container">
<header>
<h5 class="mb-0"><slot name="title"></slot></h5>
<button class="btn-close"></button>
</header>
<section class="question-area">
<slot name="question"></slot>
<slot name="extra-inputs"></slot>
</section>
<section class="button-area">
<button class="btn btn-light" autofocus>{% trans 'Cancel' %}</button>
<slot name="submit-group">
<button class="btn ms-2" data-event-type="confirm">
<slot name="action-text"></slot>
</button>
</slot>
</section>
</div>
</form>
</dialog>

<slot name="show-button"></slot>

{# All children without the "slot" attribute go into this unnamed slot #}
<slot></slot>
</template>
9 changes: 9 additions & 0 deletions evap/evaluation/templates/custom_elements.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{% load static %}

{% include "confirmation_modal_template.html" %}

<script type="module">
import { ConfirmationModal } from "{% static 'js/confirmation-modal.js' %}";

customElements.define("confirmation-modal", ConfirmationModal);
</script>
6 changes: 3 additions & 3 deletions evap/evaluation/tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from model_bakery import baker

from evap.evaluation.models import CourseType, Degree, Semester, UserProfile
from evap.evaluation.tests.tools import make_manager
from evap.evaluation.tests.tools import make_manager, submit_with_modal
from evap.staff.tests.utils import WebTestStaffMode


Expand All @@ -36,7 +36,7 @@ def test_sample_semester_file(self):
form = page.forms["semester-import-form"]
form["vote_start_datetime"] = "2015-01-01 11:11:11"
form["vote_end_date"] = "2099-01-01"
form.submit(name="operation", value="import")
submit_with_modal(page, form, name="operation", value="import")

self.assertEqual(UserProfile.objects.count(), original_user_count + 4)

Expand All @@ -50,7 +50,7 @@ def test_sample_user_file(self):
page = form.submit(name="operation", value="test")

form = page.forms["user-import-form"]
form.submit(name="operation", value="import")
submit_with_modal(page, form, name="operation", value="import")

self.assertEqual(UserProfile.objects.count(), original_user_count + 2)

Expand Down
9 changes: 9 additions & 0 deletions evap/evaluation/tests/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from contextlib import contextmanager
from datetime import timedelta

import webtest
from django.conf import settings
from django.contrib.auth.models import Group
from django.db import DEFAULT_DB_ALIAS, connections
Expand Down Expand Up @@ -125,6 +126,14 @@ def test_check_response_code_200(self):
self.app.get(self.url, user=user, status=200)


def submit_with_modal(page: webtest.TestResponse, form: webtest.Form, *, name: str, value: str) -> webtest.TestResponse:
# Like form.submit, but looks for a modal instead of a submit button.
assert page.forms[form.id] == form
assert page.html.select_one(f"confirmation-modal[type=submit][name={name}][value={value}]")
params = form.submit_fields() + [(name, value)]
return form.response.goto(form.action, method=form.method, params=params)


def get_form_data_from_instance(form_cls, instance, **kwargs):
assert form_cls._meta.model == type(instance)
form = form_cls(instance=instance, **kwargs)
Expand Down
48 changes: 24 additions & 24 deletions evap/grades/templates/grades_course_view.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ <h3 class="mb-3">{{ course.name }} ({{ semester.name }})</h3>
</div>
<div class="card-body">
{% if grade_documents %}
<form id="grade-document-deletion-form" custom-success method="POST" action="{% url 'grades:delete_grades' %}">
niklasmohrin marked this conversation as resolved.
Show resolved Hide resolved
{% csrf_token %}
</form>

<table class="table table-striped table-vertically-aligned">
<thead>
<tr>
Expand All @@ -22,23 +26,39 @@ <h3 class="mb-3">{{ course.name }} ({{ semester.name }})</h3>
</thead>
<tbody>
{% for grade_document in grade_documents %}
<tr id="grade_document-row-{{ grade_document.id }}">
<tr id="grade-document-row-{{ grade_document.id }}">
<td>{{ grade_document.description }}</td>
<td>{{ grade_document.get_type_display }}</td>
<td>{{ grade_document.last_modified_time }}, {% trans 'by' %} {{ grade_document.last_modified_user }}</td>
<td>
<a href="{% url 'grades:download_grades' grade_document.id %}" class="btn btn-sm btn-light" data-bs-toggle="tooltip" data-bs-placement="top" title="{% trans 'Download' %}"><span class="fas fa-download"></span></a>
{% if user.is_grade_publisher %}
<a href="{% url 'grades:edit_grades' grade_document.id %}" class="btn btn-sm btn-light" data-bs-toggle="tooltip" data-bs-placement="top" title="{% trans 'Edit' %}"><span class="fas fa-pencil"></span></a>
<button type="button" onclick="deleteGradedocumentModalShow({{ grade_document.id }}, '{{ grade_document.description|escapejs }}');" class="btn btn-sm btn-danger" data-bs-toggle="tooltip" data-bs-placement="top" title="{% trans 'Delete' %}">
<span class="fas fa-trash"></span>
</button>
<confirmation-modal type="submit" form="grade-document-deletion-form" name="grade_document_id" value="{{ grade_document.id }}" confirm-button-class="btn-danger">
<span slot="title">{% trans 'Delete grade document' %}</span>
<span slot="action-text">{% trans 'Delete grade document' %}</span>
<span slot="question">
{% blocktrans trimmed with description=grade_document.description %}
Do you really want to delete the grade document <strong>{{ description }}</strong>?
{% endblocktrans %}
</span>

<button slot="show-button" type="button" class="btn btn-sm btn-danger" data-bs-toggle="tooltip" data-bs-placement="top" title="{% trans 'Delete' %}">
<span class="fas fa-trash"></span>
</button>
</confirmation-modal>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>

<script type="module">
document.getElementById("grade-document-deletion-form").addEventListener("submit-success", event => {
fadeOutThenRemove(document.getElementById(`grade-document-row-${event.detail.body.get("grade_document_id")}`));
});
</script>
{% else %}
<span class="fst-italic">{% trans 'No grade documents have been uploaded yet' %}</span>
{% endif %}
Expand All @@ -49,23 +69,3 @@ <h3 class="mb-3">{{ course.name }} ({{ semester.name }})</h3>
<a href="{% url 'grades:upload_grades' course.id %}?final=true" class="btn btn-dark">{% trans 'Upload new final grades' %}</a>
{% endif %}
{% endblock %}

{% block modals %}
{{ block.super }}
{% trans 'Delete grade document' as title %}
{% trans 'Do you really want to delete the grade document <strong data-label=""></strong>?' as question %}
{% trans 'Delete grade document' as action_text %}
{% include 'confirmation_modal.html' with modal_id='deleteGradedocumentModal' title=title question=question action_text=action_text btn_type='danger' %}
<script type="text/javascript">
function deleteGradedocumentModalAction(dataId) {
fetch("{% url 'grades:delete_grades' %}", {
body: new URLSearchParams({grade_document_id: dataId}),
headers: CSRF_HEADERS,
method: "POST",
}).then(response => {
assert(response.ok);
fadeOutThenRemove(document.getElementById('grade_document-row-'+dataId));
}).catch(error => {window.alert("{% trans 'The server is not responding.' %}");});
};
</script>
{% endblock %}
Loading
Loading