diff --git a/server/vb/templates/check.dhtml b/server/vb/templates/check.dhtml index a20c9be..79124e6 100644 --- a/server/vb/templates/check.dhtml +++ b/server/vb/templates/check.dhtml @@ -2,158 +2,160 @@ {% load static %} {% block title %} -Voter Bowl x {{ school.short_name }} + Voter Bowl x {{ school.short_name }} {% endblock title %} {% block head_extras %} -<script src="{% static 'js/fireworks.js' %}"></script> -<script src="https://cdn.voteamerica.com/embed/tools.js" async></script> + <script src="{% static 'js/fireworks.js' %}"></script> + <script src="https://cdn.voteamerica.com/embed/tools.js" async></script> {% endblock head_extras %} {% block body %} -<style> - me { - background-color: white; - } -</style> -<div> <style> me { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - } - - me main { - width: 100%; - text-align: center; - padding: 2rem 0; - } - - me main img { - height: 150px; - margin: 1.5rem 0; + background-color: white; } + </style> + <div> + <style> + me { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + } - me main p { - font-weight: 378; - font-size: 20px; - line-height: 130%; - } + me main { + width: 100%; + text-align: center; + padding: 2rem 0; + } - me main h2 { - font-weight: 500; - font-size: 36px; - line-height: 120%; - text-transform: uppercase; - } + me main img { + height: 150px; + margin: 1.5rem 0; + } - me .faq { - width: 100%; - color: white; - padding: 2rem 0; - } + me main p { + font-weight: 378; + font-size: 20px; + line-height: 130%; + } - me .button-holder { - display: flex; - justify-content: center; - margin: 1.5rem 0; - } + me main h2 { + font-weight: 500; + font-size: 36px; + line-height: 120%; + text-transform: uppercase; + } - me .form { - width: 100%; - background-color: white; - padding: 2rem 0; - } - </style> - <main data-bg-color="{{ school.logo.bg_color }}" data-color="{{ school.logo.bg_text_color }}"> - <style> - me { - --main-color: transparent; - --main-bg-color: transparent; - position: relative; - color: var(--main-color); - background-color: var(--main-bg-color); + me .faq { + width: 100%; + color: white; + padding: 2rem 0; } - me .urgency { + me .button-holder { display: flex; justify-content: center; - align-items: center; + margin: 1.5rem 0; } - me .fireworks { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - overflow: hidden; + me .form { + width: 100%; + background-color: white; + padding: 2rem 0; } </style> - <script> - (function (self) { - onloadAdd(() => { - self.style.setProperty('--main-color', self.dataset.color); - self.style.setProperty('--main-bg-color', self.dataset.bgColor); - }); - })(me()); + <main data-bg-color="{{ school.logo.bg_color }}" + data-color="{{ school.logo.bg_text_color }}"> + <style> + me { + --main-color: transparent; + --main-bg-color: transparent; + position: relative; + color: var(--main-color); + background-color: var(--main-bg-color); + } - (function (self) { - /** - * Finalize a verify and, possibly, mint a new gift card if all is well. - * - * @param {string} firstName - * @param {string} lastName - * @param {string} email - */ - const finishVerify = (firstName, lastName, email) => { - htmx.ajax("POST", "./finish/", { - target: self.querySelector(".urgency"), - values: { - first_name: firstName, - last_name: lastName, - email: email - } - }); - }; + me .urgency { + display: flex; + justify-content: center; + align-items: center; + } - window.addEventListener('VoteAmericaEvent', (event) => { - const { - data - } = event.detail; - if (data?.tool === "verify" && data?.event === "action-finish") { - setTimeout(() => { - // scroll entire window back to top, smoothly - window.scrollTo({ - top: 0, - behavior: 'smooth' + me .fireworks { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + overflow: hidden; + } + </style> + <script> + (function(self) { + onloadAdd(() => { + self.style.setProperty('--main-color', self.dataset.color); + self.style.setProperty('--main-bg-color', self.dataset.bgColor); }); - const fireworks = new Fireworks.default(self.querySelector('.fireworks')); - fireworks.start(); - finishVerify(data.first_name, data.last_name, data.email); - setTimeout(() => fireworks.stop(), 10_000); - }, 500); - } - }); - })(me()); - </script> - <div class="container"> - <div class="urgency"> - <img src="{{ school.logo.url }}" alt="{{ school.short_name }} {{ school.mascot }} logo" /> - {% if current_contest %} - {% include "components/countdown.dhtml" with contest=current_contest %} - {% endif %} + })(me()); + + (function(self) { + /** + * Finalize a verify and, possibly, mint a new gift card if all is well. + * + * @param {string} firstName + * @param {string} lastName + * @param {string} email + */ + const finishVerify = (firstName, lastName, email) => { + htmx.ajax("POST", "./finish/", { + target: self.querySelector(".urgency"), + values: { + first_name: firstName, + last_name: lastName, + email: email + } + }); + }; + + window.addEventListener('VoteAmericaEvent', (event) => { + const { + data + } = event.detail; + if (data?.tool === "verify" && data?.event === "action-finish") { + setTimeout(() => { + // scroll entire window back to top, smoothly + window.scrollTo({ + top: 0, + behavior: 'smooth' + }); + finishVerify(data.first_name, data.last_name, data.email); + }, 500); + } + }); + })(me()); + </script> + <div class="container"> + <div class="urgency"> + <img src="{{ school.logo.url }}" + alt="{{ school.short_name }} {{ school.mascot }} logo" /> + {% if current_contest %} + {% include "components/countdown.dhtml" with contest=current_contest %} + {% endif %} + </div> + </div> + <div class="fireworks"></div> + </main> + <div class="form"> + <div class="container"> + <!-- TODO replace data-subscriber --> + <div class="voteamerica-embed" + data-subscriber="voterbowl" + data-tool="verify" + data-edition="college"></div> </div> - </div> - <div class="fireworks"></div> - </main> - <div class="form"> - <div class="container"> - <!-- TODO replace data-subscriber --> - <div class="voteamerica-embed" data-subscriber="voterbowl" data-tool="verify" data-edition="college"></div> </div> </div> -</div> -{% endblock body %} \ No newline at end of file +{% endblock body %} diff --git a/server/vb/templates/fail_check.dhtml b/server/vb/templates/fail_check.dhtml new file mode 100644 index 0000000..7f02c30 --- /dev/null +++ b/server/vb/templates/fail_check.dhtml @@ -0,0 +1,28 @@ +<img src="{{ school.logo.url }}" alt="{{ school.short_name }} {{ school.mascot }} logo" /> +<p> + <script> + (function (self) { + const schoolName = "{{ school.short_name }}"; + const firstName = "{{ first_name }}"; + const lastName = "{{ last_name }}"; + let email = null; + let count = 0; // give up after 3 tries + while (email === null && count < 3) { + email = prompt("Sorry, but we need your {{ school.short_name }} student email to continue. Please enter it below:"); + count++; + } + if (email) { + htmx.ajax("POST", "./finish/", { + target: document.querySelector(".urgency"), + values: { + email: email, + first_name: firstName, + last_name: lastName, + school: schoolName + } + }); + } + })(me()); + </script> + <b>We could not use your email</b>. Please use your {{ school.short_name }} student email. +</p> \ No newline at end of file diff --git a/server/vb/templates/finish_check.dhtml b/server/vb/templates/finish_check.dhtml index 816386c..2e4eb5c 100644 --- a/server/vb/templates/finish_check.dhtml +++ b/server/vb/templates/finish_check.dhtml @@ -1,5 +1,12 @@ <img src="{{ school.logo.url }}" alt="{{ school.short_name }} {{ school.mascot }} logo" /> <p> + <script> + (function(self) { + const fireworks = new Fireworks.default(document.querySelector('.fireworks')); + fireworks.start(); + setTimeout(() => fireworks.stop(), 10_000); + })(me()); + </script> <b>Please check your email</b>. We've sent you a link to claim your ${{ current_contest.amount }} gift card. </p> diff --git a/server/vb/views.py b/server/vb/views.py index 1a304c9..1a8a530 100644 --- a/server/vb/views.py +++ b/server/vb/views.py @@ -103,7 +103,21 @@ def finish_check(request: HttpRequest, slug: str) -> HttpResponse: raise ValueError("No active contest TODO") form = FinishCheckForm(request.POST, school=school) if not form.is_valid(): - raise PermissionDenied("Invalid form") + # Check if `email` is the only field that failed. + if "email" in form.errors and len(form.errors) == 1: + return render( + request, + "fail_check.dhtml", + { + "school": school, + "first_name": form.cleaned_data["first_name"], + "last_name": form.cleaned_data["last_name"], + "current_contest": current_contest, + }, + ) + + # Nope, it wasn't just the email field. Fail hard for now. + raise PermissionDenied("Invalid") email = form.cleaned_data["email"] # Create a new student if necessary.