/* * Welcome to your app's main JavaScript file! * * We recommend including the built version of this JavaScript file * (and its CSS file) in your base layout (base.html.twig). */ // any CSS you import will output into a single css file (app.css in this case) import './styles/app.css'; console.log('Hello World de app.js'); // Attendre le chargement du DOM document.addEventListener('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() function check_validity(e) { let errors = []; document.querySelectorAll('.is-invalid').forEach(input => { input.classList.remove('is-invalid'); }); const nameInput = document.querySelector('input[name="commerce_tag_value__name"]'); if (!nameInput.value.trim()) { errors.push("Le nom de l'établissement est obligatoire"); nameInput.classList.add('is-invalid'); } const emailInput = document.querySelector('input[name="commerce_tag_value__contact:email"]'); if (emailInput && emailInput.value) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(emailInput.value)) { errors.push("L'adresse email n'est pas valide"); emailInput.classList.add('is-invalid'); } } const phoneInput = document.querySelector('input[name="commerce_tag_value__contact:phone"]'); if (phoneInput && phoneInput.value) { const phoneRegex = /^(\+33|0)[1-9](\d{2}){4}$/; if (!phoneRegex.test(phoneInput.value.replace(/\s/g, ''))) { errors.push("Le numéro de téléphone n'est pas valide"); phoneInput.classList.add('is-invalid'); } } if (errors.length > 0) { e.preventDefault(); document.querySelector('#validation_messages').innerHTML = errors.join('
'); document.querySelector('#validation_messages').classList.add('is-invalid'); } } // Générer une couleur pastel aléatoire const genererCouleurPastel = () => { // Utiliser des valeurs plus claires (180-255) pour obtenir des tons pastel const r = Math.floor(Math.random() * 75 + 180); const g = Math.floor(Math.random() * 75 + 180); const b = Math.floor(Math.random() * 75 + 180); return `rgb(${r}, ${g}, ${b})`; }; const randombg = genererCouleurPastel(); // Appliquer la couleur au body 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 const btnShowAllFields = document.querySelector('#showAllFields'); if (btnShowAllFields) { console.log('btnShowAllFields détecté'); btnShowAllFields.addEventListener('click', () => { // Sélectionner tous les inputs dans le formulaire const form = document.querySelector('form'); if (form) { const hiddenInputs = form.querySelectorAll('.d-none'); hiddenInputs.forEach(input => { input.classList.toggle('d-none'); }); } }); } const btnClosedCommerce = document.querySelector('#closedCommerce'); if (btnClosedCommerce) { btnClosedCommerce.addEventListener('click', () => { if (!confirm('Êtes-vous sûr de vouloir signaler ce commerce comme fermé ?')) { return; } window.location.href = '/closed_commerce/' + document.getElementById('app_public_closed_commerce').value; }); } 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(); // lister les changesets de l'utilisateur osm-commerces async function listChangesets() { // const changesets = await fetch('https://api.openstreetmap.org/api/0.6/changesets?display_name=osm-commerce-fr'); // Ajouter le header Accept pour demander du JSON const options = { headers: { 'Accept': 'application/json' } }; const changesets = await fetch('https://api.openstreetmap.org/api/0.6/changesets?display_name=osm-commerce-fr', options); const data = await changesets.json(); console.log(data.changesets.length); // Grouper les changesets par période const now = new Date(); const last24h = new Date(now - 24 * 60 * 60 * 1000); const last7days = new Date(now - 7 * 24 * 60 * 60 * 1000); const last30days = new Date(now - 30 * 24 * 60 * 60 * 1000); const stats = { last24h: 0, last7days: 0, last30days: 0 }; data.changesets.forEach(changeset => { const changesetDate = new Date(changeset.closed_at); if (changesetDate >= last24h) { stats.last24h++; } if (changesetDate >= last7days) { stats.last7days++; } if (changesetDate >= last30days) { stats.last30days++; } }); // Afficher les statistiques const historyDiv = document.getElementById('userChangesHistory'); if (historyDiv) { historyDiv.innerHTML = `

Changesets créés :

Dernières 24h :
${stats.last24h}
7 derniers jours :
${stats.last7days}
30 derniers jours :
${stats.last30days}
`; } } // Vérifier si l'élément avec l'ID 'userChangesHistory' existe avant d'appeler la fonction if (document.getElementById('userChangesHistory')) { listChangesets(); } else { console.log('userChangesHistory non trouvé'); } function updateCompletionProgress() { const inputs = document.querySelectorAll('input[type="text"]'); let filledInputs = 0; let totalInputs = inputs.length; inputs.forEach(input => { if (input.value.trim() !== '') { filledInputs++; } }); const completionPercentage = (filledInputs / totalInputs) * 100; const progressBar = document.querySelector('#completion_progress .progress-bar'); if (progressBar) { progressBar.style.width = completionPercentage + '%'; progressBar.setAttribute('aria-valuenow', completionPercentage); document.querySelector('#completion_display').innerHTML = `Votre commerce est complété à ${Math.round(completionPercentage)}%`; } } document.querySelectorAll('input[type="text"]').forEach(input => { input.addEventListener('blur', updateCompletionProgress); }); const form = document.querySelector('form') if (form) { form.addEventListener('submit', check_validity); updateCompletionProgress() } updateCompletionProgress(); // Focus sur le premier champ texte au chargement const firstTextInput = document.querySelector('input.form-control'); if (firstTextInput) { firstTextInput.focus(); console.log('focus sur le premier champ texte', firstTextInput); } else { console.log('pas de champ texte trouvé'); } function parseCuisine() { const cuisineInput = document.querySelector('input[name="commerce_tag_value__cuisine"]'); // Récupérer tous les checkboxes de type de cuisine const cuisineCheckboxes = document.querySelectorAll('input[name="cuisine_type"]'); // Ajouter un écouteur d'événement sur chaque checkbox cuisineCheckboxes.forEach(checkbox => { checkbox.addEventListener('change', () => { // Récupérer toutes les checkboxes cochées const checkedCuisines = Array.from(document.querySelectorAll('input[name="cuisine_type"]:checked')) .map(input => input.value); // Mettre à jour l'input avec les valeurs séparées par des points-virgules cuisineInput.value = checkedCuisines.join(';'); }); }); } parseCuisine(); // Tri automatique des tableaux const tables = document.querySelectorAll('table'); tables.forEach(table => { table.classList.add('js-sort-table'); }); async function searchInseeCode(query) { try { // Afficher l'indicateur de chargement document.querySelector('#loading_search_insee').classList.remove('d-none'); const response = await fetch(`https://geo.api.gouv.fr/communes?nom=${query}&fields=nom,code,codesPostaux&limit=10`); const data = await response.json(); document.querySelector('#loading_search_insee').classList.add('d-none'); return data.map(commune => ({ label: `${commune.nom} (code insee ${commune.code})`, insee: commune.code, postcodes: commune.codesPostaux })); } catch (error) { console.error('Erreur lors de la recherche du code INSEE:', error); return []; } } // Modifier la fonction de recherche existante const searchInput = document.getElementById('app_admin_labourer'); const suggestionList = document.getElementById('suggestionList'); if (searchInput && suggestionList) { let timeoutId; searchInput.addEventListener('input', () => { clearTimeout(timeoutId); const query = searchInput.value.trim(); if (query.length < 2) { suggestionList.innerHTML = ''; return; } timeoutId = setTimeout(async () => { const suggestions = await searchInseeCode(query); suggestionList.innerHTML = ''; if (suggestions.length === 0) { const li = document.createElement('li'); li.style.cssText = ` padding: 8px 12px; color: #666; font-style: italic; `; li.textContent = 'Aucun résultat trouvé'; suggestionList.appendChild(li); return; } suggestions.forEach(suggestion => { const li = document.createElement('li'); li.style.cssText = ` padding: 8px 12px; cursor: pointer; border-bottom: 1px solid #eee; `; li.textContent = suggestion.label; li.addEventListener('mouseenter', () => { li.style.backgroundColor = '#f0f0f0'; }); li.addEventListener('mouseleave', () => { li.style.backgroundColor = 'white'; }); li.addEventListener('click', () => { searchInput.value = suggestion.insee; suggestionList.innerHTML = ''; labourer(); }); suggestionList.appendChild(li); }); }, 300); }); } });