Skip to content

Commit

Permalink
Use HTMX on employee records list
Browse files Browse the repository at this point in the history
  • Loading branch information
francoisfreitag committed Apr 29, 2024
1 parent 776e065 commit cb252f8
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 95 deletions.
50 changes: 50 additions & 0 deletions itou/templates/employee_record/includes/list_results.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{% load str_filters %}

<div id="employee-records-container">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
{% with navigation_pages.paginator.count as counter %}
<h3 class="h4 m-0">{{ counter }} résultat{{ counter|pluralizefr }}</h3>
{% endwith %}
</div>
<div>
<span class="fs-sm">Trier par :</span>
<button type="button" class="btn btn-sm btn-link dropdown-toggle p-0" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ ordered_by_label }}
</button>
<div class="dropdown-menu dropdown-menu-end" id="order-form-group">
{% for order_value, order_label in form.order.field.choices %}
<button class="dropdown-item {% if order_value == form.order.value %}active{% endif %}" type="button" value="{{ order_value }}">
{{ order_label }}
</button>
{% endfor %}
</div>
</div>
</div>

{# "Real" employee records objects #}
<div class="employee-records-list">
{% if employee_records_list %}
{% for employee_record in navigation_pages %}
{% include "employee_record/includes/list_item.html" with employee_record=employee_record item=employee_record.job_application only %}
{% endfor %}
{# New employee records i.e. job applications #}
{% else %}
{% for job_application in navigation_pages %}
{% include "employee_record/includes/list_item.html" with employee_record=None item=job_application only %}
{% endfor %}
{% endif %}
</div>

{% if not navigation_pages %}
<div class="c-box c-box--results my-3 my-md-4">
<div class="c-box--results__body">
<p class="mb-0">Aucune fiche salarié avec le statut selectionné.</p>
</div>
</div>
{% endif %}
{% include "includes/pagination.html" with page=navigation_pages boost=True boost_target="#employee-records-container" boost_indicator="#employee-records-container" %}
</div>
{% if request.htmx %}
{% include "employee_record/includes/list_status_help.html" with request=request status=form.status.value only %}
{% endif %}
41 changes: 41 additions & 0 deletions itou/templates/employee_record/includes/list_status_help.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<div id="status-help"{% if request.htmx %} hx-swap-oob="true"{% endif %}>
{% if status == "NEW" %}
<p>
Vous trouverez ici les candidatures validées <b>à partir desquelles vous devez créer de nouvelles fiches salarié</b>.
</p>
{% elif status == "READY" %}
<p class="mb-0">
Vous trouverez ici les fiches salarié complétées
<b>en attente d’envoi à l’ASP, qui a lieu automatiquement à intervalles réguliers</b>.
</p>
<p>
À ce stade, seule la visualisation des informations de la fiche est
possible.
</p>
<p>Merci de votre patience.</p>
{% elif status == "SENT" %}
<p class="mb-0">Vous trouverez ici les fiches salarié complétées et envoyées à l'ASP.</p>
<p>
À ce stade, et en attendant un retour de l'ASP, seule la visualisation des informations de
la fiche est possible.
</p>
{% elif status == "REJECTED" %}
<p class="mb-0">
Vous trouverez ici les fiches salarié envoyées à l'ASP et retournées avec une
erreur.
</p>
<p>Vous pouvez modifier les fiches en erreur et les envoyer à nouveau.</p>
{% elif status == "PROCESSED" %}
<p class="mb-0">Vous trouverez ici les fiches salarié envoyées et validées par l'ASP.</p>
<p>
Aucune action ultérieure n'est possible à ce stade, mais vous pouvez consulter le détail de
la fiche salarié.
</p>
{% elif status == "DISABLED" %}
<p class="mb-0">Vous trouverez ici les fiches salarié que vous avez désactivées.</p>
<p>
En cas de besoin vous pouvez réactiver une fiche, elle sera transférée dans la catégorie
"Nouvelle".
</p>
{% endif %}
</div>
106 changes: 12 additions & 94 deletions itou/templates/employee_record/list.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{% extends "layout/base.html" %}
{% load static %}
{% load format_filters %}
{% load str_filters %}
{% load list_filters %}
{% load django_bootstrap5 %}

Expand Down Expand Up @@ -41,45 +40,7 @@ <h2 class="h3">Nous transférons vos fiches salarié à l'ASP afin de vous faire
</li>
<li>La visualisation dans l’Extranet IAE 2.0 interviendra dans les 2 heures suivant l’envoi.</li>
</ul>
{% if form.status.value == "NEW" %}
<p>
Vous trouverez ici les candidatures validées <b>à partir desquelles vous devez créer de nouvelles fiches salarié</b>.
</p>
{% elif form.status.value == "READY" %}
<p class="mb-0">
Vous trouverez ici les fiches salarié complétées
<b>en attente d’envoi à l’ASP, qui a lieu automatiquement à intervalles réguliers</b>.
</p>
<p>
À ce stade, seule la visualisation des informations de la fiche est
possible.
</p>
<p>Merci de votre patience.</p>
{% elif form.status.value == "SENT" %}
<p class="mb-0">Vous trouverez ici les fiches salarié complétées et envoyées à l'ASP.</p>
<p>
À ce stade, et en attendant un retour de l'ASP, seule la visualisation des informations de
la fiche est possible.
</p>
{% elif form.status.value == "REJECTED" %}
<p class="mb-0">
Vous trouverez ici les fiches salarié envoyées à l'ASP et retournées avec une
erreur.
</p>
<p>Vous pouvez modifier les fiches en erreur et les envoyer à nouveau.</p>
{% elif form.status.value == "PROCESSED" %}
<p class="mb-0">Vous trouverez ici les fiches salarié envoyées et validées par l'ASP.</p>
<p>
Aucune action ultérieure n'est possible à ce stade, mais vous pouvez consulter le détail de
la fiche salarié.
</p>
{% elif form.status.value == "DISABLED" %}
<p class="mb-0">Vous trouverez ici les fiches salarié que vous avez désactivées.</p>
<p>
En cas de besoin vous pouvez réactiver une fiche, elle sera transférée dans la catégorie
"Nouvelle".
</p>
{% endif %}
{% include "employee_record/includes/list_status_help.html" with request=request status=form.status.value only %}
{% endblock %}

{% block content %}
Expand All @@ -101,7 +62,7 @@ <h2 class="h3">Nous transférons vos fiches salarié à l'ASP afin de vous faire
<span>Filtres des fiches salarié</span>
</button>
<div class="c-aside-filters__card collapse show" id="asideFiltersCollapse">
<form method="get">
<form hx-get="{% url 'employee_record_views:list' %}" hx-trigger="change delay:.5s" hx-indicator="#employee-records-container" hx-target="#employee-records-container" hx-swap="outerHTML" hx-push-url="true">
<div class="c-aside-filters__card__body">
<fieldset>
<legend>Statut</legend>
Expand All @@ -128,69 +89,26 @@ <h2 class="h3">Nous transférons vos fiches salarié à l'ASP afin de vous faire
</div>
</aside>
</div>

<div class="col-12 col-md-8">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
{% with navigation_pages.paginator.count as counter %}
<h3 class="h4 m-0">{{ counter }} résultat{{ counter|pluralizefr }}</h3>
{% endwith %}
</div>
<div>
<span class="fs-sm">Trier par :</span>
<button type="button" class="btn btn-sm btn-link dropdown-toggle p-0" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ ordered_by_label }}
</button>
<div class="dropdown-menu dropdown-menu-end" id="order-form-group">
{% for order_value, order_label in form.order.field.choices %}
<button class="dropdown-item {% if order_value == form.order.value %}active{% endif %}" type="button" value="{{ order_value }}">
{{ order_label }}
</button>
{% endfor %}
</div>
</div>
</div>

{# "Real" employee records objects #}
<div class="employee-records-list">
{% if employee_records_list %}
{% for employee_record in navigation_pages %}
{% include "employee_record/includes/list_item.html" with employee_record=employee_record item=employee_record.job_application only %}
{% endfor %}
{# New employee records i.e. job applications #}
{% else %}
{% for job_application in navigation_pages %}
{% include "employee_record/includes/list_item.html" with employee_record=None item=job_application only %}
{% endfor %}
{% endif %}
</div>

{% if not navigation_pages %}
<div class="c-box c-box--results my-3 my-md-4">
<div class="c-box--results__body">
<p class="mb-0">Aucune fiche salarié avec le statut selectionné.</p>
</div>
</div>
{% endif %}
{% include "includes/pagination.html" with page=navigation_pages %}
</div>
<div class="col-12 col-md-8">{% include "employee_record/includes/list_results.html" %}</div>
</div>
</div>
</section>
{% endblock %}

{% block script %}
{{ block.super }}
<script src='{% static "js/htmx_compat.js" %}'></script>
<!-- Needed to use Select2MultipleWidget. -->
{{ filters_form.media.js }}
<script nonce="{{ CSP_NONCE }}">
$("#asideFiltersCollapse :input").change(function() {
$("#asideFiltersCollapse form").submit();
});
$("#order-form-group :input").click(function(event) {
let input = $("#id_order")
input.val(event.target.value); // Fill the hidden order input of the form
input.change(); // Fire a change event to notify handlers
htmx.onLoad(function() {
document.getElementById("order-form-group").addEventListener("click", function(event) {
const orderHidden = document.getElementById("id_order");
orderHidden.value = event.target.value;
orderHidden.dispatchEvent(new Event("change", {
bubbles: true
}))
});
});
</script>
{% endblock %}
2 changes: 1 addition & 1 deletion itou/www/employee_record_views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def list_employee_records(request, template_name="employee_record/list.html"):
"matomo_custom_title": "Fiches salarié ASP",
}

return render(request, template_name, context)
return render(request, "employee_record/includes/list_results.html" if request.htmx else template_name, context)


@login_required
Expand Down
18 changes: 18 additions & 0 deletions tests/www/employee_record_views/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from tests.employee_record import factories as employee_record_factories
from tests.employee_record.factories import EmployeeRecordFactory
from tests.job_applications.factories import JobApplicationWithApprovalNotCancellableFactory
from tests.utils.htmx.test import assertSoupEqual, update_page_with_htmx
from tests.utils.test import BASE_NUM_QUERIES, TestCase, parse_response_to_soup


Expand Down Expand Up @@ -468,6 +469,23 @@ def test_display_result_count(self):
response = self.client.get(self.URL + "?status=READY")
self.assertContains(response, "0 résultat")

def test_htmx(self):
self.client.force_login(self.user)
response = self.client.get(self.URL, {"status": "NEW"})
simulated_page = parse_response_to_soup(response)

[new_status] = simulated_page.find_all("input", attrs={"name": "status", "value": "NEW"})
del new_status["checked"]
[ready_status] = simulated_page.find_all("input", attrs={"name": "status", "value": "READY"})
ready_status["checked"] = ""

response = self.client.get(self.URL, {"status": "READY"}, headers={"HX-Request": "true"})
update_page_with_htmx(simulated_page, f"form[hx-get='{self.URL}']", response)

response = self.client.get(self.URL + "?status=READY")
fresh_page = parse_response_to_soup(response)
assertSoupEqual(simulated_page, fresh_page)


def test_an_active_siae_without_convention_can_not_access_the_view(client):
siae = CompanyFactory(
Expand Down

0 comments on commit cb252f8

Please sign in to comment.