Skip to content

Commit

Permalink
✨(dashboard) add the global consent
Browse files Browse the repository at this point in the history
Replaced function-based `consent_form_view` with `ConsentFormView` class-based view, leveraging Django forms for better structure and maintainability. Introduced a `ConsentForm` for managing consent input and added a `BreadcrumbContextMixin` to streamline breadcrumb handling. Updated templates and URLs accordingly for this refactor.
Refactored consent views by splitting and improving helper functions for better clarity and reusability. Introduced comprehensive tests for `_bulk_update_consent`, ensuring proper validation of consent status updates with varied scenarios. This enhances maintainability and test coverage.
  • Loading branch information
ssorin committed Dec 17, 2024
1 parent 23e0f9f commit 794d69f
Show file tree
Hide file tree
Showing 9 changed files with 571 additions and 131 deletions.
10 changes: 5 additions & 5 deletions src/dashboard/apps/consent/fixtures/consent.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ def seed_consent():
entity4 = EntityFactory(users=(user5,))

# create delivery points
for i in range(1, 4):
DeliveryPointFactory(provider_assigned_id=f"entity1_{i}", entity=entity1)
DeliveryPointFactory(provider_assigned_id=f"entity2_{i}", entity=entity2)
DeliveryPointFactory(provider_assigned_id=f"entity3_{i}", entity=entity3)
DeliveryPointFactory(provider_assigned_id=f"entity4_{i}", entity=entity4)
for _ in range(1, 4):
DeliveryPointFactory(entity=entity1)
DeliveryPointFactory(entity=entity2)
DeliveryPointFactory(entity=entity3)
DeliveryPointFactory(entity=entity4)

# create awaiting consents
for delivery_point in DeliveryPoint.objects.all():
Expand Down
26 changes: 26 additions & 0 deletions src/dashboard/apps/consent/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Dashboard consent app forms."""

from django import forms
from django.forms.widgets import CheckboxInput
from django.utils.translation import gettext_lazy as _


class ConsentCheckboxInput(CheckboxInput):
"""Custom CheckboxInput widget for rendering a checkbox input field."""

template_name = "consent/forms/widgets/checkbox.html"


class ConsentForm(forms.Form):
"""Save user consent through a checkbox field."""

consent_agreed = forms.BooleanField(
required=True,
initial=False,
widget=ConsentCheckboxInput(
attrs={
"label": _("I agree to give my consent"),
"help_text": _("Please confirm your consent by checking this box."),
},
),
)
37 changes: 37 additions & 0 deletions src/dashboard/apps/consent/mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Dashboard consent app mixins."""

from django.views.generic.base import ContextMixin
from django_stubs_ext import StrOrPromise


class BreadcrumbContextMixin(ContextMixin):
"""Mixin to simplify usage of the `dsfr_breadcrumb` in class based views.
Add the breadcrumb elements in the view context for the dsfr breadcrumb:
https://numerique-gouv.github.io/django-dsfr/components/breadcrumb/.
```python
breadcrumb_links = [{"url": "first-url", "title": "First title"}, {...}],
breadcrumb_current: "Current page title",
breadcrumb_root_dir: "the root directory, if the site is not installed at the
root of the domain"
}
```
"""

breadcrumb_links: list[dict[StrOrPromise, StrOrPromise]] | None = None
breadcrumb_current: StrOrPromise | None = None
breadcrumb_root_dir: StrOrPromise | None = None

def get_context_data(self, **kwargs) -> dict:
"""Add breadcrumb context to the view."""
context = super().get_context_data(**kwargs)

context["breadcrumb_data"] = {
"links": self.breadcrumb_links,
"current": self.breadcrumb_current,
}
if self.breadcrumb_root_dir:
context["breadcrumb_data"]["root_dir"] = self.breadcrumb_root_dir

return context
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% include "django/forms/widgets/input.html" %}

<label class="fr-label"
{% if widget.attrs.id %}for="{{ widget.attrs.id }}"{% endif %}>
{{ widget.attrs.label }}
<span class="fr-hint-text">{{ widget.attrs.help_text }}</span>
</label>
116 changes: 59 additions & 57 deletions src/dashboard/apps/consent/templates/consent/manage.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,85 +3,87 @@
{% load i18n static %}

{% block dashboard_content %}
<h2>
{% trans "Manage consents" %}
</h2>

{% if entities %}
<form action="" method="post">
{% csrf_token %}
<h2>{% trans "Manage consents" %}</h2>

{% if entities %}
<form action="" method="post">
{% csrf_token %}

<div class="fr-messages-group" id="error-messages" aria-live="assertive">
{% if form.errors %}
<div class="{% if form.consent_agreed.errors %}fr-pl-3v{% endif %} fr-mb-6v">
<p class="fr-message fr-message--error" id="message-error">
{% trans "The form contains errors" %}
</p>
</div>
{% endif %}
</div>

{# toggle button #}
<div class="fr-fieldset__element">
<div class="fr-checkbox-group">
<input type="checkbox"
id="toggle-all"
name="toggle-all"
aria-describedby="toggle-all-messages"
data-fr-js-checkbox-input="true"
<input type="checkbox" id="toggle-all" name="toggle-all"
aria-describedby="toggle-all-messages"
data-fr-js-checkbox-input="true"
data-fr-js-checkbox-actionee="true">
<label class="fr-label" for="toggle-all">
<strong>{% trans "Toggle All" %}</strong>
</label>
<label class="fr-label" for="toggle-all"><strong>{% trans "Toggle All" %}</strong></label>
</div>
</div>

<div class="consent-wrapper fr-py-6v fr-mb-6v">
<div class="consent-wrapper__inner">
{% for entity in entities %}
<fieldset class="fr-fieldset"
id="checkboxes"
aria-labelledby="checkboxes-legend checkboxes-messages">
<legend class="fr-fieldset__legend--regular fr-fieldset__legend"
id="checkboxes-legend">
{{ entity.name }}
</legend>

{% for consent in entity.get_consents %}
<div class="fr-fieldset__element">
<div class="fr-checkbox-group">
<input type="checkbox"
name="status"
value="{{ consent.id }}"
id="{{ consent.id }}"
{% if consent.status == 'VALIDATED' %} checked{% endif %}
aria-describedby="{{ consent.id }}-messages"
data-fr-js-checkbox-input="true"
data-fr-js-checkbox-actionee="true" />
<label class="fr-label" for="{{ consent.id }}">
{{ consent.delivery_point.provider_assigned_id }}
</label>
<div class="fr-messages-group"
id="{{ consent.id }}-messages"
aria-live="assertive">
</div>
</div>
</div>
{% endfor %}
<fieldset class="fr-fieldset" id="checkboxes" aria-labelledby="checkboxes-legend checkboxes-messages">
<legend class="fr-fieldset__legend--regular fr-fieldset__legend" id="checkboxes-legend">{{ entity.name }}</legend>

<div class="fr-messages-group"
id="checkboxes-messages"
aria-live="assertive">
{{ field.errors }}
{% for consent in entity.get_consents %}
<div class="fr-fieldset__element">
<div class="fr-checkbox-group">
<input type="checkbox"
name="status"
value="{{ consent.id }}"
id="{{ consent.id }}"
{% if consent.status == 'VALIDATED' %} checked{% endif %}
aria-describedby="{{ consent.id }}-messages"
data-fr-js-checkbox-input="true"
data-fr-js-checkbox-actionee="true" />
<label class="fr-label" for="{{ consent.id }}">{{ consent.delivery_point.provider_assigned_id }} </label>
<div class="fr-messages-group" id="{{ consent.id }}-messages" aria-live="assertive"></div>
</div>
</fieldset>
</div>
{% endfor %}

</fieldset>
{% endfor %}
</div>
</div>
<button class="fr-btn" type="submit" name="submit">
{% trans "submit" %}
</button>
</form>

{# checkbox to apply the consent globally #}
<div class="{% if form.consent_agreed.errors %}fr-pl-3v{% endif %} fr-mb-6v">
<div class="fr-checkbox-group {% if form.consent_agreed.errors %}fr-checkbox-group--error{% endif %}">
{{ form.consent_agreed }}
<div class="fr-messages-group" id="{{ consent.id }}-messages" aria-live="assertive">
{% if form.consent_agreed.errors %}
{% for error in form.consent_agreed.errors %}
<p class="fr-message fr-message--error" id="checkboxes-error-message-error">
{{ error }}
</p>
{% endfor %}
{% endif %}
</div>
</div>
</div>

<button class="fr-btn" type="submit" name="submit"> {% trans "submit" %} </button>
</form>

{% else %}
<p>
{% trans "No consents to validate" %}
</p>
<p>{% trans "No consents to validate" %}</p>
{% endif %}
{% endblock dashboard_content %}

{% block dashboard_extra_js %}
{% if entities %}
<script src="{% static 'apps/consent/js/app.js' %}"></script>
<script src="{% static 'apps/consent/js/app.js' %}"></script>
{% endif %}
{% endblock dashboard_extra_js %}
Loading

0 comments on commit 794d69f

Please sign in to comment.