feat: implement structures management module with index views, location geojson endpoints, and modal for adding assets
This commit is contained in:
parent
ff4042b333
commit
360db5aaad
5 changed files with 99 additions and 12 deletions
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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%}
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue