mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-10-31 00:53:08 +00:00 
			
		
		
		
	Add tooltip on current registered card, allow barmen to delete cards and make card deletion a fragment
This commit is contained in:
		| @@ -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 { | .ib { | ||||||
|   display: inline-block; |   display: inline-block; | ||||||
|   padding: 1px; |   padding: 1px; | ||||||
| @@ -308,6 +331,7 @@ body { | |||||||
|         font-size: 120%; |         font-size: 120%; | ||||||
|         background-color: unset; |         background-color: unset; | ||||||
|         position: relative; |         position: relative; | ||||||
|  |  | ||||||
|         &:after { |         &:after { | ||||||
|           content: ''; |           content: ''; | ||||||
|           position: absolute; |           position: absolute; | ||||||
| @@ -318,14 +342,17 @@ body { | |||||||
|           border-radius: 2px; |           border-radius: 2px; | ||||||
|           transition: all 0.2s ease-in-out; |           transition: all 0.2s ease-in-out; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         &:hover:after { |         &:hover:after { | ||||||
|           border-bottom-color: darken($primary-neutral-light-color, 20%); |           border-bottom-color: darken($primary-neutral-light-color, 20%); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         &.active:after { |         &.active:after { | ||||||
|           border-bottom-color: $primary-dark-color; |           border-bottom-color: $primary-dark-color; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     section { |     section { | ||||||
|       padding: 20px; |       padding: 20px; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -13,10 +13,13 @@ | |||||||
|     <em class="no-cards">{% trans %}No student card registered.{% endtrans %}</em> |     <em class="no-cards">{% trans %}No student card registered.{% endtrans %}</em> | ||||||
|   {% else %} |   {% else %} | ||||||
|     <p> |     <p> | ||||||
|       {% trans %}Registered{% endtrans %} <i class="fa fa-check"></i>   -   |       {% trans %}Card registered{% endtrans %}</span> | ||||||
|       <a href="{{ url('counter:delete_student_card', customer_id=customer.pk) }}"> |     <span tooltip="{% trans uid=customer.student_card.uid %}uid: {{ uid }} {% endtrans %}"><i class="fa fa-check" style="color: green"></i></span>   -   | ||||||
|         {% trans %}Delete{% endtrans %} |     <button | ||||||
|       </a> |       hx-get="{{ url('counter:delete_student_card', customer_id=customer.pk) }}" | ||||||
|  |       hx-swap="outerHTML" | ||||||
|  |       hx-target="#student_card_form" | ||||||
|  |     >{% trans %}Delete{% endtrans %}</button> | ||||||
|     </p> |     </p> | ||||||
|   {% endif %} |   {% endif %} | ||||||
| </div> | </div> | ||||||
|   | |||||||
| @@ -0,0 +1,15 @@ | |||||||
|  | <div id="student_card_form"> | ||||||
|  |   <form hx-post="{{ action }}" hx-swap="outerHTML" hx-target="#student_card_form"> | ||||||
|  |     {% csrf_token %} | ||||||
|  |     <p>{% trans obj=object %}Are you sure you want to delete "{{ obj }}"?{% endtrans %}</p> | ||||||
|  |     <input type="submit" value="{% trans %}Confirm{% endtrans %}" /> | ||||||
|  |     <input | ||||||
|  |       hx-get="{{ action_cancel }}" | ||||||
|  |       hx-swap="outerHTML" | ||||||
|  |       hx-target="#student_card_form" | ||||||
|  |       type="submit" | ||||||
|  |       name="cancel" | ||||||
|  |       value="{% trans %}Cancel{% endtrans %}" | ||||||
|  |     /> | ||||||
|  |   </form> | ||||||
|  | </div> | ||||||
| @@ -198,8 +198,7 @@ class TestStudentCard(TestCase): | |||||||
|             StudentCard, customer=cls.customer.customer, uid="8A89B82018B0A0" |             StudentCard, customer=cls.customer.customer, uid="8A89B82018B0A0" | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def setUp(self): |     def login_in_counter(self): | ||||||
|         # Auto login on counter |  | ||||||
|         self.client.post( |         self.client.post( | ||||||
|             reverse("counter:login", args=[self.counter.id]), |             reverse("counter:login", args=[self.counter.id]), | ||||||
|             {"username": self.barmen.username, "password": "plop"}, |             {"username": self.barmen.username, "password": "plop"}, | ||||||
| @@ -222,6 +221,7 @@ class TestStudentCard(TestCase): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|     def test_search_user_with_student_card(self): |     def test_search_user_with_student_card(self): | ||||||
|  |         self.login_in_counter() | ||||||
|         response = self.client.post( |         response = self.client.post( | ||||||
|             reverse("counter:details", args=[self.counter.id]), |             reverse("counter:details", args=[self.counter.id]), | ||||||
|             {"code": self.valid_card.uid}, |             {"code": self.valid_card.uid}, | ||||||
| @@ -233,6 +233,7 @@ class TestStudentCard(TestCase): | |||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def test_add_student_card_from_counter(self): |     def test_add_student_card_from_counter(self): | ||||||
|  |         self.login_in_counter() | ||||||
|         for uid in ["8B90734A802A8F", "ABCAAAFAAFAAAB", "15248196326518"]: |         for uid in ["8B90734A802A8F", "ABCAAAFAAFAAAB", "15248196326518"]: | ||||||
|             customer = subscriber_user.make().customer |             customer = subscriber_user.make().customer | ||||||
|             response = self.client.post( |             response = self.client.post( | ||||||
| @@ -251,6 +252,7 @@ class TestStudentCard(TestCase): | |||||||
|             assert customer.student_card.uid == uid |             assert customer.student_card.uid == uid | ||||||
|  |  | ||||||
|     def test_add_student_card_from_counter_fail(self): |     def test_add_student_card_from_counter_fail(self): | ||||||
|  |         self.login_in_counter() | ||||||
|         customer = subscriber_user.make().customer |         customer = subscriber_user.make().customer | ||||||
|         for uid, error_msg in self.invalid_uids(): |         for uid, error_msg in self.invalid_uids(): | ||||||
|             response = self.client.post( |             response = self.client.post( | ||||||
| @@ -269,25 +271,15 @@ class TestStudentCard(TestCase): | |||||||
|             assert not hasattr(customer, "student_card") |             assert not hasattr(customer, "student_card") | ||||||
|  |  | ||||||
|     def test_add_student_card_from_counter_unauthorized(self): |     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): |         def send_valid_request(counter_id): | ||||||
|             return client.post( |             return self.client.post( | ||||||
|                 reverse( |                 reverse( | ||||||
|                     "counter:add_student_card", kwargs={"customer_id": customer.pk} |                     "counter:add_student_card", kwargs={"customer_id": self.customer.pk} | ||||||
|                 ), |                 ), | ||||||
|                 {"uid": "8B90734A802A8F"}, |                 {"uid": "8B90734A802A8F"}, | ||||||
|                 HTTP_REFERER=reverse( |                 HTTP_REFERER=reverse( | ||||||
|                     "counter:click", |                     "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 |         assert send_valid_request(self.counter.id).status_code == 403 | ||||||
|  |  | ||||||
|         # Send to a non bar counter |         # 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 |         assert send_valid_request(self.club_counter.id).status_code == 403 | ||||||
|  |  | ||||||
|     def test_delete_student_card_with_owner(self): |     def test_delete_student_card_with_owner(self): | ||||||
| @@ -322,6 +314,24 @@ class TestStudentCard(TestCase): | |||||||
|             self.customer.customer.refresh_from_db() |             self.customer.customer.refresh_from_db() | ||||||
|             assert not hasattr(self.customer.customer, "student_card") |             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): |     def test_delete_student_card_fail(self): | ||||||
|         """Test that non-admin users cannot delete student cards""" |         """Test that non-admin users cannot delete student cards""" | ||||||
|         self.client.force_login(self.subscriber) |         self.client.force_login(self.subscriber) | ||||||
| @@ -336,7 +346,7 @@ class TestStudentCard(TestCase): | |||||||
|         assert not hasattr(self.subscriber.customer, "student_card") |         assert not hasattr(self.subscriber.customer, "student_card") | ||||||
|  |  | ||||||
|     def test_add_student_card_from_user_preferences(self): |     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"] |         uids = ["8B90734A802A8F", "ABCAAAFAAFAAAB", "15248196326518"] | ||||||
|         for user, uid in itertools.product(users, uids): |         for user, uid in itertools.product(users, uids): | ||||||
|             self.customer.customer.student_card.delete() |             self.customer.customer.student_card.delete() | ||||||
| @@ -353,7 +363,7 @@ class TestStudentCard(TestCase): | |||||||
|  |  | ||||||
|             self.customer.customer.refresh_from_db() |             self.customer.customer.refresh_from_db() | ||||||
|             assert self.customer.customer.student_card.uid == uid |             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): |     def test_add_student_card_from_user_preferences_fail(self): | ||||||
|         customer = subscriber_user.make() |         customer = subscriber_user.make() | ||||||
|   | |||||||
| @@ -22,22 +22,32 @@ from django.utils.translation import gettext as _ | |||||||
| from django.views.generic.edit import DeleteView, FormView | from django.views.generic.edit import DeleteView, FormView | ||||||
|  |  | ||||||
| from core.utils import FormFragmentTemplateData | from core.utils import FormFragmentTemplateData | ||||||
| from core.views import CanEditMixin | from core.views import can_edit | ||||||
| from counter.forms import StudentCardForm | from counter.forms import StudentCardForm | ||||||
| from counter.models import Customer, StudentCard | from counter.models import Customer, StudentCard | ||||||
| from counter.utils import is_logged_in_counter | from counter.utils import is_logged_in_counter | ||||||
|  |  | ||||||
|  |  | ||||||
| class StudentCardDeleteView(DeleteView, CanEditMixin): | class StudentCardDeleteView(DeleteView): | ||||||
|     """View used to delete a card from a user.""" |     """View used to delete a card from a user. This is a fragment view !""" | ||||||
|  |  | ||||||
|     model = StudentCard |     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"]) |         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) |         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): |     def get_object(self, queryset=None): | ||||||
|         if not hasattr(self.customer, "student_card"): |         if not hasattr(self.customer, "student_card"): | ||||||
|             raise Http404( |             raise Http404( | ||||||
| @@ -47,7 +57,9 @@ class StudentCardDeleteView(DeleteView, CanEditMixin): | |||||||
|         return self.customer.student_card |         return self.customer.student_card | ||||||
|  |  | ||||||
|     def get_success_url(self, **kwargs): |     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): | class StudentCardFormView(FormView): | ||||||
|   | |||||||
| @@ -369,7 +369,7 @@ msgstr "Compte en banque : " | |||||||
| #: core/templates/core/user_clubs.jinja:34 | #: core/templates/core/user_clubs.jinja:34 | ||||||
| #: core/templates/core/user_clubs.jinja:63 | #: core/templates/core/user_clubs.jinja:63 | ||||||
| #: core/templates/core/user_edit.jinja:62 | #: 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:35 | ||||||
| #: counter/templates/counter/last_ops.jinja:65 | #: counter/templates/counter/last_ops.jinja:65 | ||||||
| #: election/templates/election/election_detail.jinja:191 | #: 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/delete_confirm.jinja:16 | ||||||
| #: core/templates/core/file_delete_confirm.jinja:29 | #: core/templates/core/file_delete_confirm.jinja:29 | ||||||
|  | #: counter/templates/counter/fragments/delete_student_card.jinja:4 | ||||||
| #, python-format | #, python-format | ||||||
| msgid "Are you sure you want to delete \"%(obj)s\"?" | msgid "Are you sure you want to delete \"%(obj)s\"?" | ||||||
| msgstr "Êtes-vous sûr de vouloir supprimer \"%(obj)s\" ?" | msgstr "Êtes-vous sûr de vouloir supprimer \"%(obj)s\" ?" | ||||||
|  |  | ||||||
| #: core/templates/core/delete_confirm.jinja:17 | #: core/templates/core/delete_confirm.jinja:17 | ||||||
| #: core/templates/core/file_delete_confirm.jinja:36 | #: core/templates/core/file_delete_confirm.jinja:36 | ||||||
|  | #: counter/templates/counter/fragments/delete_student_card.jinja:5 | ||||||
| msgid "Confirm" | msgid "Confirm" | ||||||
| msgstr "Confirmation" | msgstr "Confirmation" | ||||||
|  |  | ||||||
| #: core/templates/core/delete_confirm.jinja:20 | #: core/templates/core/delete_confirm.jinja:20 | ||||||
| #: core/templates/core/file_delete_confirm.jinja:46 | #: core/templates/core/file_delete_confirm.jinja:46 | ||||||
| #: counter/templates/counter/counter_click.jinja:104 | #: counter/templates/counter/counter_click.jinja:104 | ||||||
|  | #: counter/templates/counter/fragments/delete_student_card.jinja:12 | ||||||
| #: sas/templates/sas/ask_picture_removal.jinja:20 | #: sas/templates/sas/ask_picture_removal.jinja:20 | ||||||
| msgid "Cancel" | msgid "Cancel" | ||||||
| msgstr "Annuler" | msgstr "Annuler" | ||||||
| @@ -4044,8 +4047,13 @@ msgid "No student card registered." | |||||||
| msgstr "Aucune carte étudiante enregistrée." | msgstr "Aucune carte étudiante enregistrée." | ||||||
|  |  | ||||||
| #: counter/templates/counter/fragments/create_student_card.jinja:16 | #: counter/templates/counter/fragments/create_student_card.jinja:16 | ||||||
| msgid "Registered" | msgid "Card registered" | ||||||
| msgstr "Enregistré" | 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 | #: counter/templates/counter/invoices_call.jinja:8 | ||||||
| #, python-format | #, python-format | ||||||
| @@ -4317,7 +4325,7 @@ msgstr "Administration des comptoirs" | |||||||
| msgid "Product types" | msgid "Product types" | ||||||
| msgstr "Types de produit" | msgstr "Types de produit" | ||||||
|  |  | ||||||
| #: counter/views/student_card.py:44 | #: counter/views/student_card.py:54 | ||||||
| #, python-format | #, python-format | ||||||
| msgid "%(name)s has no registered student card" | msgid "%(name)s has no registered student card" | ||||||
| msgstr "%(name)s n'a pas de carte étudiante enregistrée" | msgstr "%(name)s n'a pas de carte étudiante enregistrée" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user