From 4975475e855ca8597aeb10bd34eb7970d4466516 Mon Sep 17 00:00:00 2001 From: Sli Date: Tue, 10 Dec 2024 23:48:46 +0100 Subject: [PATCH] Add tooltip on current registered card, allow barmen to delete cards and make card deletion a fragment --- core/static/core/style.scss | 27 +++++++++++ .../fragments/create_student_card.jinja | 11 +++-- .../fragments/delete_student_card.jinja | 15 ++++++ counter/tests/test_customer.py | 46 +++++++++++-------- counter/views/student_card.py | 24 +++++++--- locale/fr/LC_MESSAGES/django.po | 16 +++++-- 6 files changed, 107 insertions(+), 32 deletions(-) create mode 100644 counter/templates/counter/fragments/delete_student_card.jinja diff --git a/core/static/core/style.scss b/core/static/core/style.scss index 50892df3..6973aa75 100644 --- a/core/static/core/style.scss +++ b/core/static/core/style.scss @@ -42,6 +42,29 @@ body { } } +[tooltip] { + position: relative; +} + +[tooltip]:before { + opacity: 0; + z-index: 1; + content: attr(tooltip); + background: $white-color; + color: $black-color; + border: 1px solid $black-color; + border-radius: 5px; + padding: 5px; + top: 1em; + position: absolute; + white-space: nowrap; + transition: opacity 500ms ease-out; +} + +[tooltip]:hover:before { + opacity: 1; +} + .ib { display: inline-block; padding: 1px; @@ -308,6 +331,7 @@ body { font-size: 120%; background-color: unset; position: relative; + &:after { content: ''; position: absolute; @@ -318,14 +342,17 @@ body { border-radius: 2px; transition: all 0.2s ease-in-out; } + &:hover:after { border-bottom-color: darken($primary-neutral-light-color, 20%); } + &.active:after { border-bottom-color: $primary-dark-color; } } } + section { padding: 20px; } diff --git a/counter/templates/counter/fragments/create_student_card.jinja b/counter/templates/counter/fragments/create_student_card.jinja index f8e59d24..d972ff33 100644 --- a/counter/templates/counter/fragments/create_student_card.jinja +++ b/counter/templates/counter/fragments/create_student_card.jinja @@ -13,10 +13,13 @@ {% trans %}No student card registered.{% endtrans %} {% else %}

- {% trans %}Registered{% endtrans %}   -   - - {% trans %}Delete{% endtrans %} - + {% trans %}Card registered{% endtrans %} +   -   +

{% endif %} diff --git a/counter/templates/counter/fragments/delete_student_card.jinja b/counter/templates/counter/fragments/delete_student_card.jinja new file mode 100644 index 00000000..94be1c47 --- /dev/null +++ b/counter/templates/counter/fragments/delete_student_card.jinja @@ -0,0 +1,15 @@ +
+
+ {% csrf_token %} +

{% trans obj=object %}Are you sure you want to delete "{{ obj }}"?{% endtrans %}

+ + +
+
\ No newline at end of file diff --git a/counter/tests/test_customer.py b/counter/tests/test_customer.py index b861a97e..3e745ecc 100644 --- a/counter/tests/test_customer.py +++ b/counter/tests/test_customer.py @@ -198,8 +198,7 @@ class TestStudentCard(TestCase): StudentCard, customer=cls.customer.customer, uid="8A89B82018B0A0" ) - def setUp(self): - # Auto login on counter + def login_in_counter(self): self.client.post( reverse("counter:login", args=[self.counter.id]), {"username": self.barmen.username, "password": "plop"}, @@ -222,6 +221,7 @@ class TestStudentCard(TestCase): ] def test_search_user_with_student_card(self): + self.login_in_counter() response = self.client.post( reverse("counter:details", args=[self.counter.id]), {"code": self.valid_card.uid}, @@ -233,6 +233,7 @@ class TestStudentCard(TestCase): ) def test_add_student_card_from_counter(self): + self.login_in_counter() for uid in ["8B90734A802A8F", "ABCAAAFAAFAAAB", "15248196326518"]: customer = subscriber_user.make().customer response = self.client.post( @@ -251,6 +252,7 @@ class TestStudentCard(TestCase): assert customer.student_card.uid == uid def test_add_student_card_from_counter_fail(self): + self.login_in_counter() customer = subscriber_user.make().customer for uid, error_msg in self.invalid_uids(): response = self.client.post( @@ -269,25 +271,15 @@ class TestStudentCard(TestCase): assert not hasattr(customer, "student_card") def test_add_student_card_from_counter_unauthorized(self): - barman = subscriber_user.make() - self.counter.sellers.add(barman) - customer = self.customer.customer - # There is someone logged to a counter - # with the client of this TestCase instance, - # so we create a new client, in order to check - # that using a client not logged to a counter - # where another client is logged still isn't authorized. - client = Client() - def send_valid_request(counter_id): - return client.post( + return self.client.post( reverse( - "counter:add_student_card", kwargs={"customer_id": customer.pk} + "counter:add_student_card", kwargs={"customer_id": self.customer.pk} ), {"uid": "8B90734A802A8F"}, HTTP_REFERER=reverse( "counter:click", - kwargs={"counter_id": counter_id, "user_id": customer.pk}, + kwargs={"counter_id": counter_id, "user_id": self.customer.pk}, ), ) @@ -295,7 +287,7 @@ class TestStudentCard(TestCase): assert send_valid_request(self.counter.id).status_code == 403 # Send to a non bar counter - client.force_login(self.club_admin) + self.client.force_login(self.club_admin) assert send_valid_request(self.club_counter.id).status_code == 403 def test_delete_student_card_with_owner(self): @@ -322,6 +314,24 @@ class TestStudentCard(TestCase): self.customer.customer.refresh_from_db() assert not hasattr(self.customer.customer, "student_card") + def test_delete_student_card_from_counter(self): + self.login_in_counter() + self.client.post( + reverse( + "counter:delete_student_card", + kwargs={"customer_id": self.customer.customer.pk}, + ), + http_referer=reverse( + "counter:click", + kwargs={ + "counter_id": self.counter.id, + "user_id": self.customer.customer.pk, + }, + ), + ) + self.customer.customer.refresh_from_db() + assert not hasattr(self.customer.customer, "student_card") + def test_delete_student_card_fail(self): """Test that non-admin users cannot delete student cards""" self.client.force_login(self.subscriber) @@ -336,7 +346,7 @@ class TestStudentCard(TestCase): assert not hasattr(self.subscriber.customer, "student_card") def test_add_student_card_from_user_preferences(self): - users = [self.subscriber, self.board_admin, self.root] + users = [self.customer, self.board_admin, self.root] uids = ["8B90734A802A8F", "ABCAAAFAAFAAAB", "15248196326518"] for user, uid in itertools.product(users, uids): self.customer.customer.student_card.delete() @@ -353,7 +363,7 @@ class TestStudentCard(TestCase): self.customer.customer.refresh_from_db() assert self.customer.customer.student_card.uid == uid - self.assertContains(response, text="Enregistré") + self.assertContains(response, text="Carte enregistrée") def test_add_student_card_from_user_preferences_fail(self): customer = subscriber_user.make() diff --git a/counter/views/student_card.py b/counter/views/student_card.py index 070e260d..35226e95 100644 --- a/counter/views/student_card.py +++ b/counter/views/student_card.py @@ -22,22 +22,32 @@ from django.utils.translation import gettext as _ from django.views.generic.edit import DeleteView, FormView from core.utils import FormFragmentTemplateData -from core.views import CanEditMixin +from core.views import can_edit from counter.forms import StudentCardForm from counter.models import Customer, StudentCard from counter.utils import is_logged_in_counter -class StudentCardDeleteView(DeleteView, CanEditMixin): - """View used to delete a card from a user.""" +class StudentCardDeleteView(DeleteView): + """View used to delete a card from a user. This is a fragment view !""" model = StudentCard - template_name = "core/delete_confirm.jinja" + template_name = "counter/fragments/delete_student_card.jinja" - def dispatch(self, request, *args, **kwargs): + def dispatch(self, request: HttpRequest, *args, **kwargs): self.customer = get_object_or_404(Customer, pk=kwargs["customer_id"]) + if not is_logged_in_counter(request) and not can_edit( + self.get_object(), request.user + ): + raise PermissionDenied() return super().dispatch(request, *args, **kwargs) + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["action"] = self.request.path + context["action_cancel"] = self.get_success_url() + return context + def get_object(self, queryset=None): if not hasattr(self.customer, "student_card"): raise Http404( @@ -47,7 +57,9 @@ class StudentCardDeleteView(DeleteView, CanEditMixin): return self.customer.student_card def get_success_url(self, **kwargs): - return reverse("core:user_prefs", kwargs={"user_id": self.customer.user_id}) + return reverse( + "counter:add_student_card", kwargs={"customer_id": self.customer.pk} + ) class StudentCardFormView(FormView): diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 1a2786bc..482befd3 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -369,7 +369,7 @@ msgstr "Compte en banque : " #: core/templates/core/user_clubs.jinja:34 #: core/templates/core/user_clubs.jinja:63 #: core/templates/core/user_edit.jinja:62 -#: counter/templates/counter/fragments/create_student_card.jinja:18 +#: counter/templates/counter/fragments/create_student_card.jinja:22 #: counter/templates/counter/last_ops.jinja:35 #: counter/templates/counter/last_ops.jinja:65 #: election/templates/election/election_detail.jinja:191 @@ -2574,18 +2574,21 @@ msgstr "Confirmation de suppression" #: core/templates/core/delete_confirm.jinja:16 #: core/templates/core/file_delete_confirm.jinja:29 +#: counter/templates/counter/fragments/delete_student_card.jinja:4 #, python-format msgid "Are you sure you want to delete \"%(obj)s\"?" msgstr "Êtes-vous sûr de vouloir supprimer \"%(obj)s\" ?" #: core/templates/core/delete_confirm.jinja:17 #: core/templates/core/file_delete_confirm.jinja:36 +#: counter/templates/counter/fragments/delete_student_card.jinja:5 msgid "Confirm" msgstr "Confirmation" #: core/templates/core/delete_confirm.jinja:20 #: core/templates/core/file_delete_confirm.jinja:46 #: counter/templates/counter/counter_click.jinja:104 +#: counter/templates/counter/fragments/delete_student_card.jinja:12 #: sas/templates/sas/ask_picture_removal.jinja:20 msgid "Cancel" msgstr "Annuler" @@ -4044,8 +4047,13 @@ msgid "No student card registered." msgstr "Aucune carte étudiante enregistrée." #: counter/templates/counter/fragments/create_student_card.jinja:16 -msgid "Registered" -msgstr "Enregistré" +msgid "Card registered" +msgstr "Carte enregistrée" + +#: counter/templates/counter/fragments/create_student_card.jinja:17 +#, python-format +msgid "uid: %(uid)s " +msgstr "uid: %(uid)s" #: counter/templates/counter/invoices_call.jinja:8 #, python-format @@ -4317,7 +4325,7 @@ msgstr "Administration des comptoirs" msgid "Product types" msgstr "Types de produit" -#: counter/views/student_card.py:44 +#: counter/views/student_card.py:54 #, python-format msgid "%(name)s has no registered student card" msgstr "%(name)s n'a pas de carte étudiante enregistrée"