Pre-fetch forms for student card

This commit is contained in:
Antoine Bartuccio 2024-12-08 00:32:28 +01:00
parent 2f613607af
commit 66d2dc74e7
6 changed files with 391 additions and 369 deletions

View File

@ -35,20 +35,20 @@
{% endif %} {% endif %}
{% if profile.customer %} {% if student_card %}
<h3>{% trans %}Student cards{% endtrans %}</h3> {% with
form=student_card.form,
action=student_card.context.action,
customer=student_card.context.customer,
student_cards=student_card.context.student_cards
%}
{% include student_card.template %}
{% endwith %}
<p class="justify"> <p class="justify">
{% trans %}You can add a card by asking at a counter or add it yourself here. If you want to manually {% trans %}You can add a card by asking at a counter or add it yourself here. If you want to manually
add a student card yourself, you'll need a NFC reader. We store the UID of the card which is 14 characters long.{% endtrans %} add a student card yourself, you'll need a NFC reader. We store the UID of the card which is 14 characters long.{% endtrans %}
</p> </p>
<div {% endif %}
hx-get="{{ url('counter:add_student_card', customer_id=profile.customer.pk) }}" </div>
hx-trigger="load"
hx-swap="outerHTML"
>
<div aria-busy="true" style="min-height: 100px;"></div>
</div>
{% endif %}
</div>
{% endblock %} {% endblock %}

View File

@ -71,6 +71,7 @@ from core.views.forms import (
UserProfileForm, UserProfileForm,
) )
from counter.models import Refilling, Selling from counter.models import Refilling, Selling
from counter.views.student_card import StudentCardFormView
from eboutic.models import Invoice from eboutic.models import Invoice
from subscription.models import Subscription from subscription.models import Subscription
from trombi.views import UserTrombiForm from trombi.views import UserTrombiForm
@ -575,6 +576,10 @@ class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
hasattr(self.object, "trombi_user") and self.request.user.trombi_user.trombi hasattr(self.object, "trombi_user") and self.request.user.trombi_user.trombi
): ):
kwargs["trombi_form"] = UserTrombiForm() kwargs["trombi_form"] = UserTrombiForm()
if hasattr(self.object, "customer"):
kwargs["student_card"] = StudentCardFormView.get_template_data(
self.request, self.object.customer
).as_dict()
return kwargs return kwargs

View File

@ -31,130 +31,131 @@
<p>{% trans %}Amount: {% endtrans %}{{ customer.amount }} €</p> <p>{% trans %}Amount: {% endtrans %}{{ customer.amount }} €</p>
{% if counter.type == 'BAR' %} {% if counter.type == 'BAR' %}
<div {% with
hx-get="{{ url('counter:add_student_card', customer_id=customer.pk) }}" form=student_card.form,
hx-trigger="load" action=student_card.context.action,
hx-swap="outerHTML" customer=student_card.context.customer,
> student_cards=student_card.context.student_cards
<div aria-busy="true" style="min-height: 100px;"></div> %}
</div> {% include student_card.template %}
{% endif %} {% endwith %}
</div> {% endif %}
</div>
<div id="click_form"> <div id="click_form">
<h5>{% trans %}Selling{% endtrans %}</h5> <h5>{% trans %}Selling{% endtrans %}</h5>
<div> <div>
{% set counter_click_url = url('counter:click', counter_id=counter.id, user_id=customer.user_id) %} {% set counter_click_url = url('counter:click', counter_id=counter.id, user_id=customer.user_id) %}
{# Formulaire pour rechercher un produit en tapant son code dans une barre de recherche #} {# Formulaire pour rechercher un produit en tapant son code dans une barre de recherche #}
<form method="post" action="" <form method="post" action=""
class="code_form" @submit.prevent="handleCode"> class="code_form" @submit.prevent="handleCode">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="action" value="code"> <input type="hidden" name="action" value="code">
<label for="code_field"></label> <label for="code_field"></label>
<input type="text" name="code" value="" class="focus" id="code_field"/> <input type="text" name="code" value="" class="focus" id="code_field"/>
<input type="submit" value="{% trans %}Go{% endtrans %}"/> <input type="submit" value="{% trans %}Go{% endtrans %}"/>
</form> </form>
<template x-for="error in errors"> <template x-for="error in errors">
<div class="alert alert-red" x-text="error"> <div class="alert alert-red" x-text="error">
</div>
</template>
<p>{% trans %}Basket: {% endtrans %}</p>
<ul>
<template x-for="[id, item] in Object.entries(basket)" :key="id">
<div>
<form method="post" action="" class="inline del_product_form"
@submit.prevent="handleAction">
{% csrf_token %}
<input type="hidden" name="action" value="del_product">
<input type="hidden" name="product_id" :value="id">
<input type="submit" value="-"/>
</form>
<span x-text="item['qty'] + item['bonus_qty']"></span>
<form method="post" action="" class="inline add_product_form"
@submit.prevent="handleAction">
{% csrf_token %}
<input type="hidden" name="action" value="add_product">
<input type="hidden" name="product_id" :value="id">
<input type="submit" value="+">
</form>
<span x-text="products[id].name"></span> :
<span x-text="(item['qty'] * item['price'] / 100)
.toLocaleString(undefined, { minimumFractionDigits: 2 })">
</span> €
<template x-if="item['bonus_qty'] > 0">P</template>
</div>
</template>
</ul>
<p>
<strong>Total: </strong>
<strong x-text="sumBasket().toLocaleString(undefined, { minimumFractionDigits: 2 })"></strong>
<strong> €</strong>
</p>
<form method="post"
action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}">
{% csrf_token %}
<input type="hidden" name="action" value="finish">
<input type="submit" value="{% trans %}Finish{% endtrans %}"/>
</form>
<form method="post"
action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}">
{% csrf_token %}
<input type="hidden" name="action" value="cancel">
<input type="submit" value="{% trans %}Cancel{% endtrans %}"/>
</form>
</div> </div>
{% if (counter.type == 'BAR' and barmens_can_refill) %} </template>
<h5>{% trans %}Refilling{% endtrans %}</h5> <p>{% trans %}Basket: {% endtrans %}</p>
<div>
<form method="post"
action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}">
{% csrf_token %}
{{ refill_form.as_p() }}
<input type="hidden" name="action" value="refill">
<input type="submit" value="{% trans %}Go{% endtrans %}"/>
</form>
</div>
{% endif %}
</div>
<div id="products"> <ul>
<ul> <template x-for="[id, item] in Object.entries(basket)" :key="id">
{% for category in categories.keys() -%} <div>
<li><a href="#cat_{{ category|slugify }}">{{ category }}</a></li> <form method="post" action="" class="inline del_product_form"
{%- endfor %} @submit.prevent="handleAction">
</ul> {% csrf_token %}
{% for category in categories.keys() -%} <input type="hidden" name="action" value="del_product">
<div id="cat_{{ category|slugify }}"> <input type="hidden" name="product_id" :value="id">
<h5>{{ category }}</h5> <input type="submit" value="-"/>
{% for p in categories[category] -%} </form>
<form method="post"
action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}" <span x-text="item['qty'] + item['bonus_qty']"></span>
class="form_button add_product_form" @submit.prevent="handleAction">
{% csrf_token %} <form method="post" action="" class="inline add_product_form"
<input type="hidden" name="action" value="add_product"> @submit.prevent="handleAction">
<input type="hidden" name="product_id" value="{{ p.id }}"> {% csrf_token %}
<button type="submit"> <input type="hidden" name="action" value="add_product">
<strong>{{ p.name }}</strong> <input type="hidden" name="product_id" :value="id">
{% if p.icon %} <input type="submit" value="+">
<img src="{{ p.icon.url }}" alt="image de {{ p.name }}"/> </form>
{% else %}
<img src="{{ static('core/img/na.gif') }}" alt="image de {{ p.name }}"/> <span x-text="products[id].name"></span> :
{% endif %} <span x-text="(item['qty'] * item['price'] / 100)
<span>{{ p.price }} €<br>{{ p.code }}</span> .toLocaleString(undefined, { minimumFractionDigits: 2 })">
</button> </span> €
</form> <template x-if="item['bonus_qty'] > 0">P</template>
{%- endfor %}
</div> </div>
</template>
</ul>
<p>
<strong>Total: </strong>
<strong x-text="sumBasket().toLocaleString(undefined, { minimumFractionDigits: 2 })"></strong>
<strong> €</strong>
</p>
<form method="post"
action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}">
{% csrf_token %}
<input type="hidden" name="action" value="finish">
<input type="submit" value="{% trans %}Finish{% endtrans %}"/>
</form>
<form method="post"
action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}">
{% csrf_token %}
<input type="hidden" name="action" value="cancel">
<input type="submit" value="{% trans %}Cancel{% endtrans %}"/>
</form>
</div>
{% if (counter.type == 'BAR' and barmens_can_refill) %}
<h5>{% trans %}Refilling{% endtrans %}</h5>
<div>
<form method="post"
action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}">
{% csrf_token %}
{{ refill_form.as_p() }}
<input type="hidden" name="action" value="refill">
<input type="submit" value="{% trans %}Go{% endtrans %}"/>
</form>
</div>
{% endif %}
</div>
<div id="products">
<ul>
{% for category in categories.keys() -%}
<li><a href="#cat_{{ category|slugify }}">{{ category }}</a></li>
{%- endfor %}
</ul>
{% for category in categories.keys() -%}
<div id="cat_{{ category|slugify }}">
<h5>{{ category }}</h5>
{% for p in categories[category] -%}
<form method="post"
action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}"
class="form_button add_product_form" @submit.prevent="handleAction">
{% csrf_token %}
<input type="hidden" name="action" value="add_product">
<input type="hidden" name="product_id" value="{{ p.id }}">
<button type="submit">
<strong>{{ p.name }}</strong>
{% if p.icon %}
<img src="{{ p.icon.url }}" alt="image de {{ p.name }}"/>
{% else %}
<img src="{{ static('core/img/na.gif') }}" alt="image de {{ p.name }}"/>
{% endif %}
<span>{{ p.price }} €<br>{{ p.code }}</span>
</button>
</form>
{%- endfor %} {%- endfor %}
</div> </div>
</div> {%- endfor %}
</div>
</div>
{% endblock content %} {% endblock content %}
{% block script %} {% block script %}

View File

@ -30,6 +30,7 @@ from core.views import CanViewMixin
from counter.forms import RefillForm from counter.forms import RefillForm
from counter.models import Counter, Customer, Product, Selling from counter.models import Counter, Customer, Product, Selling
from counter.views.mixins import CounterTabsMixin from counter.views.mixins import CounterTabsMixin
from counter.views.student_card import StudentCardFormView
if TYPE_CHECKING: if TYPE_CHECKING:
from core.models import User from core.models import User
@ -414,4 +415,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
kwargs["basket_total"] = self.sum_basket(self.request) kwargs["basket_total"] = self.sum_basket(self.request)
kwargs["refill_form"] = self.refill_form or RefillForm() kwargs["refill_form"] = self.refill_form or RefillForm()
kwargs["barmens_can_refill"] = self.object.can_refill() kwargs["barmens_can_refill"] = self.object.can_refill()
kwargs["student_card"] = StudentCardFormView.get_template_data(
self.request, self.customer
).as_dict()
return kwargs return kwargs

View File

@ -14,6 +14,9 @@
# #
from dataclasses import asdict, dataclass
from typing import Any
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.http import HttpRequest from django.http import HttpRequest
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
@ -26,6 +29,16 @@ from counter.models import Customer, StudentCard
from counter.utils import is_logged_in_counter from counter.utils import is_logged_in_counter
@dataclass
class StudentCardTemplateData:
form: StudentCardForm
template: str
context: dict[str, Any]
def as_dict(self) -> dict[str, Any]:
return asdict(self)
class StudentCardDeleteView(DeleteView, CanEditMixin): class StudentCardDeleteView(DeleteView, CanEditMixin):
"""View used to delete a card from a user.""" """View used to delete a card from a user."""
@ -49,6 +62,23 @@ class StudentCardFormView(FormView):
form_class = StudentCardForm form_class = StudentCardForm
template_name = "counter/fragments/create_student_card.jinja" template_name = "counter/fragments/create_student_card.jinja"
@classmethod
def get_template_data(
cls, request: HttpRequest, customer: Customer
) -> StudentCardTemplateData:
"""Get necessary data to pre-render the fragment"""
return StudentCardTemplateData(
form=cls.form_class(),
template=cls.template_name,
context={
"action": reverse_lazy(
"counter:add_student_card", kwargs={"customer_id": customer.pk}
),
"customer": customer,
"student_cards": customer.student_cards.all(),
},
)
def dispatch(self, request: HttpRequest, *args, **kwargs): def dispatch(self, request: HttpRequest, *args, **kwargs):
self.customer = get_object_or_404( self.customer = get_object_or_404(
Customer.objects.prefetch_related("student_cards"), pk=kwargs["customer_id"] Customer.objects.prefetch_related("student_cards"), pk=kwargs["customer_id"]
@ -69,9 +99,8 @@ class StudentCardFormView(FormView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["customer"] = self.customer data = self.get_template_data(self.request, self.customer)
context["action"] = self.request.path context.update(data.context)
context["student_cards"] = self.customer.student_cards.all()
return context return context
def get_success_url(self, **kwargs): def get_success_url(self, **kwargs):

File diff suppressed because it is too large Load Diff