mirror of
				https://forge.chapril.org/tykayn/osm-commerces
				synced 2025-10-09 17:02:46 +02:00 
			
		
		
		
	up
This commit is contained in:
		
							parent
							
								
									a17fd9c232
								
							
						
					
					
						commit
						8bce5fe21c
					
				
					 14 changed files with 628 additions and 634 deletions
				
			
		
							
								
								
									
										408
									
								
								assets/app.js
									
										
									
									
									
								
							
							
						
						
									
										408
									
								
								assets/app.js
									
										
									
									
									
								
							|  | @ -7,8 +7,10 @@ | ||||||
| 
 | 
 | ||||||
| // any CSS you import will output into a single css file (app.css in this case)
 | // any CSS you import will output into a single css file (app.css in this case)
 | ||||||
| import './styles/app.css'; | import './styles/app.css'; | ||||||
|  | import './utils.js'; | ||||||
|  | import './opening_hours.js'; | ||||||
|  | import './josm.js'; | ||||||
| 
 | 
 | ||||||
| console.log('Hello World de app.js'); |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| // Attendre le chargement du DOM
 | // Attendre le chargement du DOM
 | ||||||
|  | @ -16,17 +18,8 @@ document.addEventListener('DOMContentLoaded', () => { | ||||||
|   console.log('DOMContentLoaded'); |   console.log('DOMContentLoaded'); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|   function updateMapHeightForLargeScreens() { |  | ||||||
| 
 | 
 | ||||||
|     const map = document.querySelector('#map'); | 
 | ||||||
|     // Vérifier si on est sur une page avec un titre h1 contenant "statistiques"
 |  | ||||||
|     const h1Element = document.querySelector('h1'); |  | ||||||
|     if (h1Element && h1Element.textContent.toLowerCase().includes('statistiques')) { |  | ||||||
|       if (map && window.innerHeight > 800 && window.innerWidth > 800) { |  | ||||||
|         map.style.height = '80vh'; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   updateMapHeightForLargeScreens() |   updateMapHeightForLargeScreens() | ||||||
| 
 | 
 | ||||||
|  | @ -78,11 +71,6 @@ document.addEventListener('DOMContentLoaded', () => { | ||||||
|   const randombg = genererCouleurPastel(); |   const randombg = genererCouleurPastel(); | ||||||
|   // Appliquer la couleur au body
 |   // Appliquer la couleur au body
 | ||||||
|   document.body.style.backgroundColor = randombg; |   document.body.style.backgroundColor = randombg; | ||||||
|   // Appliquer le style pour .body-landing
 |  | ||||||
|   const bodyLanding = document.querySelector('.body-landing'); |  | ||||||
|   if (bodyLanding) { |  | ||||||
|     bodyLanding.style.backgroundColor = randombg; |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   // Gestion du bouton pour afficher tous les champs
 |   // Gestion du bouton pour afficher tous les champs
 | ||||||
|   const btnShowAllFields = document.querySelector('#showAllFields'); |   const btnShowAllFields = document.querySelector('#showAllFields'); | ||||||
|  | @ -110,394 +98,6 @@ document.addEventListener('DOMContentLoaded', () => { | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|   const openingHoursFormManager = { |  | ||||||
|     defaultOpeningHours: '', |  | ||||||
|     init: function () { |  | ||||||
|       // Rechercher l'élément par son attribut name plutôt que par son id
 |  | ||||||
|       const openingHoursInput = document.querySelector('input[name="custom__opening_hours"]'); |  | ||||||
|       if (!openingHoursInput) { |  | ||||||
|         console.warn('Élément input[name="custom__opening_hours"] non trouvé'); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       this.defaultOpeningHours = openingHoursInput.value; |  | ||||||
| 
 |  | ||||||
|       this.makeForm(); |  | ||||||
| 
 |  | ||||||
|       if (this.defaultOpeningHours !== '') { |  | ||||||
|         this.parseOpeningHoursValue(); |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     parseOpeningHoursValue: function () { |  | ||||||
|       // Analyser la chaîne d'horaires d'ouverture
 |  | ||||||
|       const parsedOpeningHours = {}; |  | ||||||
| 
 |  | ||||||
|       // Masquer toutes les plages horaires par défaut
 |  | ||||||
|       const allDayContainers = document.querySelectorAll('.jour-container'); |  | ||||||
|       allDayContainers.forEach(container => { |  | ||||||
|         const checkbox = container.querySelector('input[type="checkbox"]'); |  | ||||||
|         const horairesContainer = container.querySelector('.horaires-container'); |  | ||||||
|         checkbox.checked = false; |  | ||||||
|         horairesContainer.classList.add('d-none'); |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       if (this.defaultOpeningHours) { |  | ||||||
|         // Diviser les différentes règles (séparées par des points-virgules)
 |  | ||||||
|         const rules = this.defaultOpeningHours.split(';').map(r => r.trim()); |  | ||||||
| 
 |  | ||||||
|         rules.forEach(rule => { |  | ||||||
|           // Extraire les jours et les heures
 |  | ||||||
|           const [days, hours] = rule.split(' ').filter(Boolean); |  | ||||||
| 
 |  | ||||||
|           // Convertir les jours en français
 |  | ||||||
|           const daysMap = { |  | ||||||
|             'Mo': 'lundi', |  | ||||||
|             'Tu': 'mardi', |  | ||||||
|             'We': 'mercredi', |  | ||||||
|             'Th': 'jeudi', |  | ||||||
|             'Fr': 'vendredi', |  | ||||||
|             'Sa': 'samedi', |  | ||||||
|             'Su': 'dimanche' |  | ||||||
|           }; |  | ||||||
| 
 |  | ||||||
|           // Gérer les plages de jours (ex: Mo-Fr)
 |  | ||||||
|           if (days.includes('-')) { |  | ||||||
|             const [start, end] = days.split('-'); |  | ||||||
|             const startIndex = Object.keys(daysMap).indexOf(start); |  | ||||||
|             const endIndex = Object.keys(daysMap).indexOf(end); |  | ||||||
| 
 |  | ||||||
|             for (let i = startIndex; i <= endIndex; i++) { |  | ||||||
|               const day = daysMap[Object.keys(daysMap)[i]]; |  | ||||||
|               // Cocher la case du jour
 |  | ||||||
|               const checkbox = document.querySelector(`#jour-${day}`); |  | ||||||
|               if (checkbox) { |  | ||||||
|                 checkbox.checked = true; |  | ||||||
|                 checkbox.closest('.jour-container').querySelector('.horaires-container').classList.remove('d-none'); |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|           } else { |  | ||||||
|             // Jour unique
 |  | ||||||
|             const day = daysMap[days]; |  | ||||||
|             const checkbox = document.querySelector(`#jour-${day}`); |  | ||||||
|             if (checkbox) { |  | ||||||
|               checkbox.checked = true; |  | ||||||
|               checkbox.closest('.jour-container').querySelector('.horaires-container').classList.remove('d-none'); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       console.log(parsedOpeningHours); |  | ||||||
|     }, |  | ||||||
|     makeForm: function () { |  | ||||||
|       const customOpeningHours = document.querySelector('input[name="custom__opening_hours"]'); |  | ||||||
|       console.log('makeForm customOpeningHours', customOpeningHours); |  | ||||||
| 
 |  | ||||||
|       if (customOpeningHours) { |  | ||||||
|         // Créer un conteneur flex pour aligner l'input et le formulaire
 |  | ||||||
|         const container = document.createElement('div'); |  | ||||||
|         container.classList.add('d-flex', 'flex-column', 'flex-md-row', 'align-items-start', 'gap-3', 'w-100'); |  | ||||||
| 
 |  | ||||||
|         // Créer le formulaire
 |  | ||||||
|         const form = document.createElement('form'); |  | ||||||
|         form.id = 'app_public_opening_hours'; |  | ||||||
|         form.classList.add('mt-3', 'flex-grow-1'); |  | ||||||
| 
 |  | ||||||
|         // Créer les cases à cocher pour chaque jour
 |  | ||||||
|         const jours = ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche']; |  | ||||||
|         const joursDiv = document.createElement('div'); |  | ||||||
|         joursDiv.classList.add('jours-ouverture', 'mb-4', 'row', 'g-3', 'mx-4'); |  | ||||||
| 
 |  | ||||||
|         jours.forEach(jour => { |  | ||||||
|           const jourContainer = document.createElement('div'); |  | ||||||
|           jourContainer.classList.add('jour-container', 'col-12'); |  | ||||||
| 
 |  | ||||||
|           // Checkbox pour le jour
 |  | ||||||
|           const divCheck = document.createElement('div'); |  | ||||||
|           divCheck.classList.add('form-check', 'mb-2'); |  | ||||||
| 
 |  | ||||||
|           const input = document.createElement('input'); |  | ||||||
|           input.type = 'checkbox'; |  | ||||||
|           input.id = `jour-${jour.toLowerCase()}`; |  | ||||||
|           input.name = `jour-${jour.toLowerCase()}`; |  | ||||||
|           input.classList.add('form-check-input'); |  | ||||||
| 
 |  | ||||||
|           const label = document.createElement('label'); |  | ||||||
|           label.htmlFor = `jour-${jour.toLowerCase()}`; |  | ||||||
|           label.classList.add('form-check-label'); |  | ||||||
|           label.textContent = jour; |  | ||||||
| 
 |  | ||||||
|           divCheck.appendChild(input); |  | ||||||
| 
 |  | ||||||
|           divCheck.appendChild(label); |  | ||||||
|           jourContainer.appendChild(divCheck); |  | ||||||
| 
 |  | ||||||
|           // Conteneur pour les plages horaires
 |  | ||||||
|           const horairesContainer = document.createElement('div'); |  | ||||||
|           horairesContainer.classList.add('horaires-container', 'ms-4', 'd-none'); |  | ||||||
| 
 |  | ||||||
|           // Première plage horaire
 |  | ||||||
|           const plage1 = this.createTimeRangeInputs(`${jour.toLowerCase()}-plage1`); |  | ||||||
|           horairesContainer.appendChild(plage1); |  | ||||||
| 
 |  | ||||||
|           // Séparateur
 |  | ||||||
|           const separator = document.createElement('div'); |  | ||||||
|           separator.classList.add('text-center', 'my-2'); |  | ||||||
|           separator.textContent = 'et'; |  | ||||||
|           horairesContainer.appendChild(separator); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|           // Deuxième plage horaire
 |  | ||||||
|           const plage2 = this.createTimeRangeInputs(`${jour.toLowerCase()}-plage2`); |  | ||||||
|           horairesContainer.appendChild(plage2); |  | ||||||
| 
 |  | ||||||
|           jourContainer.appendChild(horairesContainer); |  | ||||||
|           joursDiv.appendChild(jourContainer); |  | ||||||
| 
 |  | ||||||
|           // Ajouter l'événement pour afficher/masquer les plages horaires
 |  | ||||||
|           input.addEventListener('change', (e) => { |  | ||||||
|             horairesContainer.classList.toggle('d-none', !e.target.checked); |  | ||||||
|             this.convertToOSMOpeningHours(); |  | ||||||
|           }); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         form.appendChild(joursDiv); |  | ||||||
| 
 |  | ||||||
|         // Ajouter le formulaire au conteneur
 |  | ||||||
|         container.appendChild(form); |  | ||||||
| 
 |  | ||||||
|         // Insérer le conteneur après l'input original
 |  | ||||||
|         const parent = customOpeningHours.parentNode; |  | ||||||
|         if (parent) { |  | ||||||
|           parent.insertBefore(container, customOpeningHours.nextSibling); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Ajouter un debounce pour limiter les appels lors des modifications
 |  | ||||||
|         const debounce = (func, wait) => { |  | ||||||
|           let timeout; |  | ||||||
|           return function executedFunction(...args) { |  | ||||||
|             const later = () => { |  | ||||||
|               clearTimeout(timeout); |  | ||||||
|               func(...args); |  | ||||||
|             }; |  | ||||||
|             clearTimeout(timeout); |  | ||||||
|             timeout = setTimeout(later, wait); |  | ||||||
|           }; |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         // Appliquer le debounce à la fonction de conversion
 |  | ||||||
|         const debouncedConvert = debounce(() => { |  | ||||||
|           this.convertToOSMOpeningHours(); |  | ||||||
|         }, 300); |  | ||||||
| 
 |  | ||||||
|         // Ajouter les listeners sur tous les inputs
 |  | ||||||
|         const allInputs = form.querySelectorAll('input'); |  | ||||||
|         allInputs.forEach(input => { |  | ||||||
|           input.addEventListener('change', debouncedConvert); |  | ||||||
|           input.addEventListener('input', debouncedConvert); |  | ||||||
|         }); |  | ||||||
|       } else { |  | ||||||
|         console.log('pas d input opening hours détecté') |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
| 
 |  | ||||||
|     createTimeRangeInputs: function (prefix) { |  | ||||||
|       const container = document.createElement('div'); |  | ||||||
|       container.classList.add('time-range', 'mb-2'); |  | ||||||
| 
 |  | ||||||
|       // Case à cocher pour activer la plage
 |  | ||||||
|       const checkboxContainer = document.createElement('div'); |  | ||||||
|       checkboxContainer.classList.add('form-check', 'mb-2'); |  | ||||||
| 
 |  | ||||||
|       const checkbox = document.createElement('input'); |  | ||||||
|       checkbox.type = 'checkbox'; |  | ||||||
|       checkbox.id = `${prefix}-active`; |  | ||||||
|       checkbox.name = `${prefix}-active`; |  | ||||||
|       checkbox.classList.add('form-check-input'); |  | ||||||
|       checkbox.checked = true; |  | ||||||
| 
 |  | ||||||
|       const checkboxLabel = document.createElement('label'); |  | ||||||
|       checkboxLabel.htmlFor = `${prefix}-active`; |  | ||||||
|       checkboxLabel.classList.add('form-check-label'); |  | ||||||
|       checkboxLabel.textContent = ''; |  | ||||||
| 
 |  | ||||||
|       checkboxContainer.appendChild(checkbox); |  | ||||||
|       checkboxContainer.appendChild(checkboxLabel); |  | ||||||
|       container.appendChild(checkboxContainer); |  | ||||||
| 
 |  | ||||||
|       // Conteneur pour les inputs d'horaires
 |  | ||||||
|       const timeContainer = document.createElement('div'); |  | ||||||
|       timeContainer.classList.add('ms-4', 'row', 'g-2'); |  | ||||||
| 
 |  | ||||||
|       // Heure de début
 |  | ||||||
|       const startContainer = document.createElement('div'); |  | ||||||
|       startContainer.classList.add('col-6', 'd-flex', 'align-items-center', 'gap-2'); |  | ||||||
| 
 |  | ||||||
|       const startHour = document.createElement('input'); |  | ||||||
|       startHour.type = 'number'; |  | ||||||
|       startHour.min = '0'; |  | ||||||
|       startHour.max = '23'; |  | ||||||
|       startHour.classList.add('form-control', 'form-control-sm'); |  | ||||||
|       startHour.style.width = '60px'; |  | ||||||
|       startHour.placeholder = 'HH'; |  | ||||||
|       startHour.name = `${prefix}-start-hour`; |  | ||||||
|       // Définir les horaires par défaut selon la plage
 |  | ||||||
|       startHour.value = prefix.includes('plage1') ? '08' : '14'; |  | ||||||
| 
 |  | ||||||
|       const startMinute = document.createElement('input'); |  | ||||||
|       startMinute.type = 'number'; |  | ||||||
|       startMinute.min = '0'; |  | ||||||
|       startMinute.max = '59'; |  | ||||||
|       startMinute.classList.add('form-control', 'form-control-sm'); |  | ||||||
|       startMinute.style.width = '60px'; |  | ||||||
|       startMinute.placeholder = 'MM'; |  | ||||||
|       startMinute.name = `${prefix}-start-minute`; |  | ||||||
|       startMinute.value = '00'; |  | ||||||
| 
 |  | ||||||
|       // Heure de fin
 |  | ||||||
|       const endContainer = document.createElement('div'); |  | ||||||
|       endContainer.classList.add('col-6', 'd-flex', 'align-items-center', 'gap-2'); |  | ||||||
| 
 |  | ||||||
|       const endHour = document.createElement('input'); |  | ||||||
|       endHour.type = 'number'; |  | ||||||
|       endHour.min = '0'; |  | ||||||
|       endHour.max = '23'; |  | ||||||
|       endHour.classList.add('form-control', 'form-control-sm'); |  | ||||||
|       endHour.style.width = '60px'; |  | ||||||
|       endHour.placeholder = 'HH'; |  | ||||||
|       endHour.name = `${prefix}-end-hour`; |  | ||||||
|       // Définir les horaires par défaut selon la plage
 |  | ||||||
|       endHour.value = prefix.includes('plage1') ? '12' : '18'; |  | ||||||
| 
 |  | ||||||
|       const endMinute = document.createElement('input'); |  | ||||||
|       endMinute.type = 'number'; |  | ||||||
|       endMinute.min = '0'; |  | ||||||
|       endMinute.max = '59'; |  | ||||||
|       endMinute.classList.add('form-control', 'form-control-sm'); |  | ||||||
|       endMinute.style.width = '60px'; |  | ||||||
|       endMinute.placeholder = 'MM'; |  | ||||||
|       endMinute.name = `${prefix}-end-minute`; |  | ||||||
|       endMinute.value = '00'; |  | ||||||
| 
 |  | ||||||
|       // Créer le texte avec les horaires
 |  | ||||||
|       const timeText = document.createElement('div'); |  | ||||||
|       timeText.classList.add('d-flex', 'align-items-center', 'gap-2', 'flex-wrap'); |  | ||||||
| 
 |  | ||||||
|       // Texte de début
 |  | ||||||
|       const startText = document.createElement('span'); |  | ||||||
|       startText.textContent = 'de'; |  | ||||||
|       timeText.appendChild(startText); |  | ||||||
|       timeText.appendChild(startHour); |  | ||||||
|       timeText.appendChild(document.createTextNode(':')); |  | ||||||
|       timeText.appendChild(startMinute); |  | ||||||
| 
 |  | ||||||
|       // Texte du milieu
 |  | ||||||
|       const middleText = document.createElement('span'); |  | ||||||
|       middleText.textContent = 'à'; |  | ||||||
|       timeText.appendChild(middleText); |  | ||||||
| 
 |  | ||||||
|       // Texte de fin
 |  | ||||||
|       timeText.appendChild(endHour); |  | ||||||
|       timeText.appendChild(document.createTextNode(':')); |  | ||||||
|       timeText.appendChild(endMinute); |  | ||||||
| 
 |  | ||||||
|       timeContainer.appendChild(timeText); |  | ||||||
|       container.appendChild(timeContainer); |  | ||||||
| 
 |  | ||||||
|       // Fonction pour mettre à jour le style des inputs
 |  | ||||||
|       const updateInputsStyle = (isActive) => { |  | ||||||
|         const inputs = timeContainer.querySelectorAll('input'); |  | ||||||
|         inputs.forEach(input => { |  | ||||||
|           if (isActive) { |  | ||||||
|             input.classList.remove('bg-light', 'text-muted'); |  | ||||||
|             input.disabled = false; |  | ||||||
|           } else { |  | ||||||
|             input.classList.add('bg-light', 'text-muted'); |  | ||||||
|             input.disabled = true; |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|       }; |  | ||||||
| 
 |  | ||||||
|       // Ajouter l'événement sur la checkbox
 |  | ||||||
|       checkbox.addEventListener('change', (e) => { |  | ||||||
|         updateInputsStyle(e.target.checked); |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       // Appliquer le style initial
 |  | ||||||
|       updateInputsStyle(checkbox.checked); |  | ||||||
| 
 |  | ||||||
|       return container; |  | ||||||
|     }, |  | ||||||
| 
 |  | ||||||
|     convertToOSMOpeningHours: function () { |  | ||||||
|       const jours = { |  | ||||||
|         'Lundi': 'Mo', |  | ||||||
|         'Mardi': 'Tu', |  | ||||||
|         'Mercredi': 'We', |  | ||||||
|         'Jeudi': 'Th', |  | ||||||
|         'Vendredi': 'Fr', |  | ||||||
|         'Samedi': 'Sa', |  | ||||||
|         'Dimanche': 'Su' |  | ||||||
|       }; |  | ||||||
| 
 |  | ||||||
|       let joursSelectionnes = []; |  | ||||||
|       let horairesParJour = {}; |  | ||||||
| 
 |  | ||||||
|       // Parcourir les checkboxes des jours
 |  | ||||||
|       Object.keys(jours).forEach(jour => { |  | ||||||
|         const checkbox = document.getElementById(`jour-${jour.toLowerCase()}`); |  | ||||||
|         if (checkbox && checkbox.checked) { |  | ||||||
|           joursSelectionnes.push(jours[jour]); |  | ||||||
| 
 |  | ||||||
|           // Récupérer les horaires pour ce jour
 |  | ||||||
|           const prefix = jour.toLowerCase(); |  | ||||||
|           const plage1 = this.getTimeRange(`${prefix}-plage1`); |  | ||||||
|           const plage2 = this.getTimeRange(`${prefix}-plage2`); |  | ||||||
| 
 |  | ||||||
|           let horaires = []; |  | ||||||
|           if (plage1) horaires.push(plage1); |  | ||||||
|           if (plage2) horaires.push(plage2); |  | ||||||
| 
 |  | ||||||
|           if (horaires.length > 0) { |  | ||||||
|             horairesParJour[jours[jour]] = horaires.join(','); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       // Construire la chaîne au format OSM
 |  | ||||||
|       let osmFormat = ''; |  | ||||||
|       if (joursSelectionnes.length > 0) { |  | ||||||
|         const horairesStrings = joursSelectionnes.map(jour => { |  | ||||||
|           return horairesParJour[jour] ? `${jour} ${horairesParJour[jour]}` : jour; |  | ||||||
|         }); |  | ||||||
|         osmFormat = horairesStrings.join('; '); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       // Mettre à jour l'input custom__opening_hours
 |  | ||||||
|       const customOpeningHours = document.querySelector('input[name="custom__opening_hours"]'); |  | ||||||
|       if (customOpeningHours) { |  | ||||||
|         customOpeningHours.value = osmFormat; |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
| 
 |  | ||||||
|     getTimeRange: function (prefix) { |  | ||||||
|       const isActive = document.querySelector(`input[name="${prefix}-active"]`).checked; |  | ||||||
|       if (!isActive) return null; |  | ||||||
| 
 |  | ||||||
|       const startHour = document.querySelector(`input[name="${prefix}-start-hour"]`).value; |  | ||||||
|       const startMinute = document.querySelector(`input[name="${prefix}-start-minute"]`).value; |  | ||||||
|       const endHour = document.querySelector(`input[name="${prefix}-end-hour"]`).value; |  | ||||||
|       const endMinute = document.querySelector(`input[name="${prefix}-end-minute"]`).value; |  | ||||||
| 
 |  | ||||||
|       if (startHour && startMinute && endHour && endMinute) { |  | ||||||
|         const start = `${startHour.padStart(2, '0')}:${startMinute.padStart(2, '0')}`; |  | ||||||
|         const end = `${endHour.padStart(2, '0')}:${endMinute.padStart(2, '0')}`; |  | ||||||
|         return `${start}-${end}`; |  | ||||||
|       } |  | ||||||
|       return null; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   openingHoursFormManager.init(); |   openingHoursFormManager.init(); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										73
									
								
								assets/josm.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								assets/josm.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,73 @@ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Ouvre les éléments sélectionnés dans JOSM | ||||||
|  |  * @param {Object} map - L'instance de la carte MapLibre | ||||||
|  |  * @param {boolean} map_is_loaded - État de chargement de la carte | ||||||
|  |  * @param {Array|Object} osmElements - Élément(s) OSM à ouvrir (peut être un tableau d'objets ou un objet unique) | ||||||
|  |  * @returns {void} | ||||||
|  |  */ | ||||||
|  | function openInJOSM(map, map_is_loaded, osmElements) { | ||||||
|  |   if (!map_is_loaded) { | ||||||
|  |     alert('Veuillez attendre que la carte soit chargée'); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (!osmElements || (Array.isArray(osmElements) && osmElements.length === 0)) { | ||||||
|  |     alert('Aucun élément à ouvrir dans JOSM'); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const bounds = map.getBounds(); | ||||||
|  | 
 | ||||||
|  |   // Convertir en tableau si c'est un seul élément
 | ||||||
|  |   const elements = Array.isArray(osmElements) ? osmElements : [osmElements]; | ||||||
|  | 
 | ||||||
|  |   // Séparer les éléments par type
 | ||||||
|  |   const nodeIds = []; | ||||||
|  |   const wayIds = []; | ||||||
|  |   const relationIds = []; | ||||||
|  | 
 | ||||||
|  |   elements.forEach(element => { | ||||||
|  |     if (typeof element === 'string') { | ||||||
|  |       // Si c'est juste un ID, on le traite comme un node par défaut
 | ||||||
|  |       nodeIds.push(element); | ||||||
|  |     } else { | ||||||
|  |       // Sinon on utilise le type spécifié
 | ||||||
|  |       switch (element.osm_type) { | ||||||
|  |         case 'node': | ||||||
|  |           nodeIds.push(element.osm_id); | ||||||
|  |           break; | ||||||
|  |         case 'way': | ||||||
|  |           wayIds.push(element.osm_id); | ||||||
|  |           break; | ||||||
|  |         case 'relation': | ||||||
|  |           relationIds.push(element.osm_id); | ||||||
|  |           break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   // Construire les paramètres de sélection
 | ||||||
|  |   const selectParams = []; | ||||||
|  |   if (nodeIds.length > 0) selectParams.push(`node${nodeIds.join(',')}`); | ||||||
|  |   if (wayIds.length > 0) selectParams.push(`way${wayIds.join(',')}`); | ||||||
|  |   if (relationIds.length > 0) selectParams.push(`relation${relationIds.join(',')}`); | ||||||
|  | 
 | ||||||
|  |   // Construire l'URL JOSM avec les paramètres de la boîte englobante
 | ||||||
|  |   const josmUrl = `http://localhost:8111/load_and_zoom?` + | ||||||
|  |     `left=${bounds.getWest()}&` + | ||||||
|  |     `right=${bounds.getEast()}&` + | ||||||
|  |     `top=${bounds.getNorth()}&` + | ||||||
|  |     `bottom=${bounds.getSouth()}&` + | ||||||
|  |     `select=${selectParams.join(',')}`; | ||||||
|  | 
 | ||||||
|  |   // Utiliser le bouton caché pour ouvrir JOSM
 | ||||||
|  |   const josmButton = document.getElementById('josmButton'); | ||||||
|  |   if (!josmButton) { | ||||||
|  |     console.error('Le bouton JOSM n\'existe pas dans le DOM'); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   josmButton.href = josmUrl; | ||||||
|  |   josmButton.click(); | ||||||
|  | } | ||||||
							
								
								
									
										388
									
								
								assets/opening_hours.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										388
									
								
								assets/opening_hours.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,388 @@ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | const openingHoursFormManager = { | ||||||
|  |   defaultOpeningHours: '', | ||||||
|  |   init: function () { | ||||||
|  |     // Rechercher l'élément par son attribut name plutôt que par son id
 | ||||||
|  |     const openingHoursInput = document.querySelector('input[name="custom__opening_hours"]'); | ||||||
|  |     if (!openingHoursInput) { | ||||||
|  |       console.warn('Élément input[name="custom__opening_hours"] non trouvé'); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     this.defaultOpeningHours = openingHoursInput.value; | ||||||
|  | 
 | ||||||
|  |     this.makeForm(); | ||||||
|  | 
 | ||||||
|  |     if (this.defaultOpeningHours !== '') { | ||||||
|  |       this.parseOpeningHoursValue(); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   parseOpeningHoursValue: function () { | ||||||
|  |     // Analyser la chaîne d'horaires d'ouverture
 | ||||||
|  |     const parsedOpeningHours = {}; | ||||||
|  | 
 | ||||||
|  |     // Masquer toutes les plages horaires par défaut
 | ||||||
|  |     const allDayContainers = document.querySelectorAll('.jour-container'); | ||||||
|  |     allDayContainers.forEach(container => { | ||||||
|  |       const checkbox = container.querySelector('input[type="checkbox"]'); | ||||||
|  |       const horairesContainer = container.querySelector('.horaires-container'); | ||||||
|  |       checkbox.checked = false; | ||||||
|  |       horairesContainer.classList.add('d-none'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     if (this.defaultOpeningHours) { | ||||||
|  |       // Diviser les différentes règles (séparées par des points-virgules)
 | ||||||
|  |       const rules = this.defaultOpeningHours.split(';').map(r => r.trim()); | ||||||
|  | 
 | ||||||
|  |       rules.forEach(rule => { | ||||||
|  |         // Extraire les jours et les heures
 | ||||||
|  |         const [days, hours] = rule.split(' ').filter(Boolean); | ||||||
|  | 
 | ||||||
|  |         // Convertir les jours en français
 | ||||||
|  |         const daysMap = { | ||||||
|  |           'Mo': 'lundi', | ||||||
|  |           'Tu': 'mardi', | ||||||
|  |           'We': 'mercredi', | ||||||
|  |           'Th': 'jeudi', | ||||||
|  |           'Fr': 'vendredi', | ||||||
|  |           'Sa': 'samedi', | ||||||
|  |           'Su': 'dimanche' | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         // Gérer les plages de jours (ex: Mo-Fr)
 | ||||||
|  |         if (days.includes('-')) { | ||||||
|  |           const [start, end] = days.split('-'); | ||||||
|  |           const startIndex = Object.keys(daysMap).indexOf(start); | ||||||
|  |           const endIndex = Object.keys(daysMap).indexOf(end); | ||||||
|  | 
 | ||||||
|  |           for (let i = startIndex; i <= endIndex; i++) { | ||||||
|  |             const day = daysMap[Object.keys(daysMap)[i]]; | ||||||
|  |             // Cocher la case du jour
 | ||||||
|  |             const checkbox = document.querySelector(`#jour-${day}`); | ||||||
|  |             if (checkbox) { | ||||||
|  |               checkbox.checked = true; | ||||||
|  |               checkbox.closest('.jour-container').querySelector('.horaires-container').classList.remove('d-none'); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } else { | ||||||
|  |           // Jour unique
 | ||||||
|  |           const day = daysMap[days]; | ||||||
|  |           const checkbox = document.querySelector(`#jour-${day}`); | ||||||
|  |           if (checkbox) { | ||||||
|  |             checkbox.checked = true; | ||||||
|  |             checkbox.closest('.jour-container').querySelector('.horaires-container').classList.remove('d-none'); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     console.log(parsedOpeningHours); | ||||||
|  |   }, | ||||||
|  |   makeForm: function () { | ||||||
|  |     const customOpeningHours = document.querySelector('input[name="custom__opening_hours"]'); | ||||||
|  |     console.log('makeForm customOpeningHours', customOpeningHours); | ||||||
|  | 
 | ||||||
|  |     if (customOpeningHours) { | ||||||
|  |       // Créer un conteneur flex pour aligner l'input et le formulaire
 | ||||||
|  |       const container = document.createElement('div'); | ||||||
|  |       container.classList.add('d-flex', 'flex-column', 'flex-md-row', 'align-items-start', 'gap-3', 'w-100'); | ||||||
|  | 
 | ||||||
|  |       // Créer le formulaire
 | ||||||
|  |       const form = document.createElement('form'); | ||||||
|  |       form.id = 'app_public_opening_hours'; | ||||||
|  |       form.classList.add('mt-3', 'flex-grow-1'); | ||||||
|  | 
 | ||||||
|  |       // Créer les cases à cocher pour chaque jour
 | ||||||
|  |       const jours = ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche']; | ||||||
|  |       const joursDiv = document.createElement('div'); | ||||||
|  |       joursDiv.classList.add('jours-ouverture', 'mb-4', 'row', 'g-3', 'mx-4'); | ||||||
|  | 
 | ||||||
|  |       jours.forEach(jour => { | ||||||
|  |         const jourContainer = document.createElement('div'); | ||||||
|  |         jourContainer.classList.add('jour-container', 'col-12'); | ||||||
|  | 
 | ||||||
|  |         // Checkbox pour le jour
 | ||||||
|  |         const divCheck = document.createElement('div'); | ||||||
|  |         divCheck.classList.add('form-check', 'mb-2'); | ||||||
|  | 
 | ||||||
|  |         const input = document.createElement('input'); | ||||||
|  |         input.type = 'checkbox'; | ||||||
|  |         input.id = `jour-${jour.toLowerCase()}`; | ||||||
|  |         input.name = `jour-${jour.toLowerCase()}`; | ||||||
|  |         input.classList.add('form-check-input'); | ||||||
|  | 
 | ||||||
|  |         const label = document.createElement('label'); | ||||||
|  |         label.htmlFor = `jour-${jour.toLowerCase()}`; | ||||||
|  |         label.classList.add('form-check-label'); | ||||||
|  |         label.textContent = jour; | ||||||
|  | 
 | ||||||
|  |         divCheck.appendChild(input); | ||||||
|  | 
 | ||||||
|  |         divCheck.appendChild(label); | ||||||
|  |         jourContainer.appendChild(divCheck); | ||||||
|  | 
 | ||||||
|  |         // Conteneur pour les plages horaires
 | ||||||
|  |         const horairesContainer = document.createElement('div'); | ||||||
|  |         horairesContainer.classList.add('horaires-container', 'ms-4', 'd-none'); | ||||||
|  | 
 | ||||||
|  |         // Première plage horaire
 | ||||||
|  |         const plage1 = this.createTimeRangeInputs(`${jour.toLowerCase()}-plage1`); | ||||||
|  |         horairesContainer.appendChild(plage1); | ||||||
|  | 
 | ||||||
|  |         // Séparateur
 | ||||||
|  |         const separator = document.createElement('div'); | ||||||
|  |         separator.classList.add('text-center', 'my-2'); | ||||||
|  |         separator.textContent = 'et'; | ||||||
|  |         horairesContainer.appendChild(separator); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         // Deuxième plage horaire
 | ||||||
|  |         const plage2 = this.createTimeRangeInputs(`${jour.toLowerCase()}-plage2`); | ||||||
|  |         horairesContainer.appendChild(plage2); | ||||||
|  | 
 | ||||||
|  |         jourContainer.appendChild(horairesContainer); | ||||||
|  |         joursDiv.appendChild(jourContainer); | ||||||
|  | 
 | ||||||
|  |         // Ajouter l'événement pour afficher/masquer les plages horaires
 | ||||||
|  |         input.addEventListener('change', (e) => { | ||||||
|  |           horairesContainer.classList.toggle('d-none', !e.target.checked); | ||||||
|  |           this.convertToOSMOpeningHours(); | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       form.appendChild(joursDiv); | ||||||
|  | 
 | ||||||
|  |       // Ajouter le formulaire au conteneur
 | ||||||
|  |       container.appendChild(form); | ||||||
|  | 
 | ||||||
|  |       // Insérer le conteneur après l'input original
 | ||||||
|  |       const parent = customOpeningHours.parentNode; | ||||||
|  |       if (parent) { | ||||||
|  |         parent.insertBefore(container, customOpeningHours.nextSibling); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // Ajouter un debounce pour limiter les appels lors des modifications
 | ||||||
|  |       const debounce = (func, wait) => { | ||||||
|  |         let timeout; | ||||||
|  |         return function executedFunction(...args) { | ||||||
|  |           const later = () => { | ||||||
|  |             clearTimeout(timeout); | ||||||
|  |             func(...args); | ||||||
|  |           }; | ||||||
|  |           clearTimeout(timeout); | ||||||
|  |           timeout = setTimeout(later, wait); | ||||||
|  |         }; | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       // Appliquer le debounce à la fonction de conversion
 | ||||||
|  |       const debouncedConvert = debounce(() => { | ||||||
|  |         this.convertToOSMOpeningHours(); | ||||||
|  |       }, 300); | ||||||
|  | 
 | ||||||
|  |       // Ajouter les listeners sur tous les inputs
 | ||||||
|  |       const allInputs = form.querySelectorAll('input'); | ||||||
|  |       allInputs.forEach(input => { | ||||||
|  |         input.addEventListener('change', debouncedConvert); | ||||||
|  |         input.addEventListener('input', debouncedConvert); | ||||||
|  |       }); | ||||||
|  |     } else { | ||||||
|  |       console.log('pas d input opening hours détecté') | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   createTimeRangeInputs: function (prefix) { | ||||||
|  |     const container = document.createElement('div'); | ||||||
|  |     container.classList.add('time-range', 'mb-2'); | ||||||
|  | 
 | ||||||
|  |     // Case à cocher pour activer la plage
 | ||||||
|  |     const checkboxContainer = document.createElement('div'); | ||||||
|  |     checkboxContainer.classList.add('form-check', 'mb-2'); | ||||||
|  | 
 | ||||||
|  |     const checkbox = document.createElement('input'); | ||||||
|  |     checkbox.type = 'checkbox'; | ||||||
|  |     checkbox.id = `${prefix}-active`; | ||||||
|  |     checkbox.name = `${prefix}-active`; | ||||||
|  |     checkbox.classList.add('form-check-input'); | ||||||
|  |     checkbox.checked = true; | ||||||
|  | 
 | ||||||
|  |     const checkboxLabel = document.createElement('label'); | ||||||
|  |     checkboxLabel.htmlFor = `${prefix}-active`; | ||||||
|  |     checkboxLabel.classList.add('form-check-label'); | ||||||
|  |     checkboxLabel.textContent = ''; | ||||||
|  | 
 | ||||||
|  |     checkboxContainer.appendChild(checkbox); | ||||||
|  |     checkboxContainer.appendChild(checkboxLabel); | ||||||
|  |     container.appendChild(checkboxContainer); | ||||||
|  | 
 | ||||||
|  |     // Conteneur pour les inputs d'horaires
 | ||||||
|  |     const timeContainer = document.createElement('div'); | ||||||
|  |     timeContainer.classList.add('ms-4', 'row', 'g-2'); | ||||||
|  | 
 | ||||||
|  |     // Heure de début
 | ||||||
|  |     const startContainer = document.createElement('div'); | ||||||
|  |     startContainer.classList.add('col-6', 'd-flex', 'align-items-center', 'gap-2'); | ||||||
|  | 
 | ||||||
|  |     const startHour = document.createElement('input'); | ||||||
|  |     startHour.type = 'number'; | ||||||
|  |     startHour.min = '0'; | ||||||
|  |     startHour.max = '23'; | ||||||
|  |     startHour.classList.add('form-control', 'form-control-sm'); | ||||||
|  |     startHour.style.width = '60px'; | ||||||
|  |     startHour.placeholder = 'HH'; | ||||||
|  |     startHour.name = `${prefix}-start-hour`; | ||||||
|  |     // Définir les horaires par défaut selon la plage
 | ||||||
|  |     startHour.value = prefix.includes('plage1') ? '08' : '14'; | ||||||
|  | 
 | ||||||
|  |     const startMinute = document.createElement('input'); | ||||||
|  |     startMinute.type = 'number'; | ||||||
|  |     startMinute.min = '0'; | ||||||
|  |     startMinute.max = '59'; | ||||||
|  |     startMinute.classList.add('form-control', 'form-control-sm'); | ||||||
|  |     startMinute.style.width = '60px'; | ||||||
|  |     startMinute.placeholder = 'MM'; | ||||||
|  |     startMinute.name = `${prefix}-start-minute`; | ||||||
|  |     startMinute.value = '00'; | ||||||
|  | 
 | ||||||
|  |     // Heure de fin
 | ||||||
|  |     const endContainer = document.createElement('div'); | ||||||
|  |     endContainer.classList.add('col-6', 'd-flex', 'align-items-center', 'gap-2'); | ||||||
|  | 
 | ||||||
|  |     const endHour = document.createElement('input'); | ||||||
|  |     endHour.type = 'number'; | ||||||
|  |     endHour.min = '0'; | ||||||
|  |     endHour.max = '23'; | ||||||
|  |     endHour.classList.add('form-control', 'form-control-sm'); | ||||||
|  |     endHour.style.width = '60px'; | ||||||
|  |     endHour.placeholder = 'HH'; | ||||||
|  |     endHour.name = `${prefix}-end-hour`; | ||||||
|  |     // Définir les horaires par défaut selon la plage
 | ||||||
|  |     endHour.value = prefix.includes('plage1') ? '12' : '18'; | ||||||
|  | 
 | ||||||
|  |     const endMinute = document.createElement('input'); | ||||||
|  |     endMinute.type = 'number'; | ||||||
|  |     endMinute.min = '0'; | ||||||
|  |     endMinute.max = '59'; | ||||||
|  |     endMinute.classList.add('form-control', 'form-control-sm'); | ||||||
|  |     endMinute.style.width = '60px'; | ||||||
|  |     endMinute.placeholder = 'MM'; | ||||||
|  |     endMinute.name = `${prefix}-end-minute`; | ||||||
|  |     endMinute.value = '00'; | ||||||
|  | 
 | ||||||
|  |     // Créer le texte avec les horaires
 | ||||||
|  |     const timeText = document.createElement('div'); | ||||||
|  |     timeText.classList.add('d-flex', 'align-items-center', 'gap-2', 'flex-wrap'); | ||||||
|  | 
 | ||||||
|  |     // Texte de début
 | ||||||
|  |     const startText = document.createElement('span'); | ||||||
|  |     startText.textContent = 'de'; | ||||||
|  |     timeText.appendChild(startText); | ||||||
|  |     timeText.appendChild(startHour); | ||||||
|  |     timeText.appendChild(document.createTextNode(':')); | ||||||
|  |     timeText.appendChild(startMinute); | ||||||
|  | 
 | ||||||
|  |     // Texte du milieu
 | ||||||
|  |     const middleText = document.createElement('span'); | ||||||
|  |     middleText.textContent = 'à'; | ||||||
|  |     timeText.appendChild(middleText); | ||||||
|  | 
 | ||||||
|  |     // Texte de fin
 | ||||||
|  |     timeText.appendChild(endHour); | ||||||
|  |     timeText.appendChild(document.createTextNode(':')); | ||||||
|  |     timeText.appendChild(endMinute); | ||||||
|  | 
 | ||||||
|  |     timeContainer.appendChild(timeText); | ||||||
|  |     container.appendChild(timeContainer); | ||||||
|  | 
 | ||||||
|  |     // Fonction pour mettre à jour le style des inputs
 | ||||||
|  |     const updateInputsStyle = (isActive) => { | ||||||
|  |       const inputs = timeContainer.querySelectorAll('input'); | ||||||
|  |       inputs.forEach(input => { | ||||||
|  |         if (isActive) { | ||||||
|  |           input.classList.remove('bg-light', 'text-muted'); | ||||||
|  |           input.disabled = false; | ||||||
|  |         } else { | ||||||
|  |           input.classList.add('bg-light', 'text-muted'); | ||||||
|  |           input.disabled = true; | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // Ajouter l'événement sur la checkbox
 | ||||||
|  |     checkbox.addEventListener('change', (e) => { | ||||||
|  |       updateInputsStyle(e.target.checked); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // Appliquer le style initial
 | ||||||
|  |     updateInputsStyle(checkbox.checked); | ||||||
|  | 
 | ||||||
|  |     return container; | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   convertToOSMOpeningHours: function () { | ||||||
|  |     const jours = { | ||||||
|  |       'Lundi': 'Mo', | ||||||
|  |       'Mardi': 'Tu', | ||||||
|  |       'Mercredi': 'We', | ||||||
|  |       'Jeudi': 'Th', | ||||||
|  |       'Vendredi': 'Fr', | ||||||
|  |       'Samedi': 'Sa', | ||||||
|  |       'Dimanche': 'Su' | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let joursSelectionnes = []; | ||||||
|  |     let horairesParJour = {}; | ||||||
|  | 
 | ||||||
|  |     // Parcourir les checkboxes des jours
 | ||||||
|  |     Object.keys(jours).forEach(jour => { | ||||||
|  |       const checkbox = document.getElementById(`jour-${jour.toLowerCase()}`); | ||||||
|  |       if (checkbox && checkbox.checked) { | ||||||
|  |         joursSelectionnes.push(jours[jour]); | ||||||
|  | 
 | ||||||
|  |         // Récupérer les horaires pour ce jour
 | ||||||
|  |         const prefix = jour.toLowerCase(); | ||||||
|  |         const plage1 = this.getTimeRange(`${prefix}-plage1`); | ||||||
|  |         const plage2 = this.getTimeRange(`${prefix}-plage2`); | ||||||
|  | 
 | ||||||
|  |         let horaires = []; | ||||||
|  |         if (plage1) horaires.push(plage1); | ||||||
|  |         if (plage2) horaires.push(plage2); | ||||||
|  | 
 | ||||||
|  |         if (horaires.length > 0) { | ||||||
|  |           horairesParJour[jours[jour]] = horaires.join(','); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // Construire la chaîne au format OSM
 | ||||||
|  |     let osmFormat = ''; | ||||||
|  |     if (joursSelectionnes.length > 0) { | ||||||
|  |       const horairesStrings = joursSelectionnes.map(jour => { | ||||||
|  |         return horairesParJour[jour] ? `${jour} ${horairesParJour[jour]}` : jour; | ||||||
|  |       }); | ||||||
|  |       osmFormat = horairesStrings.join('; '); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Mettre à jour l'input custom__opening_hours
 | ||||||
|  |     const customOpeningHours = document.querySelector('input[name="custom__opening_hours"]'); | ||||||
|  |     if (customOpeningHours) { | ||||||
|  |       customOpeningHours.value = osmFormat; | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   getTimeRange: function (prefix) { | ||||||
|  |     const isActive = document.querySelector(`input[name="${prefix}-active"]`).checked; | ||||||
|  |     if (!isActive) return null; | ||||||
|  | 
 | ||||||
|  |     const startHour = document.querySelector(`input[name="${prefix}-start-hour"]`).value; | ||||||
|  |     const startMinute = document.querySelector(`input[name="${prefix}-start-minute"]`).value; | ||||||
|  |     const endHour = document.querySelector(`input[name="${prefix}-end-hour"]`).value; | ||||||
|  |     const endMinute = document.querySelector(`input[name="${prefix}-end-minute"]`).value; | ||||||
|  | 
 | ||||||
|  |     if (startHour && startMinute && endHour && endMinute) { | ||||||
|  |       const start = `${startHour.padStart(2, '0')}:${startMinute.padStart(2, '0')}`; | ||||||
|  |       const end = `${endHour.padStart(2, '0')}:${endMinute.padStart(2, '0')}`; | ||||||
|  |       return `${start}-${end}`; | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								assets/utils.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								assets/utils.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | 
 | ||||||
|  | function colorHeadingTable() { | ||||||
|  | 
 | ||||||
|  |   const headers = document.querySelectorAll('th'); | ||||||
|  |   headers.forEach(header => { | ||||||
|  |     const text = header.textContent; | ||||||
|  |     const match = text.match(/\((\d+)\s*\/\s*(\d+)\)/); | ||||||
|  |     if (match) { | ||||||
|  |       const [_, completed, total] = match; | ||||||
|  |       const ratio = completed / total; | ||||||
|  |       const alpha = ratio.toFixed(2); | ||||||
|  |       header.style.backgroundColor = `rgba(154, 205, 50, ${alpha})`; | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function updateMapHeightForLargeScreens() { | ||||||
|  | 
 | ||||||
|  |   const mapFound = document.querySelector('#map'); | ||||||
|  |   if (mapFound && window.innerHeight > 800 && window.innerWidth > 800) { | ||||||
|  |     mapFound.style.height = '80vh'; | ||||||
|  |   } else { | ||||||
|  |     console.log('window.innerHeight', window.innerHeight); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										0
									
								
								public/js/app.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								public/js/app.js
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										21
									
								
								templates/admin/edit.html.twig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								templates/admin/edit.html.twig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  |     <div class="container"> | ||||||
|  |         <h1>Édition</h1> | ||||||
|  |         <div class="row"> | ||||||
|  |             <div class="col-12"> | ||||||
|  |                 <div class="card"> | ||||||
|  |                     <div class="card-body"> | ||||||
|  |                         <h5 class="card-title">Carte</h5> | ||||||
|  |                         <div id="map" style="height: 600px;"></div> | ||||||
|  |                         <div class="mt-3"> | ||||||
|  |                             <button id="openInJOSM" class="btn btn-primary">Ouvrir dans JOSM</button> | ||||||
|  |                             <a href="#" id="viewInPanoramax" class="btn btn-secondary" target="_blank">Voir dans Panoramax</a> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  |     <script src="{{ asset('js/app.js') }}"></script>  | ||||||
|  | @ -25,7 +25,7 @@ commerces existants déjà en base: {{ commerces|length }} | ||||||
| </p> | </p> | ||||||
| {# {{ dump(commerces[0]) }} #} | {# {{ dump(commerces[0]) }} #} | ||||||
| 
 | 
 | ||||||
| <table class="table table-striped"> | <table class="table table-striped js-sort-table"> | ||||||
|      {% include 'admin/stats/table-head.html.twig' %} |      {% include 'admin/stats/table-head.html.twig' %} | ||||||
| 
 | 
 | ||||||
|     <tbody> |     <tbody> | ||||||
|  |  | ||||||
|  | @ -86,7 +86,7 @@ | ||||||
|             </a> |             </a> | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|         <table class="table table-bordered table-striped table-hover table-responsive"> |         <table class="table table-bordered table-striped table-hover table-responsive js-sort-table"> | ||||||
|             {% include 'admin/stats/table-head.html.twig' %} |             {% include 'admin/stats/table-head.html.twig' %} | ||||||
|             <tbody> |             <tbody> | ||||||
|                 {% for commerce in stats.places %} |                 {% for commerce in stats.places %} | ||||||
|  | @ -103,21 +103,20 @@ | ||||||
|      </div> |      </div> | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
|  | <!-- Bouton caché pour JOSM --> | ||||||
|  | <a id="josmButton" style="display: none;"></a> | ||||||
|  | 
 | ||||||
| <script src='{{ asset('js/maplibre/maplibre-gl.js') }}'></script> | <script src='{{ asset('js/maplibre/maplibre-gl.js') }}'></script> | ||||||
| <script src='{{ asset('js/turf/turf.min.js') }}'></script> | <script src='{{ asset('js/turf/turf.min.js') }}'></script> | ||||||
| <script src="{{ asset('js/chartjs/chart.umd.js') }}"></script> | <script src="{{ asset('js/chartjs/chart.umd.js') }}"></script> | ||||||
|  | <script src="{{ asset('js/app.js') }}"></script> | ||||||
| <script> | <script> | ||||||
|     const request = `{{query_places|raw}}`;  |     const request = `{{query_places|raw}}`;  | ||||||
|     const zip_code = `{{stats.zone}}`; |     const zip_code = `{{stats.zone}}`; | ||||||
|     let mapElements = []; |     let mapElements = []; | ||||||
|     let map_is_loaded=false; |     let map_is_loaded = false; | ||||||
|  |     let features = []; | ||||||
| 
 | 
 | ||||||
| const map = new maplibregl.Map({ |  | ||||||
|             container: 'map', |  | ||||||
|             style: 'https://api.maptiler.com/maps/streets-v2/style.json?key={{ maptiler_token }}', |  | ||||||
|             center: [2.3488, 48.8534], |  | ||||||
|             zoom: 12 |  | ||||||
|         }); |  | ||||||
|          |          | ||||||
|     function getCompletionColor(completion) { |     function getCompletionColor(completion) { | ||||||
|         if (completion === undefined || completion === null) { |         if (completion === undefined || completion === null) { | ||||||
|  | @ -239,76 +238,104 @@ const map = new maplibregl.Map({ | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|     async function fetchland() { |  | ||||||
|         // Requête pour obtenir le contour administratif |  | ||||||
|         const boundaryQuery = `[out:json][timeout:25]; |  | ||||||
| area["ref:INSEE"="${zip_code}"]->.searchArea; |  | ||||||
| ( |  | ||||||
|     relation["boundary"="postal_code"]["ref:INSEE"="${zip_code}"](area.searchArea); |  | ||||||
| ); |  | ||||||
| out body; |  | ||||||
| >; |  | ||||||
| out skel qt;`; |  | ||||||
| 
 | 
 | ||||||
|         const encodedBoundaryRequest = encodeURIComponent(boundaryQuery); |     async function loadPlaces(map) { | ||||||
|         const boundaryResponse = await fetch(`https://overpass-api.de/api/interpreter?data=${encodedBoundaryRequest}`); |         map_is_loaded = false; | ||||||
|         const boundaryData = await boundaryResponse.json(); |         try { | ||||||
| 
 |             const response = await fetch('https://overpass-api.de/api/interpreter', { | ||||||
|         const encodedRequest = encodeURIComponent(request); |                 method: 'POST', | ||||||
|         const overpassUrl = `https://overpass-api.de/api/interpreter?data=${encodedRequest}`; |                 body: request | ||||||
|         const response = await fetch(overpassUrl); |             }); | ||||||
|  |             if (!response.ok) { | ||||||
|  |                 throw new Error(`HTTP error! status: ${response.status}`); | ||||||
|  |             } | ||||||
|             const data = await response.json(); |             const data = await response.json(); | ||||||
| 
 |             console.log('Lieux chargés:', data.elements); | ||||||
|          |  | ||||||
| 
 |  | ||||||
|         map.on('load', () => { |  | ||||||
| 
 |  | ||||||
|             map_is_loaded=true; |  | ||||||
|             console.log('map load...'); |  | ||||||
|             // Ajouter le contour administratif |  | ||||||
|             if (boundaryData.elements && boundaryData.elements.length > 0) { |  | ||||||
|                 const boundary = boundaryData.elements[0]; |  | ||||||
|                 if (boundary.members) { |  | ||||||
|                     const coordinates = boundary.members.map(member => { |  | ||||||
|                         if (member.type === 'node') { |  | ||||||
|                             return [member.lon, member.lat]; |  | ||||||
|                         } |  | ||||||
|                     }).filter(coord => coord); |  | ||||||
| 
 |  | ||||||
|                     map.addSource('boundary', { |  | ||||||
|                         'type': 'geojson', |  | ||||||
|                         'data': { |  | ||||||
|                             'type': 'Feature', |  | ||||||
|                             'geometry': { |  | ||||||
|                                 'type': 'Polygon', |  | ||||||
|                                 'coordinates': [coordinates] |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
| 
 |  | ||||||
|                     map.addLayer({ |  | ||||||
|                         'id': 'boundary-layer', |  | ||||||
|                         'type': 'fill', |  | ||||||
|                         'source': 'boundary', |  | ||||||
|                         'paint': { |  | ||||||
|                             'fill-color': '#088', |  | ||||||
|                             'fill-opacity': 0.1, |  | ||||||
|                             'fill-outline-color': '#000' |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|             }else{ |  | ||||||
|                 console.log('pas de contour administratif trouvé'); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             console.log('map chargé',data.elements); |  | ||||||
|             mapElements = data.elements; |             mapElements = data.elements; | ||||||
|  |             map_is_loaded = true; | ||||||
| 
 | 
 | ||||||
|             // Créer le graphique de distribution |             // Créer le graphique de distribution | ||||||
|             createCompletionChart(data.elements); |             createCompletionChart(data.elements); | ||||||
| 
 | 
 | ||||||
|             document.getElementById('maploader').classList.add('d-none'); |             // Mettre à jour les cercles | ||||||
|  |             features = []; | ||||||
|  |             data.elements.forEach(element => { | ||||||
|  |                 const lat = element.lat || (element.center && element.center.lat); | ||||||
|  |                 const lon = element.lon || (element.center && element.center.lon); | ||||||
| 
 | 
 | ||||||
|  |                 if (lat && lon) { | ||||||
|  |                     const completion = calculateCompletion(element); | ||||||
|  |                     const color = getCompletionColor(completion); | ||||||
|  |                      | ||||||
|  |                     // Créer un cercle de 20 mètres de rayon (plus petit) | ||||||
|  |                     const circle = turf.circle([lon, lat], 0.04, { steps: 64, units: 'kilometers' }); | ||||||
|  |                     circle.properties = { | ||||||
|  |                         color: color, | ||||||
|  |                         completion: completion, | ||||||
|  |                         name: element.tags?.name || 'Sans nom', | ||||||
|  |                         popupContent: createPopupContent(element), | ||||||
|  |                         center: [lon, lat] // Stocker le centre comme un tableau [lon, lat] | ||||||
|  |                     }; | ||||||
|  |                     features.push(circle); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             // Mettre à jour la source des cercles | ||||||
|  |             map.getSource('completion-circles').setData({ | ||||||
|  |                 'type': 'FeatureCollection', | ||||||
|  |                 'features': features | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             // Calculer les bounds pour tous les points | ||||||
|  |             const points = features.map(f => f.properties.center); | ||||||
|  |             if (points.length > 0) { | ||||||
|  |                 const bounds = new maplibregl.LngLatBounds(points[0], points[0]); | ||||||
|  |                 points.forEach(point => bounds.extend(point)); | ||||||
|  |                  | ||||||
|  |                 // Vérifier que les bounds sont valides avant de les utiliser | ||||||
|  |                 if (bounds?._sw && bounds?._ne) { | ||||||
|  |                     map.fitBounds(bounds, { | ||||||
|  |                         padding: 50, | ||||||
|  |                         maxZoom: 15 | ||||||
|  |                     }); | ||||||
|  |                 } else { | ||||||
|  |                     console.warn('Bounds invalides, utilisation des coordonnées par défaut'); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             document.getElementById('maploader').classList.add('d-none'); | ||||||
|  |         } catch (error) { | ||||||
|  |             console.error('Erreur lors du chargement des lieux:', error); | ||||||
|  |             document.getElementById('maploader').classList.add('d-none'); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     function openInJOSM() { | ||||||
|  |         const selectedElements = document.querySelectorAll('input[type="checkbox"]:checked'); | ||||||
|  |         if (selectedElements.length === 0) { | ||||||
|  |             alert('Veuillez sélectionner au moins un élément'); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const osmElements = Array.from(selectedElements).map(checkbox => { | ||||||
|  |             return JSON.parse(checkbox.value); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         window.openInJOSM(map, map_is_loaded, osmElements); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     document.addEventListener('DOMContentLoaded', function() { | ||||||
|  |         console.log('DOMContentLoaded'); | ||||||
|  | 
 | ||||||
|  |         const map = new maplibregl.Map({ | ||||||
|  |             container: 'map', | ||||||
|  |             style: 'https://api.maptiler.com/maps/streets-v2/style.json?key={{ maptiler_token }}', | ||||||
|  |             center: [2.3488, 48.8534], | ||||||
|  |             zoom: 12 | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         document.getElementById('openInJOSM').addEventListener('click', openInJOSM); | ||||||
|  | 
 | ||||||
|  |         map.on('load', () => { | ||||||
|             // Créer une source pour les cercles |             // Créer une source pour les cercles | ||||||
|             map.addSource('completion-circles', { |             map.addSource('completion-circles', { | ||||||
|                 'type': 'geojson', |                 'type': 'geojson', | ||||||
|  | @ -341,57 +368,6 @@ out skel qt;`; | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|             const features = []; |  | ||||||
|             data.elements.forEach(element => { |  | ||||||
|                 const lat = element.lat || (element.center && element.center.lat); |  | ||||||
|                 const lon = element.lon || (element.center && element.center.lon); |  | ||||||
| 
 |  | ||||||
|                 if (lat && lon) { |  | ||||||
|                     const completion = calculateCompletion(element); |  | ||||||
|                     const color = getCompletionColor(completion); |  | ||||||
|                      |  | ||||||
|                     // Créer un cercle de 20 mètres de rayon (plus petit) |  | ||||||
|                     const circle = turf.circle([lon, lat], 0.04, { steps: 64, units: 'kilometers' }); |  | ||||||
|                     circle.properties = { |  | ||||||
|                         color: color, |  | ||||||
|                         completion: completion, |  | ||||||
|                         name: element.tags?.name || 'Sans nom', |  | ||||||
|                         popupContent: createPopupContent(element), |  | ||||||
|                         center: [lon, lat] // Stocker le centre comme un tableau [lon, lat] |  | ||||||
|                     }; |  | ||||||
|                     features.push(circle); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             // Mettre à jour la source des cercles |  | ||||||
|             map.getSource('completion-circles').setData({ |  | ||||||
|                 'type': 'FeatureCollection', |  | ||||||
|                 'features': features |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             // Fonction pour recalculer les cercles en fonction du zoom |  | ||||||
|             function updateCircles() { |  | ||||||
|                 const zoom = map.getZoom(); |  | ||||||
|                 const newFeatures = []; |  | ||||||
|                  |  | ||||||
|                 features.forEach(feature => { |  | ||||||
|                     // Ajuster la taille du cercle en fonction du zoom |  | ||||||
|                     // Plus le zoom est élevé, plus le cercle est petit |  | ||||||
|                     const radius = 0.02 / Math.pow(1.5, zoom - 12); // 12 est le niveau de zoom de référence |  | ||||||
|                     const newCircle = turf.circle(feature.properties.center, radius, { steps: 64, units: 'kilometers' }); |  | ||||||
|                     newCircle.properties = feature.properties; |  | ||||||
|                     newFeatures.push(newCircle); |  | ||||||
|                 }); |  | ||||||
| 
 |  | ||||||
|                 map.getSource('completion-circles').setData({ |  | ||||||
|                     'type': 'FeatureCollection', |  | ||||||
|                     'features': newFeatures |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Mettre à jour les cercles lors du zoom |  | ||||||
|             map.on('zoom', updateCircles); |  | ||||||
| 
 |  | ||||||
|             // Ajouter les popups sur les cercles |             // Ajouter les popups sur les cercles | ||||||
|             map.on('click', 'completion-circles', (e) => { |             map.on('click', 'completion-circles', (e) => { | ||||||
|                 const properties = e.features[0].properties; |                 const properties = e.features[0].properties; | ||||||
|  | @ -410,104 +386,14 @@ out skel qt;`; | ||||||
|             map.on('mouseleave', 'completion-circles', () => { |             map.on('mouseleave', 'completion-circles', () => { | ||||||
|                 map.getCanvas().style.cursor = ''; |                 map.getCanvas().style.cursor = ''; | ||||||
|             }); |             }); | ||||||
|  | 
 | ||||||
|  |             // Charger les lieux | ||||||
|  |             loadPlaces(map); | ||||||
|  |               | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         // Calculer les limites (bounds) à partir des marqueurs |  | ||||||
|         const bounds = new maplibregl.LngLatBounds(); |  | ||||||
|         data.elements.forEach(element => { |  | ||||||
|             if (element.lat && element.lon) { |  | ||||||
|                 bounds.extend([element.lon, element.lat]); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         // Centrer et zoomer la carte pour inclure tous les marqueurs |  | ||||||
|         if (!bounds.isEmpty()) { |  | ||||||
|             map.fitBounds(bounds, { |  | ||||||
|                 padding: 50 // Ajoute une marge autour des marqueurs |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     function openInJOSM() { |  | ||||||
|         if (mapElements.length === 0) { |  | ||||||
|             alert('Aucun élément à ouvrir dans JOSM'); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (!map_is_loaded) { |  | ||||||
|             alert('La carte n\'est pas encore chargée, veuillez patienter'); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const nodeIds = []; |  | ||||||
|         const wayIds = []; |  | ||||||
|         const relationIds = []; |  | ||||||
| 
 |  | ||||||
|         mapElements.forEach(element => { |  | ||||||
|             switch(element.type) { |  | ||||||
|                 case 'node': |  | ||||||
|                     nodeIds.push(element.id); |  | ||||||
|                     break; |  | ||||||
|                 case 'way': |  | ||||||
|                     wayIds.push(element.id); |  | ||||||
|                     break; |  | ||||||
|                 case 'relation': |  | ||||||
|                     relationIds.push(element.id); |  | ||||||
|                     break; |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         // Obtenir les coordonnées du viewport actuel |  | ||||||
|         const center = map.getCenter(); |  | ||||||
|         const zoom = map.getZoom(); |  | ||||||
|         const width = map.getCanvas().width; |  | ||||||
|         const height = map.getCanvas().height; |  | ||||||
|          |  | ||||||
|         // Calculer les limites en utilisant les coordonnées du centre et le zoom |  | ||||||
|         const west = center.lng - (360 / Math.pow(2, zoom + 1)) * (width / 256); |  | ||||||
|         const east = center.lng + (360 / Math.pow(2, zoom + 1)) * (width / 256); |  | ||||||
|         const north = center.lat + (180 / Math.pow(2, zoom + 1)) * (height / 256); |  | ||||||
|         const south = center.lat - (180 / Math.pow(2, zoom + 1)) * (height / 256); |  | ||||||
| 
 |  | ||||||
|         let josmUrl = 'http://localhost:8111/load_and_zoom?'; |  | ||||||
|         const params = []; |  | ||||||
| 
 |  | ||||||
|         if (nodeIds.length > 0) { |  | ||||||
|             params.push(`node${nodeIds.join(',')}`); |  | ||||||
|         } |  | ||||||
|         if (wayIds.length > 0) { |  | ||||||
|             params.push(`way${wayIds.join(',')}`); |  | ||||||
|         } |  | ||||||
|         if (relationIds.length > 0) { |  | ||||||
|             params.push(`relation${relationIds.join(',')}`); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Ajouter la bounding box calculée |  | ||||||
|         params.push(`left=${west}&right=${east}&top=${north}&bottom=${south}`); |  | ||||||
| 
 |  | ||||||
|         josmUrl += params.join('&'); |  | ||||||
|         window.open(josmUrl); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     document.addEventListener('DOMContentLoaded', function() { |  | ||||||
|         console.log('DOMContentLoaded'); |  | ||||||
|         const headers = document.querySelectorAll('th'); |  | ||||||
|         headers.forEach(header => { |  | ||||||
|             const text = header.textContent; |  | ||||||
|             const match = text.match(/\((\d+)\s*\/\s*(\d+)\)/); |  | ||||||
|             if (match) { |  | ||||||
|                 const [_, completed, total] = match; |  | ||||||
|                 const ratio = completed / total; |  | ||||||
|                 const alpha = ratio.toFixed(2); |  | ||||||
|                 header.style.backgroundColor = `rgba(154, 205, 50, ${alpha})`; |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         document.getElementById('openInJOSM').addEventListener('click', openInJOSM); |  | ||||||
| 
 |  | ||||||
|         fetchland(); |  | ||||||
|   |  | ||||||
|         sortTable(); |         sortTable(); | ||||||
|  |         colorHeadingTable(); | ||||||
|     }); |     }); | ||||||
| </script> | </script> | ||||||
| {% endblock %}  | {% endblock %}  | ||||||
|  | @ -107,11 +107,11 @@ | ||||||
|         {% block javascripts %} |         {% block javascripts %} | ||||||
|             {{ encore_entry_script_tags('app') }}   |             {{ encore_entry_script_tags('app') }}   | ||||||
|         <script src="{{ asset('js/bootstrap/bootstrap.bundle.min.js') }}"></script> |         <script src="{{ asset('js/bootstrap/bootstrap.bundle.min.js') }}"></script> | ||||||
| 
 |         <script src='{{ asset('js/maplibre/maplibre-gl.js') }}'></script> | ||||||
|         <script src="{{ asset('js/sort-table/sort-table.js') }}"></script> |         <script src="{{ asset('js/sort-table/sort-table.js') }}"></script> | ||||||
| 
 | 
 | ||||||
|         <!-- Script pour le tri automatique des tableaux --> |         <!-- Script pour le tri automatique des tableaux --> | ||||||
|         <script src="{{ asset('js/bootstrap/Sortable.min.js') }}"></script> |         {# <script src="{{ asset('js/bootstrap/Sortable.min.js') }}"></script> #} | ||||||
|             <script src="{{ asset('js/qrcode/qrcode.min.js') }}"></script> |             <script src="{{ asset('js/qrcode/qrcode.min.js') }}"></script> | ||||||
|             <script> |             <script> | ||||||
|                 new QRCode(document.getElementById('qrcode'), { |                 new QRCode(document.getElementById('qrcode'), { | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ | ||||||
|     <h1>Commerces fermés</h1> |     <h1>Commerces fermés</h1> | ||||||
|     <p>Voici la liste des commerces fermés :</p> |     <p>Voici la liste des commerces fermés :</p> | ||||||
|     <div class="table-responsive"> |     <div class="table-responsive"> | ||||||
|         <table class="table table-striped"> |         <table class="table table-striped js-sort-table"> | ||||||
|             <thead> |             <thead> | ||||||
|                 <tr> |                 <tr> | ||||||
|                     <th>Nom du commerce</th> |                     <th>Nom du commerce</th> | ||||||
|  |  | ||||||
|  | @ -122,12 +122,12 @@ | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Créer une carte des villes avec les codes postaux |         // Créer une carte des villes avec les codes postaux | ||||||
|         {# let map = new maplibregl.Map({ |         let map = new maplibregl.Map({ | ||||||
|             container: 'mapDashboard', |             container: 'mapDashboard', | ||||||
|             style: 'https://api.maptiler.com/maps/basic-v2/style.json?key={{ maptiler_token }}', |             style: 'https://api.maptiler.com/maps/basic-v2/style.json?key={{ maptiler_token }}', | ||||||
|             center: [2.3488, 48.8534], // Paris |             center: [2.3488, 48.8534], // Paris | ||||||
|             zoom: 10 |             zoom: 10 | ||||||
|         }); #} |         }); | ||||||
|   |   | ||||||
|         // Fonction pour obtenir la couleur selon le pourcentage |         // Fonction pour obtenir la couleur selon le pourcentage | ||||||
|         function getColorFromPercent(percent) { |         function getColorFromPercent(percent) { | ||||||
|  | @ -190,7 +190,7 @@ out skel qt;`; | ||||||
|             {# <div id="mapDashboard"></div> #} |             {# <div id="mapDashboard"></div> #} | ||||||
|              |              | ||||||
| 
 | 
 | ||||||
|               <table class="table table-hover table-striped table-responsive"> |               <table class="table table-hover table-striped table-responsive js-sort-table"> | ||||||
|                   <thead> |                   <thead> | ||||||
|                       <tr> |                       <tr> | ||||||
|                           <th>Zone</th> |                           <th>Zone</th> | ||||||
|  |  | ||||||
|  | @ -135,12 +135,12 @@ | ||||||
|                             <span class="last-modification">{{ 'display.last_modification'|trans }}: {{ commerce_overpass['@attributes'].timestamp }}</span>,  |                             <span class="last-modification">{{ 'display.last_modification'|trans }}: {{ commerce_overpass['@attributes'].timestamp }}</span>,  | ||||||
|                             <strong>{{ 'display.days_ago'|trans({'%days%': date(commerce_overpass['@attributes'].timestamp).diff(date()).days}) }}</strong> |                             <strong>{{ 'display.days_ago'|trans({'%days%': date(commerce_overpass['@attributes'].timestamp).diff(date()).days}) }}</strong> | ||||||
|                             {{ 'display.by'|trans }}  |                             {{ 'display.by'|trans }}  | ||||||
|                             <a href="https://www.openstreetmap.org/user/{{ commerce_overpass['@attributes'].user }}" target="_blank">{{ commerce_overpass['@attributes'].user }}</a> |                             <a href="https://www.openstreetmap.org/user/{{ commerce_overpass['@attributes'].user }}" >{{ commerce_overpass['@attributes'].user }}</a> | ||||||
|                         </div>  |                         </div>  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|                         <div class="lien-OpenStreetMap"> |                         <div class="lien-OpenStreetMap"> | ||||||
|                             <a href="https://www.openstreetmap.org/{{commerce.osmKind}}/{{ commerce_overpass['@attributes'].id }}" target="_blank" class="btn btn-outline-secondary"> |                             <a href="https://www.openstreetmap.org/{{commerce.osmKind}}/{{ commerce_overpass['@attributes'].id }}"  class="btn btn-outline-secondary"> | ||||||
|                                 {{ 'display.view_on_osm'|trans }} |                                 {{ 'display.view_on_osm'|trans }} | ||||||
|                             </a> |                             </a> | ||||||
|                             <button onclick="openInJOSM('{{commerce.osmKind}}', '{{ commerce_overpass['@attributes'].id }}')" class="btn btn-outline-secondary ms-2"> |                             <button onclick="openInJOSM('{{commerce.osmKind}}', '{{ commerce_overpass['@attributes'].id }}')" class="btn btn-outline-secondary ms-2"> | ||||||
|  | @ -183,14 +183,14 @@ | ||||||
|         <script> |         <script> | ||||||
|             function openInJOSM(type, id) { |             function openInJOSM(type, id) { | ||||||
|                 const josmUrl = `http://localhost:8111/load_object?objects=${type}${id}`; |                 const josmUrl = `http://localhost:8111/load_object?objects=${type}${id}`; | ||||||
|                 window.open(josmUrl, '_blank'); |                 window.open(josmUrl); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             function openInPanoramax() { |             function openInPanoramax() { | ||||||
|                 const center = map.getCenter(); |                 const center = map.getCenter(); | ||||||
|                 const zoom = map.getZoom(); |                 const zoom = map.getZoom(); | ||||||
|                 const panoramaxUrl = `https://api.panoramax.xyz/?focus=map&map=${zoom}/${center.lat}/${center.lng}`; |                 const panoramaxUrl = `https://api.panoramax.xyz/?focus=map&map=${zoom}/${center.lat}/${center.lng}`; | ||||||
|                 window.open(panoramaxUrl, '_blank'); |                 window.open(panoramaxUrl); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             {% if commerce is not empty and mapbox_token is not empty and maptiler_token is not empty and commerce_overpass['@attributes'].lon is defined and commerce_overpass['@attributes'].lat is defined %} |             {% if commerce is not empty and mapbox_token is not empty and maptiler_token is not empty and commerce_overpass['@attributes'].lon is defined and commerce_overpass['@attributes'].lat is defined %} | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ | ||||||
|     <div class="row"> |     <div class="row"> | ||||||
|         <div class="col-12 col-md-6 "> |         <div class="col-12 col-md-6 "> | ||||||
|             <h2>Lieux modifiés</h2>  |             <h2>Lieux modifiés</h2>  | ||||||
|               <table class="table table-striped table-hover table-responsive"> |               <table class="table table-striped table-hover table-responsive js-sort-table"> | ||||||
|                   <thead> |                   <thead> | ||||||
|                       <tr> |                       <tr> | ||||||
|                           <th>Nom</th> |                           <th>Nom</th> | ||||||
|  | @ -33,7 +33,7 @@ | ||||||
|         </div> |         </div> | ||||||
|         <div class="col-12 col-md-6 "> |         <div class="col-12 col-md-6 "> | ||||||
|             <h2>Lieux affichés</h2> |             <h2>Lieux affichés</h2> | ||||||
|               <table class="table table-striped table-hover table-responsive"> |               <table class="table table-striped table-hover table-responsive js-sort-table"> | ||||||
|                   <thead> |                   <thead> | ||||||
|                       <tr> |                       <tr> | ||||||
|                           <th>Nom</th>   |                           <th>Nom</th>   | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| {% block body %} | {% block body %} | ||||||
| <div class="container"> | <div class="container"> | ||||||
|     <h1>Commerces avec une note</h1> |     <h1>Commerces avec une note</h1> | ||||||
|     <table class="table table-striped"> |     <table class="table table-striped js-sort-table table-hover table-responsive"> | ||||||
|         <thead> |         <thead> | ||||||
|             <tr> |             <tr> | ||||||
|                 <th>Nom</th> |                 <th>Nom</th> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Tykayn
						Tykayn