loko/streetup/home/static/home/home_index.js
2026-07-22 14:48:40 +02:00

206 lines
No EOL
6.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const map = new maplibregl.Map({
container: "map",
style: window.MAP_STYLE_LIGHT,
//style: 'https://tiles.openfreemap.org/styles/positron',
center: [4.3572, 50.8477],
zoom: 11,
minzoom: 0,
maxzoom: 24
});
async function fetchAndMergeGeoJSON(urls) {
const allFeatures = [];
for (const url of urls) {
const response = await fetch(url);
const data = await response.json();
if (data && data.features) {
allFeatures.push(...data.features);
}
}
return {
type: 'FeatureCollection',
features: allFeatures
};
}
map.on('load', async () => {
const mergedGeoJSON = await fetchAndMergeGeoJSON(interventions_geojson_urls);
map.addSource('interventions', {
'type': 'geojson',
'data': mergedGeoJSON
});
// map.addLayer({
// id: 'heatmap',
// type: 'heatmap',
// source: 'interventions',
// paint: {
// 'heatmap-weight': ['get', 'density'],
// 'heatmap-intensity': 1,
// 'heatmap-color': [
// 'interpolate',
// ['linear'],
// ['heatmap-density'],
// 0, 'rgba(33,102,172,0)',
// 0.2, 'rgb(103,169,207)',
// 0.4, 'rgb(209,229,240)',
// 0.6, 'rgb(253,219,199)',
// 0.8, 'rgb(239,138,98)',
// 1, 'rgb(178,24,43)'
// ],
// 'heatmap-radius': 15,
// 'heatmap-opacity': 0.75
// }
// });
map.addLayer({
'id': 'interventions',
'type': 'circle',
'source': 'interventions',
//filter: ['has', 'point_count'],
minzoom: 0,
maxzoom: 24,
'paint': {
'circle-radius': [
'interpolate',
['linear'],
['zoom'],
10, 5,
22, 15
],
//'circle-color': '#0094ff',
'circle-color': INTERVENTION_STATUS_COLOR_EXPR,
'circle-opacity': 0.7
}
});
});
function statusFormatter(value) {
const icons = {
'in_preparation': { label: gettext('En préparation'), icon: 'bi-pencil', color: 'secondary' },
'to_be_approved': { label: gettext('À approuver'), icon: 'bi-hourglass-split', color: 'warning' },
'to_be_planned': { label: gettext('À planifier'), icon: 'bi-calendar-plus', color: 'warning' },
'to_be_processed': { label: gettext('À traiter'), icon: 'bi-tools', color: 'danger' },
'assigned': { label: gettext('Pris en charge'), icon: 'bi-person-check', color: 'warning' },
'in_progress': { label: gettext('Démarré'), icon: 'bi-play-circle', color: 'primary' },
'on_pause': { label: gettext('En pause'), icon: 'bi-pause-circle', color: 'warning' },
'finished': { label: gettext('Terminé'), icon: 'bi-check-circle', color: 'info' },
'processed': { label: gettext('Traité'), icon: 'bi-check2-all', color: 'info' },
'to_be_corrected': { label: gettext('À corriger'), icon: 'bi-exclamation-circle', color: 'danger' },
'corrected': { label: gettext('Corrigé'), icon: 'bi-check-circle', color: 'info' },
'validated': { label: gettext('Validé'), icon: 'bi-award', color: 'success' },
'invoiced': { label: gettext('Comptabilisé'), icon: 'bi-receipt', color: 'dark' },
'closed': { label: gettext('Finalisé'), icon: 'bi-archive', color: 'dark' },
// 'archived': { label: gettext('Archivé'), icon: 'bi-box-seam', color: 'dark' },
'canceled': { label: gettext('Annulé'), icon: 'bi-x-circle', color: 'secondary' },
};
const entry = icons[value];
if (!entry) return value;
return `<span class="badge text-bg-${entry.color} status-${value}"><i class="bi ${entry.icon} me-2"></i>${entry.label}</span>`;
}
map.on('click', function(e) {
const radiusInPixels = 10; // Rayon autour du clic
const features = map.queryRenderedFeatures([
[e.point.x - radiusInPixels, e.point.y - radiusInPixels],
[e.point.x + radiusInPixels, e.point.y + radiusInPixels]
], {
layers: ['interventions']
});
if (!features.length) {
return;
}
// Générer le contenu de la popup avec tous les points proches
const popupContent = features.map((feature) => {
const props = feature.properties;
return `
<div>
<strong><a href="${interventionDetailUrlPattern.replace('/0/', '/' + props.id + '/')}">${props.code || gettext('Intervention')}</a></strong><br/>
${gettext("Statut")}: ${statusFormatter(props.status)}<br/>
${gettext("Titre")}: ${props.title || '-'}
</div>
<hr/>
`;
}).join('');
// Afficher la popup à l’endroit du clic
new maplibregl.Popup()
.setLngLat(e.lngLat)
.setHTML(popupContent)
.addTo(map);
});
map.on('mouseenter', 'interventions', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'interventions', () => {
map.getCanvas().style.cursor = '';
});
function populateLegend() {
const statuses = ['to_be_processed', 'assigned', 'in_progress', 'on_pause', 'finished'];
const legendContainer = document.getElementById('map-legend');
if (!legendContainer) return;
const legendItems = statuses.map(status => {
return `<div class="mb-1">${statusFormatter(status)}</div>`;
});
legendContainer.innerHTML = `
${legendItems.join('')}
`;
}
document.addEventListener('DOMContentLoaded', populateLegend);
// ### chart ###
const series = [
{
name: gettext('Correctif'),
type: 'line',
smooth: true,
data: seriesData['Correctif'],
itemStyle: { color: '#dc3545' } // Bootstrap "danger"
},
{
name: gettext('Préventif'),
type: 'line',
smooth: true,
data: seriesData['Préventif'],
itemStyle: { color: '#ffc107' } // Bootstrap "warning"
},
{
name: gettext('Amélioratif'),
type: 'line',
smooth: true,
data: seriesData['Amélioratif'],
itemStyle: { color: '#28a745' } // Bootstrap "success"
}
];
const option = {
legend: { data: ['Correctif', 'Préventif', 'Amélioratif'] },
tooltip: { trigger: 'axis' },
xAxis: { type: 'category', data: seriesData.dates },
yAxis: { type: 'value' },
series: series
};
const chartDom = document.getElementById('chartNbrInterventions');
const myChart = echarts.init(chartDom);
myChart.setOption(option);
window.addEventListener('resize', () => myChart.resize());