{% include "job_seekers_views/includes/list_counter.html" with paginator=paginator request=request only %}
-
- {% include "job_seekers_views/includes/list_results.html" with page_obj=page_obj request=request only %}
+ {% include "job_seekers_views/includes/list_results.html" with page_obj=page_obj request=request order=order only %}
diff --git a/itou/utils/staticfiles.py b/itou/utils/staticfiles.py
index 475fcfef70..89a0c0a32b 100644
--- a/itou/utils/staticfiles.py
+++ b/itou/utils/staticfiles.py
@@ -219,11 +219,11 @@
},
"theme-inclusion": {
"download": {
- "url": "https://github.com/gip-inclusion/itou-theme/archive/refs/tags/v2.6.6.zip",
- "sha256": "34107c71ba0afecb36d157f57d7ec39e20b33ccc66440e62e954faca4a8fba53",
+ "url": "https://github.com/gip-inclusion/itou-theme/archive/refs/tags/v2.6.9.zip",
+ "sha256": "93bf5f758ca3c5daf70b07eeb7720957d2e6bd9b7ce91efb1414c01ddd6139ab",
},
"extract": {
- "origin": "itou-theme-2.6.6/dist",
+ "origin": "itou-theme-2.6.9/dist",
"destination": "vendor/theme-inclusion/",
"files": [
"javascripts/app.js",
diff --git a/itou/www/job_seekers_views/enums.py b/itou/www/job_seekers_views/enums.py
index cd272a1f2f..8a235db72e 100644
--- a/itou/www/job_seekers_views/enums.py
+++ b/itou/www/job_seekers_views/enums.py
@@ -1,7 +1,29 @@
-from enum import StrEnum
+import enum
-class JobSeekerSessionKinds(StrEnum):
+class JobSeekerSessionKinds(enum.StrEnum):
CHECK_NIR_JOB_SEEKER = "job-seeker-check-nir-job-seeker"
GET_OR_CREATE = "job-seeker-get-or-create"
UPDATE = "job-seeker-update"
+
+
+class JobSeekerOrder(enum.StrEnum):
+ FULL_NAME_ASC = "full_name"
+ FULL_NAME_DESC = "-full_name"
+ LAST_UPDATED_AT_ASC = "last_updated_at"
+ LAST_UPDATED_AT_DESC = "-last_updated_at"
+ JOB_APPLICATIONS_NB_ASC = "job_applications_nb"
+ JOB_APPLICATIONS_NB_DESC = "-job_applications_nb"
+
+ @property
+ def opposite(self):
+ if self.value.startswith("-"):
+ return self.__class__(self.value[1:])
+ else:
+ return self.__class__(f"-{self.value}")
+
+ # Make the Enum work in Django's templates
+ # See :
+ # - https://docs.djangoproject.com/en/dev/ref/templates/api/#variables-and-lookups
+ # - https://github.com/django/django/pull/12304
+ do_not_call_in_templates = enum.nonmember(True)
diff --git a/itou/www/job_seekers_views/forms.py b/itou/www/job_seekers_views/forms.py
index 436f0a402f..81233b2604 100644
--- a/itou/www/job_seekers_views/forms.py
+++ b/itou/www/job_seekers_views/forms.py
@@ -14,6 +14,7 @@
from itou.utils.templatetags.str_filters import mask_unless
from itou.utils.validators import validate_nir
from itou.utils.widgets import DuetDatePickerWidget
+from itou.www.job_seekers_views.enums import JobSeekerOrder
class FilterForm(forms.Form):
@@ -27,6 +28,12 @@ class FilterForm(forms.Form):
),
)
+ order = forms.ChoiceField(
+ choices=[(item.value, item.name) for item in JobSeekerOrder],
+ required=False,
+ widget=forms.HiddenInput(),
+ )
+
def __init__(self, job_seeker_qs, data, *args, request_user, **kwargs):
super().__init__(data, *args, **kwargs)
self.fields["job_seeker"].choices = [
diff --git a/itou/www/job_seekers_views/views.py b/itou/www/job_seekers_views/views.py
index 8a9532e0ac..a6043e995c 100644
--- a/itou/www/job_seekers_views/views.py
+++ b/itou/www/job_seekers_views/views.py
@@ -28,7 +28,7 @@
from itou.utils.session import SessionNamespace
from itou.utils.urls import get_safe_url
from itou.www.apply.views.submit_views import ApplicationBaseView
-from itou.www.job_seekers_views.enums import JobSeekerSessionKinds
+from itou.www.job_seekers_views.enums import JobSeekerOrder, JobSeekerSessionKinds
from itou.www.job_seekers_views.forms import (
CheckJobSeekerInfoForm,
CheckJobSeekerNirForm,
@@ -111,10 +111,7 @@ def get_context_data(self, **kwargs):
class JobSeekerListView(UserPassesTestMixin, ListView):
model = User
queryset = (
- User.objects.filter(kind=UserKind.JOB_SEEKER)
- .order_by("first_name", "last_name")
- .prefetch_related("approvals")
- .select_related("jobseeker_profile")
+ User.objects.filter(kind=UserKind.JOB_SEEKER).prefetch_related("approvals").select_related("jobseeker_profile")
)
paginate_by = 10
paginator_class = ItouPaginator
@@ -122,6 +119,7 @@ class JobSeekerListView(UserPassesTestMixin, ListView):
def __init__(self):
super().__init__()
self.form = None
+ self.order = None
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
@@ -146,9 +144,11 @@ def setup(self, request, *args, **kwargs):
)
self.form = FilterForm(
User.objects.filter(kind=UserKind.JOB_SEEKER).filter(pk__in=self.job_seekers_ids),
- self.request.GET or None,
+ self.request.GET or {},
request_user=request.user,
)
+ self.form.full_clean() # We do not use is_valid to validate each field independently
+ self.order = JobSeekerOrder(self.form.cleaned_data.get("order") or JobSeekerOrder.FULL_NAME_ASC)
def test_func(self):
return self.request.user.is_prescriber
@@ -160,6 +160,7 @@ def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["back_url"] = get_safe_url(self.request, "back_url")
context["filters_form"] = self.form
+ context["order"] = self.order
page_obj = context["page_obj"]
if page_obj is not None:
for job_seeker in page_obj:
@@ -198,9 +199,13 @@ def get_queryset(self):
valid_eligibility_diagnosis=subquery_diagnosis,
)
- if self.form.is_valid() and (job_seeker_pk := self.form.cleaned_data["job_seeker"]):
+ if job_seeker_pk := self.form.cleaned_data["job_seeker"]:
query = query.filter(pk=job_seeker_pk)
-
+ order_args = {
+ JobSeekerOrder.FULL_NAME_ASC: ("first_name", "last_name"),
+ JobSeekerOrder.FULL_NAME_DESC: ("-first_name", "-last_name"),
+ }.get(self.order, (str(self.order),))
+ query = query.order_by(*order_args)
return query
diff --git a/tests/www/job_seekers_views/__snapshots__/test_list.ambr b/tests/www/job_seekers_views/__snapshots__/test_list.ambr
index f5cf2a12f3..23361c6b6f 100644
--- a/tests/www/job_seekers_views/__snapshots__/test_list.ambr
+++ b/tests/www/job_seekers_views/__snapshots__/test_list.ambr
@@ -56,11 +56,12 @@