Skip to content

Commit

Permalink
Refondre le fonctionnement des liens libres
Browse files Browse the repository at this point in the history
Les liens libre ne sont plus ajoutés via une modale sur chaque page
fiche mais lors de la création / édition de la fiche en question.
On ajoute aussi la possibilité de supprimer un lien libre lors de
l'édition d'une fiche.

Suppression d'une grosse partie de l'ancienne feature et ajout du
nouveau comportement uniquement pour la fiche détection pour le moment.
  • Loading branch information
Anto59290 committed Nov 14, 2024
1 parent 4fe7582 commit 06e7130
Show file tree
Hide file tree
Showing 22 changed files with 225 additions and 232 deletions.
8 changes: 8 additions & 0 deletions core/content_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.contrib.contenttypes.models import ContentType


def content_type_str_to_obj(obj_as_str):
content_type_id, object_id = obj_as_str.split("-")
content_type = ContentType.objects.get(id=content_type_id)
model_class = content_type.model_class()
return model_class.objects.get(id=object_id)
30 changes: 29 additions & 1 deletion core/managers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.contrib.contenttypes.models import ContentType
from django.db.models import Case, When, Value, IntegerField, QuerySet
from django.db.models import Case, When, Value, IntegerField, QuerySet, Q

from core.constants import MUS_STRUCTURE, BSV_STRUCTURE, AC_STRUCTURE

Expand Down Expand Up @@ -55,3 +55,31 @@ def order_by_structure_and_name(self):

def order_by_structure_and_niveau2(self):
return self.order_by("services_deconcentres_first", "structure__niveau2")


class LienLibreQueryset(QuerySet):
def for_object(self, obj):
content_type = ContentType.objects.get_for_model(obj)
return self.filter(
(Q(content_type_1=content_type, object_id_1=obj.id)) | (Q(content_type_2=content_type, object_id_2=obj.id))
)

def for_both_objects(self, object_1, object_2):
content_type_1 = ContentType.objects.get_for_model(object_1)
content_type_2 = ContentType.objects.get_for_model(object_2)
link = self.filter(
content_type_1=content_type_1,
object_id_1=object_1.id,
content_type_2=content_type_2,
object_id_2=object_2.id,
).first()
if link:
return link

link = self.filter(
content_type_2=content_type_1,
object_id_2=object_1.id,
content_type_1=content_type_2,
object_id_1=object_2.id,
).first()
return link
7 changes: 1 addition & 6 deletions core/mixins.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.db import models
from django.urls import reverse

Expand Down Expand Up @@ -73,11 +72,7 @@ def get_context_data(self, **kwargs):
class WithFreeLinksListInContextMixin:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
obj = self.get_object()
context["free_links_list"] = LienLibre.objects.filter(
(Q(content_type_1=ContentType.objects.get_for_model(obj), object_id_1=obj.id))
| (Q(content_type_2=ContentType.objects.get_for_model(obj), object_id_2=obj.id))
)
context["free_links_list"] = LienLibre.objects.for_object(self.get_object())
return context


Expand Down
4 changes: 3 additions & 1 deletion core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.utils.translation import gettext_lazy as _

from django.contrib.auth import get_user_model
from .managers import DocumentQueryset, ContactQueryset
from .managers import DocumentQueryset, ContactQueryset, LienLibreQueryset
from django.apps import apps
from core.constants import AC_STRUCTURE, MUS_STRUCTURE, BSV_STRUCTURE
from .storage import get_timestamped_filename
Expand Down Expand Up @@ -230,6 +230,8 @@ class LienLibre(models.Model):
object_id_2 = models.PositiveIntegerField()
related_object_2 = GenericForeignKey("content_type_2", "object_id_2")

objects = LienLibreQueryset.as_manager()

class Meta:
constraints = [
CheckConstraint(
Expand Down
14 changes: 14 additions & 0 deletions core/static/core/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ header{
/* Make sure we never have 2 down arrows (1 via choices js, 1 via DSFR) for select inputs that uses choices js*/
background-image: none;
}
.choices__list--multiple .choices__item {
/* Customize ChoicesJS multiple choices picker */
min-width: 0px;
min-height: 0px;
padding: .25rem .75rem !important;
background-color: var(--background-active-blue-france) !important;
border: 1px solid var(--background-active-blue-france) !important;
color: var(--text-inverted-blue-france) !important;
margin: 0.25rem;
}
.choices__input {
background-color: var(--background-contrast-grey) !important;
margin-left: 1rem;
}
.choices__list--dropdown{
z-index: 10 !important;
}
Expand Down
15 changes: 1 addition & 14 deletions core/static/core/message.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,12 @@
.message-document-container .fr-link {
max-width:75%;
}
.message-container .choices__list--multiple .choices__item{
min-width: 0px;
min-height: 0px;
padding: .25rem .75rem;
background-color: var(--background-active-blue-france);
border: 1px solid var(--background-active-blue-france);
color: var(--text-inverted-blue-france);
margin: 0.25rem;
}


.message-container .choices__button:hover{
background-color: unset;
}

.message-container .choices__input{
background-color: var(--background-contrast-grey);
margin-left: 1rem;
}

.message-documents-container {
background-color: var(--background-disabled-grey);
}
Expand Down
15 changes: 14 additions & 1 deletion sv/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.contrib.contenttypes.models import ContentType
from django.db import models, transaction
from django.core.validators import RegexValidator
from django.db.models import TextChoices, Q
Expand All @@ -13,7 +14,7 @@
IsActiveMixin,
WithMessageUrlsMixin,
)
from core.models import Document, Message, Contact, Structure, FinSuiviContact, UnitesMesure, Visibilite
from core.models import Document, Message, Contact, Structure, FinSuiviContact, UnitesMesure, Visibilite, LienLibre
from sv.managers import (
LaboratoireAgreeManager,
LaboratoireConfirmationOfficielleManager,
Expand Down Expand Up @@ -520,6 +521,18 @@ def get_fiche_zone_delimitee(self) -> "FicheZoneDelimitee":
if self.zone_infestee and self.zone_infestee.fiche_zone_delimitee:
return self.zone_infestee.fiche_zone_delimitee

@property
def free_link_ids(self):
content_type = ContentType.objects.get_for_model(self)
links = LienLibre.objects.for_object(self).select_related("content_type_2", "content_type_1")
link_ids = []
for link in links:
if link.object_id_1 == self.id and link.content_type_1 == content_type:
link_ids.append(f"{link.content_type_2.pk}-{link.object_id_2}")
else:
link_ids.append(f"{link.content_type_1.pk}-{link.object_id_1}")
return link_ids


class ZoneInfestee(models.Model):
class UnitesSurfaceInfesteeTotale(TextChoices):
Expand Down
6 changes: 0 additions & 6 deletions sv/static/sv/fichedetection_detail.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,4 @@ document.addEventListener('DOMContentLoaded', function() {
detailElement.classList.remove(hiddenClass);
syntheseElement.classList.add(hiddenClass);

new Choices(document.getElementById('id_object_choice'), {
classNames: {
containerInner: 'fr-select',
},
itemSelectText: ''
});
});
6 changes: 5 additions & 1 deletion sv/static/sv/fichedetection_form.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ main {
#mesures-gestion{
grid-column: span 3;
}
#liens-libre{
grid-column: span 3;
}
#lieux, #prelevements {
overflow-y: auto;
max-height: 40rem;
Expand All @@ -27,7 +30,8 @@ main {
#objet-evenement,
#lieux,
#prelevements,
#mesures-gestion {
#mesures-gestion,
#liens-libre {
background-color: white;
padding: 1.5rem;
}
Expand Down
27 changes: 26 additions & 1 deletion sv/static/sv/fichedetection_form.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ document.addEventListener('alpine:init', () => {
numeroRasff: '',
numeroEurophyt: '',
organismeNuisibleId: '',
freeLinksIds: '',
statutReglementaireId: '',
contexteId: '',
datePremierSignalement: '',
Expand Down Expand Up @@ -195,6 +196,7 @@ document.addEventListener('alpine:init', () => {
statutEvenementId: this.getValueById('statut-evenement-id'),
organismeNuisibleId: this.getValueById('organisme-nuisible-id'),
statutReglementaireId: this.getValueById('statut-reglementaire-id'),
freeLinksIds: JSON.parse(document.getElementById('free-links-id').textContent),
contexteId: this.getValueById('contexte-id'),
datePremierSignalement: this.getValueById('date-premier-signalement'),
commentaire: this.getValueById('commentaire'),
Expand All @@ -203,7 +205,6 @@ document.addEventListener('alpine:init', () => {
mesuresPhytosanitaires: this.getValueById('mesures-phytosanitaires'),
mesuresSurveillanceSpecifique: this.getValueById('mesures-surveillance-specifique')
};

// Récupération et initialisation des lieux (si modification d'une fiche de détection existante)
const lieux = JSON.parse(document.getElementById('lieux-json').textContent);
if(lieux) {
Expand Down Expand Up @@ -289,6 +290,27 @@ document.addEventListener('alpine:init', () => {

this.choicesCommunes = choicesCommunes

options = {
searchResultLimit: 500,
classNames: {
containerInner: 'fr-select',
},
removeItemButton: true,
itemSelectText: '',
noResultsText: 'Aucun résultat trouvé',
noChoicesText: 'Aucune fiche à sélectionner',
searchFields: ['label'],
};
const freeLinksChoices = new Choices(document.getElementById("free-links"), options);
freeLinksChoices.passedElement.element.addEventListener("change", (event)=> {
this.ficheDetection.freeLinksIds = Array.from(freeLinksChoices.getValue(true))
})
if (!!this.ficheDetection.freeLinksIds) {
this.ficheDetection.freeLinksIds.forEach(value => {
freeLinksChoices.setChoiceByValue(value);
});
}

},

getValueById(id) {
Expand Down Expand Up @@ -582,6 +604,9 @@ document.addEventListener('alpine:init', () => {
formData.append('lieux', JSON.stringify(this.lieux));
formData.append('prelevements', JSON.stringify(this.prelevements));
formData.append('action', action);
for (var i = 0; i < this.ficheDetection.freeLinksIds.length; i++) {
formData.append('freeLinksIds', this.ficheDetection.freeLinksIds[i]);
}

const csrfToken = document.querySelector('input[name="csrfmiddlewaretoken"]').value;
const url = document.getElementById('fiche-detection-form-url').dataset.url;
Expand Down
17 changes: 0 additions & 17 deletions sv/static/sv/fichezone_form.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,3 @@
.zone-infestee-form__numero-input {
width: 50%;
}

.fichezoneform__detections-hors-zone-infestee .choices__input,
.zone-infestees__zone-infestee-form .choices__input {
background-color: var(--background-contrast-grey);
margin-left: 1rem;
}

.fichezoneform__detections-hors-zone-infestee .choices__list--multiple .choices__item,
.zone-infestees__zone-infestee-form .choices__list--multiple .choices__item {
min-width: 0px;
min-height: 0px;
padding: .25rem .75rem;
background-color: var(--background-active-blue-france);
border: 1px solid var(--background-active-blue-france);
color: var(--text-inverted-blue-france);
margin: 0.25rem;
}
1 change: 0 additions & 1 deletion sv/templates/sv/_detection_action_navigation.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
<button class="fr-translate__language fr-nav__link" href="#" type="submit" disabled><span class="fr-icon-notification-3-fill fr-mr-2v fr-icon--sm" aria-hidden="true"></span>Déclarer à l'AC</button>
{% endif %}
</li>
<li><a class="fr-translate__language fr-nav__link" href="#" data-fr-opened="false" aria-controls="fr-modal-freelink"><span class="fr-icon-git-pull-request-fill fr-mr-2v fr-icon--sm" aria-hidden="true"></span>Ajouter un lien libre</a></li>
<li>
<button class="fr-translate__language fr-nav__link" href="#" data-fr-opened="false" aria-controls="fr-modal-create-fiche-zone-delimitee" {% if fichedetection.is_linked_to_fiche_zone_delimitee %}disabled{% endif %}>
<span class="fr-icon-git-pull-request-fill fr-mr-2v fr-icon--sm" aria-hidden="true"></span>Ajouter une zone
Expand Down
29 changes: 0 additions & 29 deletions sv/templates/sv/_lienlibre_modal.html

This file was deleted.

1 change: 0 additions & 1 deletion sv/templates/sv/_zone_action_navigation.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
aria-expanded="false" title="Sélectionner une action">Actions</button>
<div class="fr-collapse fr-translate__menu fr-menu" id="action-1">
<ul class="fr-menu__list">
<li><a class="fr-translate__language fr-nav__link" href="#" data-fr-opened="false" aria-controls="fr-modal-freelink"><span class="fr-icon-git-pull-request-fill fr-mr-2v fr-icon--sm" aria-hidden="true"></span>Ajouter un lien libre</a></li>
{% if can_update_visibilite %}
<li><a class="fr-translate__language fr-nav__link" href="#" data-fr-opened="false" aria-controls="fr-modal-edit-visibilite"><span class="fr-icon-eye-fill fr-mr-2v fr-icon--sm" aria-hidden="true"></span>Modifier la visibilité</a></li>
{% endif %}
Expand Down
1 change: 0 additions & 1 deletion sv/templates/sv/fichedetection_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ <h1 class="fiche-header__title">Fiche détection {% if fichedetection.numero %}
<a href="{% url 'fiche-detection-modification' fichedetection.pk %}"><span class="fr-icon-edit-fill">Modifier</span></a>
</button>
{% include "sv/_detection_action_navigation.html" %}
{% include "sv/_lienlibre_modal.html" with fiche=fichedetection %}
{% include "sv/_delete_fiche_modal.html" %}
{% include "sv/_cloturer_modal.html" with fiche=fichedetection %}
{% if can_update_visibilite %}
Expand Down
16 changes: 16 additions & 0 deletions sv/templates/sv/fichedetection_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<input type="hidden" id="mesures-consignation" value="{{ form.instance.mesures_consignation }}">
<input type="hidden" id="mesures-phytosanitaires" value="{{ form.instance.mesures_phytosanitaires }}">
<input type="hidden" id="mesures-surveillance-specifique" value="{{ form.instance.mesures_surveillance_specifique }}">
{{ form.instance.free_link_ids|json_script:"free-links-id" }}
{{ lieux|json_script:"lieux-json" }}
{{ prelevements|json_script:"prelevements" }}
{{ structures_preleveurs|json_script:"structures-preleveurs" }}
Expand Down Expand Up @@ -190,6 +191,21 @@ <h2>Mesures de gestion</h2>
</p>
</div>
</div>

<div id="liens-libre">
<h2>Liens libres</h2>
<div>
<label class="fr-label" for="free-links">Lien vers une fiche</label>
<select id="free-links" multiple="">
<option value="">----</option>
{% for object_type_id, label, queryset in possible_links %}
{% for object in queryset %}
<option value="{{ object_type_id }}-{{ object.id }}">{{ label }} : {{ object }}</option>
{% endfor %}
{% endfor %}
</select>
</div>
</div>
</div>
</form>

Expand Down
1 change: 0 additions & 1 deletion sv/templates/sv/fichezonedelimitee_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ <h1 class="fr-mb-0-5v">Fiche zone délimitée n° {{ fiche.numero }}</h1>
<li>
<div class="fiche-action">
{% include "sv/_zone_action_navigation.html" %}
{% include "sv/_lienlibre_modal.html" %}
{% if can_update_visibilite %}
{% include "sv/_update_visibilite_modal.html" %}
{% endif %}
Expand Down
Loading

0 comments on commit 06e7130

Please sign in to comment.