feat: implement asset preselection in quick inspection map with coordinate and filter auto-focus

This commit is contained in:
kdeterme 2026-08-08 10:04:55 +02:00
parent ccc465bf5d
commit 79673367f0
4 changed files with 185 additions and 6 deletions

View file

@ -22,9 +22,19 @@
if (mapContainer.dataset.mapInitialized === 'true') return;
mapContainer.dataset.mapInitialized = 'true';
// Centrage et style depuis les settings globaux (window.MAP_STYLE_LIGHT / window.DEFAULT_MAP_CENTER)
const defaultCenter = window.DEFAULT_MAP_CENTER || [4.3517, 50.8503];
const defaultZoom = window.DEFAULT_MAP_ZOOM || 15;
// Centrage et style depuis les settings globaux ou pré-sélection d'asset
let defaultCenter = window.DEFAULT_MAP_CENTER || [4.3517, 50.8503];
let defaultZoom = window.DEFAULT_MAP_ZOOM || 15;
if (window.PRESELECT_LON !== null && window.PRESELECT_LAT !== null && typeof window.PRESELECT_LON !== 'undefined' && typeof window.PRESELECT_LAT !== 'undefined') {
const preLon = parseFloat(window.PRESELECT_LON);
const preLat = parseFloat(window.PRESELECT_LAT);
if (!isNaN(preLon) && !isNaN(preLat) && preLon >= -180 && preLon <= 180 && preLat >= -90 && preLat <= 90 && !(preLon === 0 && preLat === 0)) {
defaultCenter = [preLon, preLat];
defaultZoom = 19;
}
}
const styleUrl = window.MAP_STYLE_LIGHT || 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json';
map = new maplibregl.Map({
@ -304,6 +314,34 @@
map.getCanvas().style.cursor = '';
});
// Prise en compte de la pré-sélection d'un asset (zoom, thématique, catégorie)
if (window.PRESELECT_THEMATIC_CODE) {
const tileBtn = document.querySelector(`.thematic-tile[data-thematic="${window.PRESELECT_THEMATIC_CODE}"]`);
if (tileBtn) {
document.querySelectorAll('.thematic-tile').forEach(t => t.classList.remove('active'));
tileBtn.classList.add('active');
}
}
if (window.PRESELECT_CATEGORY_ID) {
const catSelect = document.getElementById('filter-category-select');
if (catSelect) {
catSelect.value = String(window.PRESELECT_CATEGORY_ID);
}
}
if (window.PRESELECT_LON !== null && window.PRESELECT_LAT !== null && typeof window.PRESELECT_LON !== 'undefined' && typeof window.PRESELECT_LAT !== 'undefined') {
const lon = parseFloat(window.PRESELECT_LON);
const lat = parseFloat(window.PRESELECT_LAT);
if (!isNaN(lon) && !isNaN(lat) && lon >= -180 && lon <= 180 && lat >= -90 && lat <= 90 && !(lon === 0 && lat === 0)) {
map.flyTo({
center: [lon, lat],
zoom: 19,
duration: 1200
});
}
}
// Charger les assets et l'historique d'inspections au premier affichage
loadAssets();
loadInspectionsHistory();
@ -383,8 +421,11 @@
gpsBtn.addEventListener('click', locateUser);
}
const hasPreselect = (window.PRESELECT_LON !== null && window.PRESELECT_LAT !== null && typeof window.PRESELECT_LON !== 'undefined' && typeof window.PRESELECT_LAT !== 'undefined');
if (!hasPreselect) {
locateUser();
}
}
function locateUser() {
if (!navigator.geolocation) return;

View file

@ -10,9 +10,18 @@
{% endif %}
</h5>
{% if can_inspect %}
<a href="{% url 'assets:quick_inspection_map' %}?content_type_id={{ asset_ct_id }}&object_id={{ asset_pk }}" class="btn btn-outline-primary btn-sm">
<button type="button"
class="btn btn-outline-primary btn-sm btn-trigger-asset-inspection"
data-bs-toggle="modal"
data-bs-target="#quickInspectionModal"
data-content-type-id="{{ asset_ct_id }}"
data-object-id="{{ asset_pk }}"
data-target-code="{{ asset.code|default:asset.pk }}"
data-target-name="{{ asset.name_fr|default:asset.name_nl|default:asset }}"
data-target-type="{{ asset.category.name_fr|default:'Asset' }}"
data-target-status="{{ asset.get_status_display|default:asset.inspection_status|default:'-' }}">
<i class="bi bi-plus-lg me-1"></i>{% translate "Réaliser une inspection" %}
</a>
</button>
{% endif %}
</div>
<div class="card-body p-0">
@ -149,3 +158,47 @@
</div>
</div>
{% endfor %}
{% if can_inspect %}
{% include "assets/components/inspection_form_modal.html" %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const triggerBtns = document.querySelectorAll('.btn-trigger-asset-inspection');
triggerBtns.forEach(btn => {
btn.addEventListener('click', function() {
const ctId = this.getAttribute('data-content-type-id');
const objId = this.getAttribute('data-object-id');
const code = this.getAttribute('data-target-code');
const name = this.getAttribute('data-target-name');
const type = this.getAttribute('data-target-type');
const status = this.getAttribute('data-target-status');
const modalCtInput = document.getElementById('insp_content_type_id');
const modalObjInput = document.getElementById('insp_object_id');
const modalTypeCode = document.getElementById('insp_target_code');
const modalTypeName = document.getElementById('insp_target_name');
const modalTypeBadge = document.getElementById('insp_target_type');
const modalStatusBadge = document.getElementById('insp_target_status');
if (modalCtInput) modalCtInput.value = ctId;
if (modalObjInput) modalObjInput.value = objId;
if (modalTypeCode) modalTypeCode.textContent = code;
if (modalTypeName) modalTypeName.textContent = name;
if (modalTypeBadge) modalTypeBadge.textContent = type;
if (modalStatusBadge) modalStatusBadge.textContent = status;
});
});
const form = document.getElementById('quickInspectionForm');
if (form && !form.dataset.reloadHandlerAttached) {
form.dataset.reloadHandlerAttached = 'true';
form.addEventListener('submit', function() {
setTimeout(function() {
window.location.reload();
}, 600);
});
}
});
</script>
{% endif %}

View file

@ -436,6 +436,12 @@
<script>
window.AUTOCOMPLETE_STREET_URL = "{% url 'common:autocomplete_street' %}";
window.PARKING_SPOT_CT_ID = {{ parking_spot_ct_id|default:"null" }};
window.PRESELECT_CONTENT_TYPE_ID = {{ preselect_ct_id|safe }};
window.PRESELECT_OBJECT_ID = {{ preselect_obj_id|safe }};
window.PRESELECT_LON = {{ preselect_lon|safe }};
window.PRESELECT_LAT = {{ preselect_lat|safe }};
window.PRESELECT_THEMATIC_CODE = {{ preselect_thematic_code|safe }};
window.PRESELECT_CATEGORY_ID = {{ preselect_category_id|safe }};
</script>
<script src="{% static 'assets/inspection_map.js' %}"></script>
{% endblock %}

View file

@ -126,6 +126,54 @@ def inspections_list(request):
return render(request, 'assets/inspections_list.html', context)
def _get_asset_wgs84_lon_lat(obj):
if hasattr(obj, 'lon') and hasattr(obj, 'lat') and obj.lon is not None and obj.lat is not None:
try:
lon_f = float(obj.lon)
lat_f = float(obj.lat)
if -180 <= lon_f <= 180 and -90 <= lat_f <= 90 and not (lon_f == 0 and lat_f == 0):
return lon_f, lat_f
except (ValueError, TypeError):
pass
if hasattr(obj, 'geojson') and obj.geojson:
try:
import json
data = json.loads(obj.geojson) if isinstance(obj.geojson, str) else obj.geojson
geom_data = data.get('geometry', {}) if data.get('type') == 'Feature' else data
g_type = geom_data.get('type')
coords = geom_data.get('coordinates')
if g_type == 'Point' and coords:
return float(coords[0]), float(coords[1])
elif g_type == 'Polygon' and coords and len(coords) > 0:
ring = coords[0]
avg_lon = sum(pt[0] for pt in ring) / len(ring)
avg_lat = sum(pt[1] for pt in ring) / len(ring)
return avg_lon, avg_lat
elif g_type == 'MultiPolygon' and coords and len(coords) > 0:
all_pts = [pt for poly in coords for ring in poly for pt in ring]
if all_pts:
avg_lon = sum(pt[0] for pt in all_pts) / len(all_pts)
avg_lat = sum(pt[1] for pt in all_pts) / len(all_pts)
return avg_lon, avg_lat
except Exception:
pass
if hasattr(obj, 'geom') and obj.geom:
try:
g = obj.geom
srid = getattr(g, 'srid', None) or 3812
from django.contrib.gis.geos import GEOSGeometry
g_copy = GEOSGeometry(g.wkt, srid=srid)
g_4326 = g_copy.transform(4326, clone=True)
centroid = g_4326.centroid
return float(centroid.x), float(centroid.y)
except Exception:
pass
return None, None
@login_required
def quick_inspection_map(request):
"""
@ -143,12 +191,43 @@ def quick_inspection_map(request):
from assets.models import ParkingSpot
parking_spot_ct_id = ContentType.objects.get_for_model(ParkingSpot).id
preselect_ct_id = request.GET.get('content_type_id')
preselect_obj_id = request.GET.get('object_id')
preselect_lon = None
preselect_lat = None
preselect_thematic_code = None
preselect_category_id = None
if preselect_ct_id and preselect_obj_id:
try:
ct = ContentType.objects.get(pk=preselect_ct_id)
obj = ct.get_object_for_this_type(pk=preselect_obj_id)
if obj:
from assets.permissions import _get_asset_thematic
th = _get_asset_thematic(obj)
if th:
preselect_thematic_code = th.code
if hasattr(obj, 'category') and obj.category:
preselect_category_id = obj.category.id
preselect_lon, preselect_lat = _get_asset_wgs84_lon_lat(obj)
except Exception:
pass
import json
context = {
'base_template': base_template,
'is_mobile': is_mobile,
'thematics': thematics,
'categories': categories,
'parking_spot_ct_id': parking_spot_ct_id,
'preselect_ct_id': preselect_ct_id or "null",
'preselect_obj_id': preselect_obj_id or "null",
'preselect_lon': json.dumps(preselect_lon) if preselect_lon is not None else "null",
'preselect_lat': json.dumps(preselect_lat) if preselect_lat is not None else "null",
'preselect_thematic_code': json.dumps(preselect_thematic_code) if preselect_thematic_code else "null",
'preselect_category_id': preselect_category_id or "null",
}
return render(request, 'assets/quick_inspection_map.html', context)