Files
yann64 e835bab7df Fix paramètres de route utilisateur et z-index carte
- Vues admin/utilisateurs : route() utilisait $user sans clé nommée ;
  Laravel ne résout pas automatiquement un modèle vers un paramètre
  {utilisateur} (nom non-anglais) — remplacé par ['utilisateur' => $user]
  dans edit.blade.php, index.blade.php et UserController::store()
- Carte : ajout de position:relative + z-index:0 sur #carte-map pour
  créer un contexte d'empilement qui confine les z-indexes internes de
  Leaflet (≤800) et laisse le menu (z-index:40) s'afficher par-dessus

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 03:56:41 +02:00

170 lines
8.6 KiB
PHP

<x-app-layout>
<x-slot name="header">
<div class="flex items-center justify-between">
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">Carte des relevés</h2>
<p class="text-sm text-gray-500 dark:text-gray-400" id="carte-stats"></p>
</div>
</x-slot>
@push('head')
{{-- Leaflet (bundlé via Vite) --}}
@vite('resources/js/carte.js')
<style>
#carte-map {
height: calc(100vh - 120px);
min-height: 400px;
position: relative;
z-index: 0; /* crée un contexte d'empilement qui scelle les z-indexes internes de Leaflet */
}
/* Popup dark mode */
.dark .leaflet-popup-content-wrapper,
.dark .leaflet-popup-tip {
background-color: #1f2937;
color: #f3f4f6;
}
.dark .leaflet-popup-content-wrapper {
border: 1px solid #374151;
}
/* Contrôles dark mode */
.dark .leaflet-control-zoom a,
.dark .leaflet-control-attribution {
background-color: #1f2937;
color: #d1d5db;
border-color: #374151;
}
.dark .leaflet-control-zoom a:hover {
background-color: #374151;
}
/* Tuiles légèrement assombries en dark mode */
.dark .leaflet-tile {
filter: brightness(0.7) invert(1) contrast(3) hue-rotate(200deg) saturate(0.3) brightness(0.7);
}
</style>
@endpush
{{-- La carte occupe toute la hauteur disponible --}}
<div id="carte-map" class="w-full"></div>
@push('head')
<script>
document.addEventListener('DOMContentLoaded', function () {
const L = window.LeafletMap;
if (!L) return;
// ── Initialisation de la carte ──────────────────────────────────────
const map = L.map('carte-map', {
center: [46.5, 2.2], // centre de la France
zoom: 6,
zoomControl: true,
});
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
}).addTo(map);
// ── Icône personnalisée ────────────────────────────────────────────
function makeIcon(count) {
const size = count > 10 ? 40 : count > 3 ? 34 : 28;
const color = count > 10 ? '#4f46e5' : count > 3 ? '#6366f1' : '#818cf8';
return L.divIcon({
className: '',
html: `<div style="
width:${size}px; height:${size}px;
background:${color};
border:3px solid white;
border-radius:50%;
box-shadow:0 2px 6px rgba(0,0,0,.35);
display:flex; align-items:center; justify-content:center;
font-size:${size > 30 ? 13 : 11}px;
font-weight:700; color:white; font-family:sans-serif;
">${count}</div>`,
iconSize: [size, size],
iconAnchor: [size / 2, size / 2],
popupAnchor: [0, -(size / 2 + 4)],
});
}
// ── Statut → badge couleur ─────────────────────────────────────────
const statusColors = {
'a_faire': 'background:#f3f4f6;color:#374151',
'en_cours': 'background:#dbeafe;color:#1d4ed8',
'a_valider': 'background:#fef9c3;color:#92400e',
'termine': 'background:#d1fae5;color:#065f46',
};
// ── Chargement des données ─────────────────────────────────────────
fetch('{{ route('carte.data') }}')
.then(r => r.json())
.then(lieux => {
if (lieux.length === 0) {
document.getElementById('carte-stats').textContent =
'Aucun lieu géolocalisé avec des relevés.';
return;
}
const totalReleves = lieux.reduce((s, l) => s + l.releves_count, 0);
document.getElementById('carte-stats').textContent =
`${lieux.length} lieu${lieux.length > 1 ? 'x' : ''} · ${totalReleves.toLocaleString('fr-FR')} relevé${totalReleves > 1 ? 's' : ''}`;
const bounds = [];
lieux.forEach(lieu => {
bounds.push([lieu.lat, lieu.lng]);
// ── Contenu du popup ───────────────────────────────────
const sourcesHtml = lieu.sources.map(s => {
const style = statusColors[s.status_value] || statusColors['a_faire'];
const annees = s.annees ? ` <span style="color:#6b7280;font-size:11px">(${s.annees})</span>` : '';
return `<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;padding:3px 0;border-bottom:1px solid #f3f4f6">
<div style="min-width:0">
<a href="/sources/${s.id}" style="color:#4f46e5;font-size:13px;font-weight:500;text-decoration:none"
onmouseover="this.style.textDecoration='underline'"
onmouseout="this.style.textDecoration='none'">${s.nom}</a>${annees}
</div>
<div style="display:flex;align-items:center;gap:6px;flex-shrink:0">
<span style="font-size:11px;${style};padding:1px 6px;border-radius:9999px;white-space:nowrap">${s.status}</span>
<span style="font-size:12px;color:#6b7280;white-space:nowrap">${s.releves_count.toLocaleString('fr-FR')} rel.</span>
</div>
</div>`;
}).join('');
const popup = `
<div style="min-width:260px;max-width:320px;font-family:sans-serif">
<div style="font-size:15px;font-weight:700;margin-bottom:6px;padding-bottom:6px;border-bottom:2px solid #e5e7eb">
📍 ${lieu.nom}
</div>
<div style="font-size:12px;color:#6b7280;margin-bottom:8px">
${lieu.sources_count} source${lieu.sources_count > 1 ? 's' : ''} ·
<strong>${lieu.releves_count.toLocaleString('fr-FR')}</strong> relevé${lieu.releves_count > 1 ? 's' : ''}
</div>
<div>${sourcesHtml}</div>
<div style="margin-top:8px;text-align:right">
<a href="/recherche?lieu_id=${lieu.id}"
style="font-size:12px;color:#4f46e5;text-decoration:none"
onmouseover="this.style.textDecoration='underline'"
onmouseout="this.style.textDecoration='none'">
Rechercher dans ces relevés →
</a>
</div>
</div>`;
L.marker([lieu.lat, lieu.lng], { icon: makeIcon(lieu.sources_count) })
.addTo(map)
.bindPopup(popup, { maxWidth: 340 });
});
// Ajuster la vue sur tous les marqueurs
if (bounds.length > 0) {
map.fitBounds(bounds, { padding: [40, 40], maxZoom: 12 });
}
})
.catch(() => {
document.getElementById('carte-stats').textContent = 'Erreur lors du chargement des données.';
});
});
</script>
@endpush
</x-app-layout>