diff --git a/evap/contributor/templates/contributor_evaluation_form.html b/evap/contributor/templates/contributor_evaluation_form.html index d2ab8c7e74..92a54fb5b7 100644 --- a/evap/contributor/templates/contributor_evaluation_form.html +++ b/evap/contributor/templates/contributor_evaluation_form.html @@ -69,15 +69,19 @@
{% translate 'Course data' %}
{% translate 'Evaluation data' %}
{% if evaluation.allow_editors_to_edit %}
- + {% blocktranslate trimmed asvar title with evaluation_name=evaluation.full_name %} + Request account creation for {{ evaluation_name }}{% endblocktranslate %} + {% translate 'Please tell us which new account we should create. We need the name and email for all new accounts.' as teaser %} + {% translate 'Request creation of new account' as show_button_text %} + {% include 'contact_modal.html' with form_id="create-participant-account-form" modal_id='createParticipantAccountRequestModal' form_id='create-participant-account-form' user=request.user title=title teaser=teaser show_button_text=show_button_text show_button_class="btn btn-sm btn-light mb-3" %}
{% else %}
- + {% blocktranslate asvar title with evaluation_name=evaluation.full_name %}Request evaluation + changes for {{ evaluation_name }}{% endblocktranslate %} + {% translate 'Please tell us what changes to the evaluation we should make.' as teaser %} + {% translate 'Request changes' as show_button_text %} + {% include 'contact_modal.html' with form_id="change-evaluation-form" modal_id='changeEvaluationRequestModal' form_id='change-evaluation-form' user=request.user title=title teaser=teaser show_button_text=show_button_text show_button_class="btn btn-sm btn-light" %}
{% endif %} @@ -150,22 +154,15 @@ {% endif %} -{% endblock %} - -{% block modals %} - {{ block.super }} - - {% blocktranslate trimmed asvar title with evaluation_name=evaluation.full_name %} - Request account creation for {{ evaluation_name }} - {% endblocktranslate %} - {% translate 'Please tell us which new account we should create. We need the name and email for all new accounts.' as teaser %} - {% include 'contact_modal.html' with modal_id='createAccountRequestModal' user=request.user title=title teaser=teaser %} - - {% blocktranslate trimmed asvar title with evaluation_name=evaluation.full_name %} - Request evaluation changes for {{ evaluation_name }} - {% endblocktranslate %} - {% translate 'Please tell us what changes to the evaluation we should make.' as teaser %} - {% include 'contact_modal.html' with modal_id='changeEvaluationRequestModal' user=request.user title=title teaser=teaser %} +
+ {% csrf_token %} +
+
+ {% csrf_token %} +
+
+ {% csrf_token %} +
{% endblock %} {% block additional_javascript %} diff --git a/evap/contributor/tests/test_views.py b/evap/contributor/tests/test_views.py index 4db2451b10..5fba70e06f 100644 --- a/evap/contributor/tests/test_views.py +++ b/evap/contributor/tests/test_views.py @@ -243,10 +243,11 @@ def test_contact_modal_escape(self): Regression test for #1060 """ self.evaluation.name_en = "Adam & Eve" + self.evaluation.allow_editors_to_edit = False self.evaluation.save() page = self.app.get(self.url, user=self.responsible, status=200) - self.assertIn("changeEvaluationRequestModalLabel", page) + self.assertIn("changeEvaluationRequestModalSubject", page) self.assertNotIn("Adam & Eve", page) self.assertIn("Adam & Eve", page) diff --git a/evap/evaluation/templates/base.html b/evap/evaluation/templates/base.html index 0738d38c09..4d653a725b 100644 --- a/evap/evaluation/templates/base.html +++ b/evap/evaluation/templates/base.html @@ -29,11 +29,6 @@ {% include "custom_elements.html" %} {% block modals %} - {% if user.is_authenticated %} - {% translate 'Feedback' as title %} - {% translate 'You are welcome to submit feedback regarding the evaluation platform or specific evaluations. Please let us know how we can improve your experience on EvaP.' as teaser %} - {% include 'contact_modal.html' with modal_id='feedbackModal' user=request.user title=title teaser=teaser %} - {% endif %} {% endblock %}
diff --git a/evap/evaluation/templates/contact_modal.html b/evap/evaluation/templates/contact_modal.html index 4333d30273..579ec85184 100644 --- a/evap/evaluation/templates/contact_modal.html +++ b/evap/evaluation/templates/contact_modal.html @@ -1,112 +1,81 @@ {% load static %} - + + {{ title }} + {{ teaser }} + {% translate 'Send Message' %} - diff --git a/evap/evaluation/templates/profile.html b/evap/evaluation/templates/profile.html index be4111ce62..a59967aa91 100644 --- a/evap/evaluation/templates/profile.html +++ b/evap/evaluation/templates/profile.html @@ -73,27 +73,22 @@

{% translate 'Delegates and emails' %}

{% endif %} + -
-
-
-
- - -
-
+
+
+
+ +
+ {% csrf_token %} +
+ {% translate 'Request user setting changes' as title %} + {% translate 'Please tell us which of your settings we should change.' as teaser %} + {% translate "Request changes" as show_button_text %} + {% include 'contact_modal.html' with form_id="request-changes-form" modal_id='requestChangesModal' user=request.user title=title teaser=teaser show_button_text=show_button_text show_button_class="btn btn-light" %}
- -{% endblock %} - -{% block modals %} - {{ block.super }} - {% translate 'Request user setting changes' as title %} - {% translate 'Please tell us which of your settings we should change.' as teaser %} - {% include 'contact_modal.html' with modal_id='changeRequestModal' user=request.user title=title teaser=teaser %} +
{% endblock %} diff --git a/evap/evaluation/tests/test_live.py b/evap/evaluation/tests/test_live.py index e789091268..c2d416d685 100644 --- a/evap/evaluation/tests/test_live.py +++ b/evap/evaluation/tests/test_live.py @@ -8,14 +8,15 @@ class ContactModalTests(LiveServerTest): def test_contact_modal(self) -> None: self.selenium.get(self.reverse("evaluation:index")) + self.wait_until_document_ready() self.selenium.find_element(By.ID, "feedbackModalShowButton").click() - self.wait.until(visibility_of_element_located((By.ID, "feedbackModalMessageText"))) - self.selenium.find_element(By.ID, "feedbackModalMessageText").send_keys("Test message") - self.selenium.find_element(By.ID, "feedbackModalActionButton").click() + text_area = self.wait.until(visibility_of_element_located((By.ID, "feedbackModalMessageText"))) + text_area.send_keys("Test message") + self.selenium.find_element(By.CSS_SELECTOR, "confirmation-modal > span[slot='action-text']").click() self.wait.until( text_to_be_present_in_element( - (By.CSS_SELECTOR, "#successMessageModal_feedbackModal .modal-body"), + (By.CSS_SELECTOR, ".alert-success"), "Your message was successfully sent.", ) ) diff --git a/evap/evaluation/tests/tools.py b/evap/evaluation/tests/tools.py index 5fd6de2e57..65c497ad2f 100644 --- a/evap/evaluation/tests/tools.py +++ b/evap/evaluation/tests/tools.py @@ -396,6 +396,10 @@ def wait_until_page_reloads(self): yield self.wait.until(staleness_of(html_element)) + # wait for all javascript to fully execute, so our click handlers are registered + def wait_until_document_ready(self): + self.wait.until(lambda driver: driver.execute_script("return document.readyState") == "complete") + @classmethod def setUpClass(cls) -> None: super().setUpClass() diff --git a/evap/evaluation/views.py b/evap/evaluation/views.py index 7dce34bee0..b1fb8de93a 100644 --- a/evap/evaluation/views.py +++ b/evap/evaluation/views.py @@ -186,6 +186,7 @@ def contact(request: HttpRequest) -> HttpResponse: try: mail.send() logger.info("Sent contact email: \n%s\n", mail.message()) + messages.success(request, _("Your message was successfully sent.")) return HttpResponse() except Exception: logger.exception("An exception occurred when sending the following contact email:\n%s\n", mail.message()) diff --git a/evap/static/scss/components/_buttons.scss b/evap/static/scss/components/_buttons.scss index 7016d8e245..70a7bd4429 100644 --- a/evap/static/scss/components/_buttons.scss +++ b/evap/static/scss/components/_buttons.scss @@ -143,21 +143,22 @@ a:not([href]):not(.disabled) { } } -.feedback-button { - @include media-breakpoint-up(md) { - position: fixed; - right: 10px; - bottom: 8px; - z-index: $zindex-fixed; - } - - .btn-dark { - padding: 0 0.2rem; - color: $light-gray; - } -} .feedback-button-placeholder { min-width: 140px; + + [slot=show-button] { + @include media-breakpoint-up(md) { + position: fixed; + right: 10px; + bottom: 8px; + z-index: $zindex-fixed; + } + + > button { + padding: 0 0.2rem; + color: $light-gray; + } + } } @media screen and (min-aspect-ratio: $notebook-break) { diff --git a/evap/static/ts/src/confirmation-modal.ts b/evap/static/ts/src/confirmation-modal.ts index 9f5eb1af29..dabe99f306 100644 --- a/evap/static/ts/src/confirmation-modal.ts +++ b/evap/static/ts/src/confirmation-modal.ts @@ -24,6 +24,11 @@ export class ConfirmationModal extends HTMLElement { const confirmButtonExtraClass = this.getAttribute("confirm-button-class") ?? "btn-primary"; confirmButton.className += " " + confirmButtonExtraClass; + const dialogExtraClass = this.getAttribute("dialog-extra-class"); + if (dialogExtraClass !== null) { + this.dialog.className += " " + dialogExtraClass; + } + const showButton = selectOrError("[slot=show-button]", this); showButton.addEventListener("click", event => { event.stopPropagation(); diff --git a/evap/static/ts/src/contact_modal.ts b/evap/static/ts/src/contact_modal.ts deleted file mode 100644 index 287d372459..0000000000 --- a/evap/static/ts/src/contact_modal.ts +++ /dev/null @@ -1,69 +0,0 @@ -declare const bootstrap: typeof import("bootstrap"); - -import { selectOrError, sleep, assert } from "./utils.js"; -import { CSRF_HEADERS } from "./csrf-utils.js"; - -const SUCCESS_MESSAGE_TIMEOUT = 3000; - -export class ContactModalLogic { - private readonly modal: bootstrap.Modal; - private readonly successMessageModal: bootstrap.Modal; - private readonly actionButtonElement: HTMLButtonElement; - private readonly messageTextElement: HTMLInputElement; - private readonly showButtonElements: HTMLElement[]; - private readonly title: string; - - // may be null if anonymous feedback is not enabled - private readonly anonymousRadioElement: HTMLInputElement | null; - - constructor(modalId: string, title: string) { - this.title = title; - this.modal = new bootstrap.Modal(selectOrError("#" + modalId)); - this.successMessageModal = new bootstrap.Modal(selectOrError("#successMessageModal_" + modalId)); - this.actionButtonElement = selectOrError("#" + modalId + "ActionButton"); - this.messageTextElement = selectOrError("#" + modalId + "MessageText"); - this.anonymousRadioElement = document.querySelector("#" + modalId + "AnonymousName"); - this.showButtonElements = Array.from(document.querySelectorAll(`#${modalId}ShowButton, .${modalId}ShowButton`)); - } - - public attach = (): void => { - this.actionButtonElement.addEventListener("click", async event => { - this.actionButtonElement.disabled = true; - event.preventDefault(); - const message = this.messageTextElement.value; - if (message.trim() === "") { - this.modal.hide(); - this.actionButtonElement.disabled = false; - return; - } - try { - const response = await fetch("/contact", { - body: new URLSearchParams({ - anonymous: String(this.anonymousRadioElement?.checked), - message, - title: this.title, - }), - headers: CSRF_HEADERS, - method: "POST", - }); - assert(response.ok); - } catch (_) { - window.alert("Sending failed, sorry!"); - return; - } - this.modal.hide(); - this.successMessageModal.show(); - this.messageTextElement.value = ""; - - await sleep(SUCCESS_MESSAGE_TIMEOUT); - this.successMessageModal.hide(); - this.actionButtonElement.disabled = false; - }); - - this.showButtonElements.forEach(button => - button.addEventListener("click", () => { - this.modal.show(); - }), - ); - }; -} diff --git a/evap/student/tests/test_live.py b/evap/student/tests/test_live.py index d9de0e77ae..86b8dbbe18 100644 --- a/evap/student/tests/test_live.py +++ b/evap/student/tests/test_live.py @@ -136,8 +136,7 @@ def test_resolving_submit_errors_clears_warning(self) -> None: with self.wait_until_page_reloads(): self.selenium.find_element(By.ID, "vote-submit-btn").click() - # wait for all javascript to fully execute, so our click handlers are registered - self.wait.until(lambda driver: driver.execute_script("return document.readyState") == "complete") + self.wait_until_document_ready() row = self.selenium.find_element(By.CSS_SELECTOR, "#student-vote-form .row:has(.btn-check)") error_marked = row.find_elements(By.CSS_SELECTOR, ".choice-error")