feat: implement structures management module with index views, location geojson endpoints, and modal for adding assets

This commit is contained in:
Karl Determe 2026-08-18 16:05:57 +02:00
parent ff4042b333
commit 360db5aaad
5 changed files with 99 additions and 12 deletions

View file

@ -20,6 +20,18 @@
const categoriesCache = {}; const categoriesCache = {};
function populateCategorySelectIfEmpty(select, categories) {
if (!select || select.options.length > 1 || !categories || categories.length === 0) {
return;
}
categories.forEach(cat => {
const option = document.createElement('option');
option.value = cat.id;
option.textContent = cat.name;
select.appendChild(option);
});
}
function initializeForm(form, modalId) { function initializeForm(form, modalId) {
const thematicCode = form.dataset.thematic; const thematicCode = form.dataset.thematic;
const categorySelect = form.querySelector('.asset-category-select'); const categorySelect = form.querySelector('.asset-category-select');
@ -31,17 +43,22 @@
const submitBtn = form.querySelector(`#${modalId}Submit`); const submitBtn = form.querySelector(`#${modalId}Submit`);
// Fetch and cache categories for this thematic // Fetch and cache categories for this thematic
if (thematicCode && !categoriesCache[thematicCode]) { if (thematicCode) {
fetch(`/assets/${thematicCode}/categories/`) if (categoriesCache[thematicCode]) {
.then(response => response.json()) populateCategorySelectIfEmpty(categorySelect, categoriesCache[thematicCode]);
.then(data => { } else {
if (data.categories) { fetch(`/assets/${thematicCode}/categories/`)
categoriesCache[thematicCode] = data.categories; .then(response => response.json())
} .then(data => {
}) if (data.categories) {
.catch(error => { categoriesCache[thematicCode] = data.categories;
console.error('Error prefetching categories:', error); populateCategorySelectIfEmpty(categorySelect, data.categories);
}); }
})
.catch(error => {
console.error('Error prefetching categories:', error);
});
}
} }
async function onCategoryChange() { async function onCategoryChange() {

View file

@ -23,6 +23,12 @@
{% if intervention_id %} {% if intervention_id %}
<input type="hidden" name="intervention_id" value="{{ intervention_id }}"> <input type="hidden" name="intervention_id" value="{{ intervention_id }}">
{% endif %} {% endif %}
{% if location %}
<input type="hidden" name="location_id" value="{{ location.id }}">
{% endif %}
{% if structure %}
<input type="hidden" name="structure_id" value="{{ structure.id }}">
{% endif %}
<input type="hidden" name="thematic_code" value="{{ thematic_code }}"> <input type="hidden" name="thematic_code" value="{{ thematic_code }}">
<div class="modal-body"> <div class="modal-body">

View file

@ -382,7 +382,7 @@
<!-- Add Asset Modal --> <!-- Add Asset Modal -->
{% if can_create_assets %} {% if can_create_assets %}
{% include "assets/components/add_asset_modal.html" with modal_id="addAssetLocationModal" thematic_code="structures" location=location %} {% include "assets/components/add_asset_modal.html" with modal_id="addAssetLocationModal" thematic_code="structures" location=location categories=categories %}
{% endif %} {% endif %}
{% endblock content%} {% endblock content%}

View file

@ -374,6 +374,65 @@ class CategoryAllowedModelTest(TestCase):
self.assertFalse(accessory_model["is_model_required"]) self.assertFalse(accessory_model["is_model_required"])
class StructuresLocationDetailAddAssetModalTest(TestCase):
def setUp(self):
from common.models import Role
from assets.models.structures import Structure, StructureLocation
from django.contrib.gis.geos import Point
self.user = User.objects.create_superuser(username="admin_struct", password="password123")
self.config = UserConfig.objects.create(user=self.user, is_intern=True)
admin_role, _ = Role.objects.get_or_create(name="admin")
self.config.roles.add(admin_role)
self.thematic, _ = Thematic.objects.get_or_create(
code="structures",
defaults={"name_fr": "Ouvrages d'art", "name_nl": "Kunstwerken"}
)
UserThematics.objects.create(
user_config=self.config,
thematic=self.thematic,
can_view_assets=True,
can_edit_assets=True
)
self.category, _ = AssetCategory.objects.get_or_create(
code="TU_BAL_TEST",
defaults={
"name_fr": "Balisage Test",
"name_nl": "Balisage Test NL",
"thematic": self.thematic
}
)
from django.contrib.gis.geos import MultiPolygon, Polygon
poly = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
mpoly = MultiPolygon(poly, srid=3812)
self.structure = Structure.objects.create(
code="OA_TEST_01",
name_fr="Pont Test",
structure_type="bridge"
)
self.location = StructureLocation.objects.create(
code="LOC_TEST_01",
name_fr="Localisation Test",
structure=self.structure,
geom=mpoly
)
def test_structures_location_detail_contains_categories(self):
self.client.force_login(self.user)
response = self.client.get(reverse("assets:structures_locations_detail", args=[self.location.id]))
self.assertEqual(response.status_code, 200)
self.assertIn("categories", response.context)
categories_in_context = response.context["categories"]
self.assertTrue(any(c.id == self.category.id for c in categories_in_context))
self.assertContains(response, "Balisage Test")
self.assertContains(response, f'name="location_id" value="{self.location.id}"')
from assets.views.trafficlights import parse_dxf_poles from assets.views.trafficlights import parse_dxf_poles
@tag('slow', 'dxf') @tag('slow', 'dxf')

View file

@ -621,10 +621,15 @@ def structures_locations_detail(request, location_id):
geom = json.loads(location_geom_4326.geojson) if location_geom_4326 else None geom = json.loads(location_geom_4326.geojson) if location_geom_4326 else None
# Récupérer les catégories d'assets pour la thématique "structures"
categories = list(AssetCategory.objects.filter(thematic__code='structures'))
categories.sort(key=lambda m: m.get_name().lower())
return render(request, "assets/structures_location_detail.html", { return render(request, "assets/structures_location_detail.html", {
"location": location, "location": location,
"interventions": interventions, # toutes les interventions de l'arbre "interventions": interventions, # toutes les interventions de l'arbre
"assets": assets, "assets": assets,
"categories": categories,
"main_documents": main_documents, "main_documents": main_documents,
"other_documents": other_docs, "other_documents": other_docs,
"add_document_form": StructureDocumentForm(), "add_document_form": StructureDocumentForm(),