feat: add initial view button and no-results zone alert for mobile assets map
This commit is contained in:
parent
27ab3a8c1b
commit
4b1bc59f1e
2 changed files with 233 additions and 41 deletions
|
|
@ -165,6 +165,46 @@
|
||||||
transform: scale(0.92);
|
transform: scale(0.92);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bouton flottant Vue Initiale */
|
||||||
|
.map-initial-view-btn {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 4.5rem;
|
||||||
|
right: 1.25rem;
|
||||||
|
z-index: 10;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
box-shadow: 0 3px 10px rgba(0,0,0,0.25);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--brand-color, #2f3e8b);
|
||||||
|
font-size: 1.15rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.15s ease, background-color 0.15s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
.map-initial-view-btn:active {
|
||||||
|
transform: scale(0.92);
|
||||||
|
}
|
||||||
|
.map-initial-view-btn.highlight-pulse {
|
||||||
|
animation: initial-btn-pulse 1.5s infinite;
|
||||||
|
border-color: #2563eb;
|
||||||
|
color: #2563eb;
|
||||||
|
}
|
||||||
|
@keyframes initial-btn-pulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(37, 99, 235, 0.7);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 0 12px rgba(37, 99, 235, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(37, 99, 235, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Marqueur utilisateur GPS */
|
/* Marqueur utilisateur GPS */
|
||||||
.user-gps-marker {
|
.user-gps-marker {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
|
|
@ -374,6 +414,19 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Alerte Aucun résultat dans la zone visible (invitation retour vue initiale) -->
|
||||||
|
<div id="noResultsInZoneAlert" class="alert alert-info py-2 px-2.5 mb-1.5 small d-flex align-items-center gap-2 rounded-3 shadow-sm d-none flex-shrink-0" role="alert">
|
||||||
|
<i class="bi bi-info-circle-fill fs-5 text-primary flex-shrink-0"></i>
|
||||||
|
<div class="flex-grow-1" style="line-height: 1.3;">
|
||||||
|
<div class="fw-semibold text-dark">{% translate "Aucun résultat dans cette zone" %}</div>
|
||||||
|
<div class="text-secondary small">{% translate "Appuyez sur le bouton pour revenir à la vue initiale." %}</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" id="btnResetToInitialView" class="btn btn-sm btn-primary rounded-3 text-nowrap py-1.5 px-2.5 d-flex align-items-center gap-1 shadow-sm flex-shrink-0">
|
||||||
|
<i class="bi bi-arrows-angle-expand"></i>
|
||||||
|
<span>{% translate "Vue initiale" %}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Indicateur de résultats & compteur -->
|
<!-- Indicateur de résultats & compteur -->
|
||||||
<div class="d-flex justify-content-between align-items-center mb-1.5 px-1 text-secondary small flex-shrink-0">
|
<div class="d-flex justify-content-between align-items-center mb-1.5 px-1 text-secondary small flex-shrink-0">
|
||||||
<div class="d-flex align-items-center gap-1 flex-wrap">
|
<div class="d-flex align-items-center gap-1 flex-wrap">
|
||||||
|
|
@ -407,6 +460,11 @@
|
||||||
<p class="small text-muted mb-0">{% translate "Veuillez choisir une thématique ci-dessus pour afficher les assets sur la carte." %}</p>
|
<p class="small text-muted mb-0">{% translate "Veuillez choisir une thématique ci-dessus pour afficher les assets sur la carte." %}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Bouton flottant Vue initiale -->
|
||||||
|
<button type="button" id="btnInitialView" class="map-initial-view-btn" title="{% translate 'Retour à la vue initiale (toute la commune)' %}" aria-label="{% translate 'Retour à la vue initiale' %}">
|
||||||
|
<i class="bi bi-arrows-angle-expand"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- Bouton flottant GPS -->
|
<!-- Bouton flottant GPS -->
|
||||||
<button type="button" id="btnGpsLocate" class="map-gps-btn" title="{% translate 'Ma position' %}">
|
<button type="button" id="btnGpsLocate" class="map-gps-btn" title="{% translate 'Ma position' %}">
|
||||||
<i class="bi bi-crosshair"></i>
|
<i class="bi bi-crosshair"></i>
|
||||||
|
|
@ -433,10 +491,15 @@
|
||||||
<i class="bi bi-search fs-1"></i>
|
<i class="bi bi-search fs-1"></i>
|
||||||
</div>
|
</div>
|
||||||
<h6 class="fw-bold text-secondary">{% translate "Aucun asset trouvé" %}</h6>
|
<h6 class="fw-bold text-secondary">{% translate "Aucun asset trouvé" %}</h6>
|
||||||
<p class="small text-muted mb-3">{% translate "Modifiez votre recherche ou zoomez sur une autre zone." %}</p>
|
<p class="small text-muted mb-3" id="emptyListSubtitle">{% translate "Modifiez votre recherche ou zoomez sur une autre zone." %}</p>
|
||||||
<button type="button" class="btn btn-outline-primary btn-sm rounded-3" id="btnResetFilters">
|
<div class="d-flex justify-content-center gap-2 flex-wrap">
|
||||||
{% translate "Réinitialiser les filtres" %}
|
<button type="button" class="btn btn-outline-primary btn-sm rounded-3" id="btnResetFilters">
|
||||||
</button>
|
{% translate "Réinitialiser les filtres" %}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm rounded-3 d-none" id="btnListInitialView">
|
||||||
|
<i class="bi bi-arrows-angle-expand me-1"></i>{% translate "Vue initiale" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -520,7 +583,7 @@
|
||||||
let isProgrammaticMove = false;
|
let isProgrammaticMove = false;
|
||||||
let startCenter = null;
|
let startCenter = null;
|
||||||
let startZoom = null;
|
let startZoom = null;
|
||||||
const defaultCenter = window.MAP_DEFAULT_CENTER || [4.3517, 50.8503]; // Bruxelles par défaut
|
const defaultCenter = window.MAP_DEFAULT_CENTER || [4.3572, 50.8477]; // Bruxelles par défaut
|
||||||
const defaultZoom = window.MAP_DEFAULT_ZOOM || 12;
|
const defaultZoom = window.MAP_DEFAULT_ZOOM || 12;
|
||||||
|
|
||||||
let userCoordinates = null; // { lat, lon }
|
let userCoordinates = null; // { lat, lon }
|
||||||
|
|
@ -528,6 +591,7 @@
|
||||||
let userMarker = null;
|
let userMarker = null;
|
||||||
let activeMarkers = [];
|
let activeMarkers = [];
|
||||||
let searchTimeout = null;
|
let searchTimeout = null;
|
||||||
|
let noResultsAlertTimeout = null;
|
||||||
let mapMoveTimeout = null;
|
let mapMoveTimeout = null;
|
||||||
let lastResults = [];
|
let lastResults = [];
|
||||||
|
|
||||||
|
|
@ -547,6 +611,11 @@
|
||||||
const noThematicSelectedMessage = document.getElementById('noThematicSelectedMessage');
|
const noThematicSelectedMessage = document.getElementById('noThematicSelectedMessage');
|
||||||
const mapThematicPrompt = document.getElementById('mapThematicPrompt');
|
const mapThematicPrompt = document.getElementById('mapThematicPrompt');
|
||||||
const filterLimitAlert = document.getElementById('filterLimitAlert');
|
const filterLimitAlert = document.getElementById('filterLimitAlert');
|
||||||
|
const noResultsInZoneAlert = document.getElementById('noResultsInZoneAlert');
|
||||||
|
const btnResetToInitialView = document.getElementById('btnResetToInitialView');
|
||||||
|
const btnInitialView = document.getElementById('btnInitialView');
|
||||||
|
const btnListInitialView = document.getElementById('btnListInitialView');
|
||||||
|
const emptyListSubtitle = document.getElementById('emptyListSubtitle');
|
||||||
const limitNotice = document.getElementById('limitNotice');
|
const limitNotice = document.getElementById('limitNotice');
|
||||||
const zoneIndicator = document.getElementById('zoneIndicator');
|
const zoneIndicator = document.getElementById('zoneIndicator');
|
||||||
const resultsCountEl = document.getElementById('resultsCount');
|
const resultsCountEl = document.getElementById('resultsCount');
|
||||||
|
|
@ -701,6 +770,10 @@
|
||||||
closeBottomSheet();
|
closeBottomSheet();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
map.on('movestart', () => {
|
||||||
|
hideNoResultsInZoneAlert();
|
||||||
|
});
|
||||||
|
|
||||||
// Détection du déplacement ou zoom pour filtrage par zone visible (bbox) et synchronisation de la liste
|
// Détection du déplacement ou zoom pour filtrage par zone visible (bbox) et synchronisation de la liste
|
||||||
map.on('moveend', () => {
|
map.on('moveend', () => {
|
||||||
// Ignorer si la vue active n'est pas la carte ou si aucune thématique n'est choisie
|
// Ignorer si la vue active n'est pas la carte ou si aucune thématique n'est choisie
|
||||||
|
|
@ -777,12 +850,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Récupération des données depuis le backend
|
// 3. Récupération des données depuis le backend
|
||||||
function fetchAndRenderAssets(fromMapMove = false) {
|
function fetchAndRenderAssets(fromMapMove = false, options = {}) {
|
||||||
// L'utilisateur doit choisir au minimum une thématique
|
// L'utilisateur doit choisir au minimum une thématique
|
||||||
if (!currentThematic) {
|
if (!currentThematic) {
|
||||||
loadingSpinner.classList.add('d-none');
|
loadingSpinner.classList.add('d-none');
|
||||||
if (filterLimitAlert) filterLimitAlert.classList.add('d-none');
|
if (filterLimitAlert) filterLimitAlert.classList.add('d-none');
|
||||||
if (limitNotice) limitNotice.classList.add('d-none');
|
if (limitNotice) limitNotice.classList.add('d-none');
|
||||||
|
hideNoResultsInZoneAlert();
|
||||||
resultsCountEl.textContent = '0';
|
resultsCountEl.textContent = '0';
|
||||||
if (zoneIndicator) zoneIndicator.classList.add('d-none');
|
if (zoneIndicator) zoneIndicator.classList.add('d-none');
|
||||||
if (mapThematicPrompt) mapThematicPrompt.classList.remove('d-none');
|
if (mapThematicPrompt) mapThematicPrompt.classList.remove('d-none');
|
||||||
|
|
@ -819,7 +893,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!bboxToSend && currentView === 'map') {
|
if (!bboxToSend && currentView === 'map' && isBboxFiltered) {
|
||||||
bboxToSend = getValidMapBbox();
|
bboxToSend = getValidMapBbox();
|
||||||
if (bboxToSend) {
|
if (bboxToSend) {
|
||||||
currentBbox = bboxToSend;
|
currentBbox = bboxToSend;
|
||||||
|
|
@ -845,7 +919,7 @@
|
||||||
})
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
loadingSpinner.classList.add('d-none');
|
loadingSpinner.classList.add('d-none');
|
||||||
renderResults(data, fromMapMove);
|
renderResults(data, fromMapMove, options);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error("Erreur chargement assets:", err);
|
console.error("Erreur chargement assets:", err);
|
||||||
|
|
@ -854,7 +928,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Rendu des résultats sur Carte et dans la Liste
|
// 4. Rendu des résultats sur Carte et dans la Liste
|
||||||
function renderResults(data, fromMapMove = false) {
|
function renderResults(data, fromMapMove = false, options = {}) {
|
||||||
lastResults = data.results || [];
|
lastResults = data.results || [];
|
||||||
|
|
||||||
// Gestion de la limite de 500 assets atteinte
|
// Gestion de la limite de 500 assets atteinte
|
||||||
|
|
@ -871,14 +945,30 @@
|
||||||
// Synchronisation de la Liste (toujours à jour avec la zone zoomée ou les filtres)
|
// Synchronisation de la Liste (toujours à jour avec la zone zoomée ou les filtres)
|
||||||
renderListView(lastResults);
|
renderListView(lastResults);
|
||||||
|
|
||||||
// Rendu Carte (mise à jour des marqueurs)
|
// Rendu Carte (mise à jour des marqueurs, sans modifier la visualisation)
|
||||||
renderMapView(lastResults, fromMapMove);
|
renderMapView(lastResults, fromMapMove);
|
||||||
|
|
||||||
|
// Gestion du message invitant à revenir à la vue initiale en cas de recherche sans résultat
|
||||||
|
handleNoResultsInZoneAlert(data, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderListView(results) {
|
function renderListView(results) {
|
||||||
assetsListContainer.innerHTML = '';
|
assetsListContainer.innerHTML = '';
|
||||||
if (!results || results.length === 0) {
|
if (!results || results.length === 0) {
|
||||||
emptyListMessage.classList.remove('d-none');
|
emptyListMessage.classList.remove('d-none');
|
||||||
|
if (btnListInitialView) {
|
||||||
|
if (currentQuery && !isAtMaxBbox()) {
|
||||||
|
btnListInitialView.classList.remove('d-none');
|
||||||
|
if (emptyListSubtitle) {
|
||||||
|
emptyListSubtitle.textContent = "{% translate 'Aucun asset trouvé dans cette zone pour votre recherche.' %}";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
btnListInitialView.classList.add('d-none');
|
||||||
|
if (emptyListSubtitle) {
|
||||||
|
emptyListSubtitle.textContent = "{% translate 'Modifiez votre recherche ou zoomez sur une autre zone.' %}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emptyListMessage.classList.add('d-none');
|
emptyListMessage.classList.add('d-none');
|
||||||
|
|
@ -921,9 +1011,6 @@
|
||||||
activeMarkers.forEach(m => m.remove());
|
activeMarkers.forEach(m => m.remove());
|
||||||
activeMarkers = [];
|
activeMarkers = [];
|
||||||
|
|
||||||
const bounds = new maplibregl.LngLatBounds();
|
|
||||||
let hasValidCoords = false;
|
|
||||||
|
|
||||||
results.forEach(asset => {
|
results.forEach(asset => {
|
||||||
if (asset.lon === null || asset.lat === null) return;
|
if (asset.lon === null || asset.lat === null) return;
|
||||||
|
|
||||||
|
|
@ -944,15 +1031,9 @@
|
||||||
.addTo(map);
|
.addTo(map);
|
||||||
|
|
||||||
activeMarkers.push(marker);
|
activeMarkers.push(marker);
|
||||||
bounds.extend([asset.lon, asset.lat]);
|
|
||||||
hasValidCoords = true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ajuster la vue uniquement s'il n'y a pas de position sauvegardée ni filtrage spatial actif
|
// Ne jamais modifier la visualisation (zoom ou pan) de manière automatique lors d'une action de filtre ou de recherche
|
||||||
if (hasValidCoords && !userCoordinates && !fromMapMove && !hasSavedPosition && !isBboxFiltered) {
|
|
||||||
isProgrammaticMove = true;
|
|
||||||
map.fitBounds(bounds, { padding: 40, maxZoom: 16 });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Volet d'aperçu inférieur (Bottom Sheet)
|
// 5. Volet d'aperçu inférieur (Bottom Sheet)
|
||||||
|
|
@ -1076,6 +1157,7 @@
|
||||||
zoneIndicator.classList.add('d-none');
|
zoneIndicator.classList.add('d-none');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hideNoResultsInZoneAlert();
|
||||||
closeBottomSheet();
|
closeBottomSheet();
|
||||||
|
|
||||||
// Supprimer la persistance localStorage
|
// Supprimer la persistance localStorage
|
||||||
|
|
@ -1107,39 +1189,141 @@
|
||||||
fetchAndRenderAssets(false);
|
fetchAndRenderAssets(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vérifier si la vue courante correspond à la boîte englobante maximale (vue initiale)
|
||||||
|
function isAtMaxBbox() {
|
||||||
|
if (!map) return true;
|
||||||
|
const currentZoom = map.getZoom();
|
||||||
|
const currentCenter = map.getCenter();
|
||||||
|
|
||||||
|
const dLng = Math.abs(currentCenter.lng - defaultCenter[0]);
|
||||||
|
const dLat = Math.abs(currentCenter.lat - defaultCenter[1]);
|
||||||
|
const zoomDiff = currentZoom - defaultZoom;
|
||||||
|
|
||||||
|
// Si le zoom est plus grand que le zoom initial (+0.5) ou si le centre s'est décalé (> 0.02 deg ~ 1.5 km)
|
||||||
|
if (zoomDiff > 0.5 || dLng > 0.02 || dLat > 0.02) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si isBboxFiltered est actif et que la position/zoom a bougé
|
||||||
|
if (isBboxFiltered && (zoomDiff > 0.25 || dLng > 0.015 || dLat > 0.015)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showNoResultsInZoneAlert() {
|
||||||
|
if (noResultsInZoneAlert) {
|
||||||
|
noResultsInZoneAlert.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
if (btnInitialView) {
|
||||||
|
btnInitialView.classList.add('highlight-pulse');
|
||||||
|
}
|
||||||
|
if (btnListInitialView) {
|
||||||
|
btnListInitialView.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideNoResultsInZoneAlert() {
|
||||||
|
clearTimeout(noResultsAlertTimeout);
|
||||||
|
if (noResultsInZoneAlert) {
|
||||||
|
noResultsInZoneAlert.classList.add('d-none');
|
||||||
|
}
|
||||||
|
if (btnInitialView) {
|
||||||
|
btnInitialView.classList.remove('highlight-pulse');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNoResultsInZoneAlert(data, options = {}) {
|
||||||
|
clearTimeout(noResultsAlertTimeout);
|
||||||
|
|
||||||
|
const count = data.count || (data.results ? data.results.length : 0);
|
||||||
|
const hasQuery = Boolean(currentQuery && currentQuery.trim().length > 0);
|
||||||
|
|
||||||
|
// Si la recherche a retourné des résultats ou si aucune recherche textuelle n'est active, masquer l'alerte
|
||||||
|
if (!hasQuery || count > 0) {
|
||||||
|
hideNoResultsInZoneAlert();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si la vue correspond déjà à la bbox maximale, ne pas afficher l'alerte (recherche déjà globale)
|
||||||
|
if (isAtMaxBbox()) {
|
||||||
|
hideNoResultsInZoneAlert();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recherche sans résultat ET vue restreinte à une zone inférieure à la bbox maximale :
|
||||||
|
if (options.fromSearchTyping) {
|
||||||
|
// Petite latence pour lui permettre d'entrer suffisamment de caractères pour sa recherche
|
||||||
|
if (currentQuery.trim().length < 2) {
|
||||||
|
hideNoResultsInZoneAlert();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
noResultsAlertTimeout = setTimeout(() => {
|
||||||
|
if (currentQuery && currentQuery.trim().length >= 2 && lastResults.length === 0 && !isAtMaxBbox()) {
|
||||||
|
showNoResultsInZoneAlert();
|
||||||
|
}
|
||||||
|
}, 850);
|
||||||
|
} else {
|
||||||
|
// Soumission explicite (touche Entrée ou clic loupe)
|
||||||
|
showNoResultsInZoneAlert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retour à la vue initiale (toute la commune) tout en conservant la recherche et la thématique
|
||||||
|
function returnToInitialView() {
|
||||||
|
isBboxFiltered = false;
|
||||||
|
currentBbox = null;
|
||||||
|
hasSavedPosition = false;
|
||||||
|
if (zoneIndicator) zoneIndicator.classList.add('d-none');
|
||||||
|
hideNoResultsInZoneAlert();
|
||||||
|
|
||||||
|
if (map) {
|
||||||
|
isProgrammaticMove = true;
|
||||||
|
map.flyTo({
|
||||||
|
center: defaultCenter,
|
||||||
|
zoom: defaultZoom,
|
||||||
|
essential: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
saveState();
|
||||||
|
fetchAndRenderAssets(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (btnGlobalReset) {
|
if (btnGlobalReset) {
|
||||||
btnGlobalReset.addEventListener('click', resetAll);
|
btnGlobalReset.addEventListener('click', resetAll);
|
||||||
}
|
}
|
||||||
if (btnResetFilters) {
|
if (btnResetFilters) {
|
||||||
btnResetFilters.addEventListener('click', resetAll);
|
btnResetFilters.addEventListener('click', resetAll);
|
||||||
}
|
}
|
||||||
|
if (btnResetToInitialView) {
|
||||||
|
btnResetToInitialView.addEventListener('click', returnToInitialView);
|
||||||
|
}
|
||||||
|
if (btnInitialView) {
|
||||||
|
btnInitialView.addEventListener('click', returnToInitialView);
|
||||||
|
}
|
||||||
|
if (btnListInitialView) {
|
||||||
|
btnListInitialView.addEventListener('click', returnToInitialView);
|
||||||
|
}
|
||||||
|
|
||||||
// Clic sur l'indicateur de zone : élargir à toute la commune sans réinitialiser la recherche
|
// Clic sur l'indicateur de zone : élargir à toute la commune sans réinitialiser la recherche
|
||||||
if (zoneIndicator) {
|
if (zoneIndicator) {
|
||||||
zoneIndicator.addEventListener('click', () => {
|
zoneIndicator.addEventListener('click', returnToInitialView);
|
||||||
isBboxFiltered = false;
|
|
||||||
currentBbox = null;
|
|
||||||
zoneIndicator.classList.add('d-none');
|
|
||||||
hasSavedPosition = false;
|
|
||||||
saveState();
|
|
||||||
fetchAndRenderAssets(false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. Filtres et événements
|
// 8. Filtres et événements
|
||||||
// Chips thématiques (au moins une thématique obligatoire)
|
// Chips thématiques (au moins une thématique obligatoire) : ne pas modifier la visualisation (zoom ou pan)
|
||||||
thematicChips.forEach(chip => {
|
thematicChips.forEach(chip => {
|
||||||
chip.addEventListener('click', () => {
|
chip.addEventListener('click', () => {
|
||||||
thematicChips.forEach(c => c.classList.remove('active'));
|
thematicChips.forEach(c => c.classList.remove('active'));
|
||||||
chip.classList.add('active');
|
chip.classList.add('active');
|
||||||
currentThematic = chip.dataset.thematic || '';
|
currentThematic = chip.dataset.thematic || '';
|
||||||
closeBottomSheet();
|
closeBottomSheet();
|
||||||
isBboxFiltered = false;
|
hideNoResultsInZoneAlert();
|
||||||
currentBbox = null;
|
|
||||||
hasSavedPosition = false;
|
|
||||||
if (zoneIndicator) zoneIndicator.classList.add('d-none');
|
|
||||||
saveState();
|
saveState();
|
||||||
fetchAndRenderAssets(false);
|
// Conserver la boîte englobante si un filtrage spatial est actif, sans modifier le zoom ou pan
|
||||||
|
fetchAndRenderAssets(isBboxFiltered);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1147,11 +1331,12 @@
|
||||||
statusSelect.addEventListener('change', () => {
|
statusSelect.addEventListener('change', () => {
|
||||||
currentStatus = statusSelect.value;
|
currentStatus = statusSelect.value;
|
||||||
closeBottomSheet();
|
closeBottomSheet();
|
||||||
|
hideNoResultsInZoneAlert();
|
||||||
saveState();
|
saveState();
|
||||||
fetchAndRenderAssets(false);
|
fetchAndRenderAssets(isBboxFiltered);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Champ de recherche
|
// Champ de recherche : ne pas modifier la visualisation (zoom ou pan)
|
||||||
function handleSearchInput() {
|
function handleSearchInput() {
|
||||||
currentQuery = searchInput.value.trim();
|
currentQuery = searchInput.value.trim();
|
||||||
if (currentQuery) {
|
if (currentQuery) {
|
||||||
|
|
@ -1159,11 +1344,12 @@
|
||||||
} else {
|
} else {
|
||||||
clearSearchBtn.classList.add('d-none');
|
clearSearchBtn.classList.add('d-none');
|
||||||
}
|
}
|
||||||
|
hideNoResultsInZoneAlert();
|
||||||
saveState();
|
saveState();
|
||||||
clearTimeout(searchTimeout);
|
clearTimeout(searchTimeout);
|
||||||
searchTimeout = setTimeout(() => {
|
searchTimeout = setTimeout(() => {
|
||||||
closeBottomSheet();
|
closeBottomSheet();
|
||||||
fetchAndRenderAssets(false);
|
fetchAndRenderAssets(isBboxFiltered, { fromSearchTyping: true });
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1174,7 +1360,7 @@
|
||||||
clearTimeout(searchTimeout);
|
clearTimeout(searchTimeout);
|
||||||
closeBottomSheet();
|
closeBottomSheet();
|
||||||
saveState();
|
saveState();
|
||||||
fetchAndRenderAssets(false);
|
fetchAndRenderAssets(isBboxFiltered, { isExplicitSubmit: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1182,16 +1368,17 @@
|
||||||
searchInput.value = '';
|
searchInput.value = '';
|
||||||
currentQuery = '';
|
currentQuery = '';
|
||||||
clearSearchBtn.classList.add('d-none');
|
clearSearchBtn.classList.add('d-none');
|
||||||
|
hideNoResultsInZoneAlert();
|
||||||
closeBottomSheet();
|
closeBottomSheet();
|
||||||
saveState();
|
saveState();
|
||||||
fetchAndRenderAssets(false);
|
fetchAndRenderAssets(isBboxFiltered);
|
||||||
});
|
});
|
||||||
|
|
||||||
submitSearchBtn.addEventListener('click', () => {
|
submitSearchBtn.addEventListener('click', () => {
|
||||||
clearTimeout(searchTimeout);
|
clearTimeout(searchTimeout);
|
||||||
closeBottomSheet();
|
closeBottomSheet();
|
||||||
saveState();
|
saveState();
|
||||||
fetchAndRenderAssets(false);
|
fetchAndRenderAssets(isBboxFiltered, { isExplicitSubmit: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Boutons Vue Carte / Liste
|
// Boutons Vue Carte / Liste
|
||||||
|
|
|
||||||
|
|
@ -274,9 +274,14 @@ class MobileAssetDetailTests(TestCase):
|
||||||
self.assertIn("accessible_thematics", response.context)
|
self.assertIn("accessible_thematics", response.context)
|
||||||
self.assertIn("thematic_config_json", response.context)
|
self.assertIn("thematic_config_json", response.context)
|
||||||
self.assertIn("asset_status_choices", response.context)
|
self.assertIn("asset_status_choices", response.context)
|
||||||
# Assert JS script block is rendered and loaded
|
# Assert JS script block and initial view controls are rendered
|
||||||
self.assertContains(response, "initMap")
|
self.assertContains(response, "initMap")
|
||||||
self.assertContains(response, "maplibre-gl.js")
|
self.assertContains(response, "maplibre-gl.js")
|
||||||
|
self.assertContains(response, "noResultsInZoneAlert")
|
||||||
|
self.assertContains(response, "btnResetToInitialView")
|
||||||
|
self.assertContains(response, "btnInitialView")
|
||||||
|
self.assertContains(response, "returnToInitialView")
|
||||||
|
self.assertContains(response, "handleNoResultsInZoneAlert")
|
||||||
|
|
||||||
def test_mobile_assets_search_api(self):
|
def test_mobile_assets_search_api(self):
|
||||||
"""Test mobile_assets_search JSON endpoint with filters and search queries."""
|
"""Test mobile_assets_search JSON endpoint with filters and search queries."""
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue