Scripts

<script>
    window.addEventListener('alpine:init', () => {
        console.log('Alpine.js has been initialized');
        Alpine.store('screen', {
            isMobile: window.matchMedia('(max-width: 768px)').matches,
            isTablet: window.matchMedia('(max-width: 1024px)').matches,
            // Méthode d'initialisation pour mettre à jour `isMobile` en fonction de la taille de l'écran
            init() {
                const mobileMedia = window.matchMedia('(max-width: 768px)');
                const tabletMedia = window.matchMedia('(max-width: 1024px)');
                this.isMobile = mobileMedia.matches;
                this.isTablet = tabletMedia.matches;
                const updateScreen = (event, type) => {
                    if (type === 'mobile') this.isMobile = event.matches;
                    if (type === 'tablet') this.isTablet = event.matches;
                };
                [{
                        media: mobileMedia,
                        type: 'mobile'
                    },
                    {
                        media: tabletMedia,
                        type: 'tablet'
                    }
                ].forEach(({
                    media,
                    type
                }) => {
                    if (typeof media.onchange !== 'object') {
                        media.addListener((e) => updateScreen(e, type));
                    } else {
                        media.addEventListener('change', (e) => updateScreen(e, type));
                    }
                });
            }
        });
        Alpine.store('filter', {
            filters: [],
            getFilter(type, value) {
                return this.filters.some((filter) => filter.type === type && filter.value === value);
            },
            toggleFilter(type, value) {
                const filterArray = this.filters.selected[type];
                const index = filterArray.indexOf(value);
                if (index === -1) {
                    filterArray.push(value);
                } else {
                    filterArray.splice(index, 1);
                }
                this.applyFilters();
            },
            clearFilters() {
                this.filters = [];
            }
        });
        Alpine.store('asideBlocs', {
            asides: [],
            // Ajouter un nouvel aside au tableau
            addAside(name, customProperties = {}) {
                if (!this.asides.find(aside => aside.name === name)) {
                    this.asides.push({
                        name,
                        open: false,
                        ...customProperties, // Ajoute des propriétés spécifiques
                    });
                }
            },
            // Supprimer un aside par son nom
            removeAside(name) {
                this.asides = this.asides.filter(aside => aside.name !== name);
            },
            // Basculer l'état d'ouverture d'un aside
            toggleAside(name) {
                const aside = this.asides.find(aside => aside.name === name);
                if (aside) {
                    aside.open = !aside.open;
                    document.body.classList.toggle('overflow-hidden', aside.open);
                }
            },
            // Fermer un aside spécifique
            closeAside(name) {
                const aside = this.asides.find(aside => aside.name === name);
                if (aside) {
                    aside.open = false;
                    document.body.classList.remove('overflow-hidden');
                }
            },
            // Ouvrir un aside spécifique
            openAside(name) {
                const aside = this.asides.find(aside => aside.name === name);
                if (aside) {
                    aside.open = true;
                    document.body.classList.add('overflow-hidden');
                }
            }
        });
        Alpine.store('locator', {
            allStores: [], // Liste complète des magasins
            countStore: "",
            filteredStores: [], // Liste des magasins filtrés
            filteredDistanceStores: null, //Liste des magasins trié par distance
            selectedStore: null, // Magasin sélectionné
            isAudio: false, // Type de magasin affiché (true = audio, false = optique)
            loading: true, // État de chargement
            mapCenter: null, // Centre lat et lng de la google map
            mapInstance: null, // Instance de la google map. ( /!\ Toutes les fonctions ne sont pas disponible )
            // Listes des filtres disponibles (extraites des données)
            filterLists: {
                audio: {
                    mutuals: [], // Liste des mutuelles optique disponibles
                    brands: [], // Liste des marques optique disponibles
                    types: [] // Contiendra ['optic', 'teleophtalmologie'] selon disponibilité
                },
                optic: {
                    mutuals: [], // Liste des mutuelles optique disponibles
                    brands: [], // Liste des marques optique disponibles
                    types: [] // Contiendra ['audio', 'teleophtalmologie'] selon disponibilité
                }
            },
            // Modification des selectedFilters pour inclure les types
            selectedFilters: {
                audio: {
                    mutuals: [], // Mutuelles audio sélectionnées
                    brands: [], // Marques audio sélectionnées
                    types: [] // Les types sélectionnés
                },
                optic: {
                    mutuals: [], // Mutuelles audio sélectionnées
                    brands: [], // Marques audio sélectionnées
                    types: [] // Les types sélectionnés
                },
                search: ''
            },
            // Défault donnée des prises de rdv
            defaultStepperState: {
                steps: [{
                        id: 1,
                        title: 'Où',
                        completed: true
                    },
                    {
                        id: 2,
                        title: 'Quoi',
                        completed: false
                    },
                    {
                        id: 3,
                        title: 'Quand',
                        completed: false
                    },
                    {
                        id: 4,
                        title: 'Qui',
                        completed: false
                    }
                ],
                completed: false,
                currentStep: 2,
                code_mur: null,
                type: null,
                data: []
            },
            // Initialisation avec persist
            stepperData: Alpine.$persist(function() {
                return {
                    ...this.defaultStepperState
                }
            }).as('stepperData'),
            // Getters
            get currentFilterList() {
                return this.filterLists[this.currentType];
            },
            get currentType() {
                return this.isAudio ? 'audio' : 'optic';
            },
            get currentSelectedFilters() {
                return this.selectedFilters[this.currentType];
            },
            async init() {
                try {
                    // Détection du type depuis l'URL
                    try {
                        const pathname = window.location.pathname;
                        this.isAudio = pathname.includes('/acousticien');
                    } catch (error) {
                        console.error('Erreur lors de la détection du type depuis l\'URL:', error);
                        this.isAudio = false; // Défault value
                    }
                    await this.loadLibraries();
                    await this.loadStores();
                    this.extractFiltersFromStores();
                    this.applyFilters();
                } finally {
                    this.loading = false;
                }
            },
            async loadLibraries() {
                const [geometry] = await Promise.all([
                    google.maps.importLibrary("geometry"),
                ]);
            },
            async loadStores() {
                try {
                    const response = await fetch(`${window.location.origin}/js/json/getListv2.json`);
                    const data = await response.json();
                    if (data.success) {
                        this.countStore = data.data.totalCount ?? data.data.items.length;
                        this.allStores = data.data.items;
                    }
                } catch (error) {
                    console.error('Erreur lors du chargement des magasins:', error);
                    this.allStores = [];
                }
            },
            extractFiltersFromStores() {
                const storesArray = Object.values(this.allStores);
                // Pour les magasins Audio
                const audioStores = storesArray.filter(store =>
                    store.locations.some(location => location.is_audio)
                );
                // Comptage pour les mutuelles audio
                const audioMutualCounts = new Map();
                audioStores.forEach(store => {
                    store.locations
                        .filter(location => location.is_audio)
                        .forEach(location => {
                            (location.mutuelles || []).forEach(mutual => {
                                audioMutualCounts.set(mutual, (audioMutualCounts.get(mutual) || 0) + 1);
                            });
                        });
                });
                // Comptage pour les marques audio
                const audioBrandCounts = new Map();
                audioStores.forEach(store => {
                    store.locations
                        .filter(location => location.is_audio)
                        .forEach(location => {
                            (location.brands || []).forEach(brand => {
                                audioBrandCounts.set(brand, (audioBrandCounts.get(brand) || 0) + 1);
                            });
                        });
                });
                // Pour les magasins Optique
                const opticStores = storesArray.filter(store =>
                    store.locations.some(location => !location.is_audio)
                );
                // Comptage pour les mutuelles optique
                const opticMutualCounts = new Map();
                opticStores.forEach(store => {
                    store.locations
                        .filter(location => !location.is_audio)
                        .forEach(location => {
                            (location.mutuelles || []).forEach(mutual => {
                                opticMutualCounts.set(mutual, (opticMutualCounts.get(mutual) || 0) + 1);
                            });
                        });
                });
                // Comptage pour les marques optique
                const opticBrandCounts = new Map();
                opticStores.forEach(store => {
                    store.locations
                        .filter(location => !location.is_audio)
                        .forEach(location => {
                            (location.brands || []).forEach(brand => {
                                opticBrandCounts.set(brand, (opticBrandCounts.get(brand) || 0) + 1);
                            });
                        });
                });
                this.filterLists.audio = {
                    mutuals: Array.from(audioMutualCounts.entries())
                        .map(([id, count]) => ({
                            id,
                            label: id,
                            count
                        }))
                        .sort((a, b) => a.label.localeCompare(b.label)),
                    brands: Array.from(audioBrandCounts.entries())
                        .map(([id, count]) => ({
                            id,
                            label: id,
                            count
                        }))
                        .sort((a, b) => a.label.localeCompare(b.label)),
                    types: this.extractAvailableTypes(audioStores, true)
                };
                this.filterLists.optic = {
                    mutuals: Array.from(opticMutualCounts.entries())
                        .map(([id, count]) => ({
                            id,
                            label: id,
                            count
                        }))
                        .sort((a, b) => a.label.localeCompare(b.label)),
                    brands: Array.from(opticBrandCounts.entries())
                        .map(([id, count]) => ({
                            id,
                            label: id,
                            count
                        }))
                        .sort((a, b) => a.label.localeCompare(b.label)),
                    types: this.extractAvailableTypes(opticStores, false)
                };
            },
            // Méthode pour extraire les types disponibles
            extractAvailableTypes(stores, isAudio) {
                const types = new Set();
                const counts = {
                    [isAudio ? 'optic' : 'audio']: 0,
                    teleophtalmologie: 0
                };
                stores.forEach(store => {
                    // Vérifie si le magasin a l'autre type de service
                    const hasOtherService = store.locations.some(location =>
                        isAudio ? !location.is_audio : location.is_audio
                    );
                    if (hasOtherService) {
                        types.add(isAudio ? 'optic' : 'audio');
                        counts[isAudio ? 'optic' : 'audio']++;
                    }
                    // Vérifie si le magasin a la téléophtalmologie
                    const hasTelephtalmology = store.locations.some(location =>
                        location.attributes.teleophtalmologie?.value === "1"
                    );
                    if (hasTelephtalmology) {
                        types.add('teleophtalmologie');
                        counts.teleophtalmologie++;
                    }
                });
                // Retourne un tableau d'objets avec le type et son count
                return Array.from(types).sort().map(type => ({
                    id: type,
                    label: type,
                    count: counts[type]
                }));
            },
            // Dans toggleStoreType du store locator
            toggleStoreType(isAudio) {
                if (isAudio !== undefined) {
                    this.isAudio = isAudio;
                } else {
                    this.isAudio = !this.isAudio;
                }
                // Mise à jour de l'URL
                try {
                    const currentUrl = new URL(window.location.href);
                    const params = new URLSearchParams(currentUrl.search);
                    // Changer le pathname en gardant la même base
                    const newPathname = currentUrl.pathname.replace(
                        /(\/opticien|\/acousticien)/,
                        this.isAudio ? '/acousticien' : '/opticien'
                    );
                    // Construire la nouvelle URL avec les paramètres existants
                    const newUrl = `${currentUrl.origin}${newPathname}${params.toString() ? '?' + params.toString() : ''}`;
                    // Mettre à jour l'URL sans recharger la page
                    window.history.pushState({}, '', newUrl);
                } catch (error) {
                    console.error('Erreur lors de la mise à jour de l\'URL:', error);
                }
                this.selectedStore = null;
                this.clearFilters();
                this.applyFilters();
            },
            updateFilter(category, value) {
                const filters = this.selectedFilters[this.currentType][category];
                const index = filters.indexOf(value.id || value);
                if (index === -1) {
                    filters.push(value.id || value);
                } else {
                    filters.splice(index, 1);
                }
            },
            updateSearchTerm(term) {
                this.filters.selected.search = term;
                this.applyFilters();
            },
            clearFilters() {
                this.selectedFilters[this.currentType].mutuals = [];
                this.selectedFilters[this.currentType].brands = [];
                this.selectedFilters[this.currentType].types = [];
                this.selectedFilters.search = '';
                this.applyFilters();
            },
            // Application des filtres modifiée
            applyFilters() {
                const searchTerm = this.selectedFilters.search?.toLowerCase() || '';
                const selectedMutuals = this.selectedFilters[this.currentType].mutuals || [];
                const selectedBrands = this.selectedFilters[this.currentType].brands || [];
                const selectedTypes = this.selectedFilters[this.currentType].types || [];
                const filteredStores = Object.values(this.allStores).filter(store => {
                    // 1. Vérification du service principal (toujours requis)
                    const hasMainService = store.locations.some(location =>
                        this.isAudio ? location.is_audio : !location.is_audio
                    );
                    if (!hasMainService) return false;
                    // 2. Vérification des types additionnels si sélectionnés
                    if (selectedTypes.length > 0) {
                        // Vérifie l'autre service si sélectionné
                        const otherServiceType = this.isAudio ? 'optic' : 'audio';
                        if (selectedTypes.includes(otherServiceType)) {
                            const hasOtherService = store.locations.some(location =>
                                this.isAudio ? !location.is_audio : location.is_audio
                            );
                            if (!hasOtherService) return false;
                        }
                        // Vérifie la téléophtalmologie si sélectionnée
                        if (selectedTypes.includes('teleophtalmologie')) {
                            const hasTelephtalmology = store.locations.some(location =>
                                location.attributes.teleophtalmologie?.value === "1"
                            );
                            if (!hasTelephtalmology) return false;
                        }
                    }
                    // Filtres existants inchangés
                    if (searchTerm && !this.matchesSearch(store, searchTerm)) return false;
                    // Filtre par mutuelles
                    if (selectedMutuals.length > 0) {
                        const storeMutuals = store.locations
                            .filter(location => this.isAudio ? location.is_audio : !location.is_audio)
                            .flatMap(location => location.mutuelles || []);
                        if (!selectedMutuals.some(mutualId => storeMutuals.includes(mutualId))) {
                            return false;
                        }
                    }
                    if (selectedBrands.length > 0) {
                        const storeBrands = store.locations
                            .filter(location => this.isAudio ? location.is_audio : !location.is_audio)
                            .flatMap(location => location.brands || []);
                        if (!selectedBrands.some(brandId => storeBrands.includes(brandId))) {
                            return false;
                        }
                    }
                    return true;
                });
                this.filteredStores = filteredStores;
                this.updateDistances(filteredStores);
            },
            // Mise à jour de la méthode matchesSearch également si nécessaire
            matchesSearch(store, searchTerm) {
                return store.locations.some(location =>
                    location.name?.toLowerCase().includes(searchTerm) ||
                    store.city?.toLowerCase().includes(searchTerm) ||
                    store.zip?.toLowerCase().includes(searchTerm)
                );
            },
            selectStore(store) {
                this.selectedStore = store;
            },
            isFilterSelected(category, value) {
                return this.selectedFilters[category].includes(value);
            },
            updateDistances(filteredStores) {
                if (!this.mapInstance) return;
                const center = this.mapInstance.getCenter();
                const stores = Object.values(filteredStores || {});
                // Créer de nouveaux objets avec les distances au lieu de modifier les existants
                this.filteredDistanceStores = stores.map(store => {
                    const distance = google.maps.geometry.spherical.computeDistanceBetween(
                        center, {
                            lat: parseFloat(store.lat),
                            lng: parseFloat(store.lng)
                        }
                    );
                    // Retourner un nouvel objet au lieu de modifier l'original
                    return {
                        ...store,
                        distance: (distance / 1000).toFixed(1) + ' km'
                    };
                }).sort((a, b) => {
                    const distA = parseFloat(a.distance);
                    const distB = parseFloat(b.distance);
                    return distA - distB;
                });
            },
            setMapCenter(lat, lng, zoom) {
                if (!isNaN(lat) && !isNaN(lng)) {
                    this.mapInstance.panTo({
                        lat,
                        lng
                    })
                }
                if (zoom) {
                    this.mapInstance.setZoom(zoom)
                }
            },
            goToStore(store) {
                if (!this.mapInstance || !store) return;
                const lat = parseFloat(store.lat);
                const lng = parseFloat(store.lng);
                if (isNaN(lat) || isNaN(lng)) return;
                this.setMapCenter(lat, lng, 15)
                this.selectStore(store);
            },
            // PARTIE: PRISE DE RENDEZ-VOUS
            initStepper(store) {
                this.stepperData = {
                    ...this.defaultStepperState,
                    code_mur: store.mur_code,
                    type: this.isAudio,
                    data: [{
                        title: 'MAGASIN',
                        value: `${store.address}, ${store.zip} ${store.city}`
                    }]
                };
            },
            updateStepperData(title, value, targetStep) {
                if (!this.stepperData) return;
                // Trouver l'index de la donnée existante
                const currentIndex = this.stepperData.data.findIndex(item => item.title === title);
                // Mise à jour ou ajout des données
                if (currentIndex === -1) {
                    // Ajout d'une nouvelle entrée
                    this.stepperData.data.push({
                        title,
                        value
                    });
                } else if (this.stepperData.data[currentIndex].value !== value) {
                    // Si la valeur change, on supprime les données suivantes
                    this.stepperData.data = this.stepperData.data.slice(0, currentIndex + 1);
                    this.stepperData.data[currentIndex] = {
                        title,
                        value
                    };
                }
                // Mise à jour du step et des états des étapes si nécessaire
                if (targetStep) {
                    this.stepperData.currentStep = targetStep;
                    this.stepperData.steps = this.stepperData.steps.map(step => ({
                        ...step,
                        completed: step.id < targetStep
                    }));
                }
                // Alpine.persist se charge automatiquement de la persistance
                return this.stepperData;
            },
            completeStepperData(appointmentId) {
                if (!this.stepperData) return;
                // Marquer toutes les étapes comme complétées
                this.stepperData.steps = this.stepperData.steps.map(step => ({
                    ...step,
                    completed: true
                }));
                // Marquer le stepper comme complété et ajouter l'ID du rendez-vous
                this.stepperData.completed = true;
                this.stepperData.appointmentId = appointmentId;
            },
            goToStepperStep(targetStep) {
                // Vérifier que l'étape cible est valide
                if (!this.stepperData || targetStep < 1 || targetStep > 4) return;
                // Si on retourne à l'étape 1, réinitialiser complètement
                if (targetStep === 1) {
                    this.resetStepperData();
                    this.$store.asideBlocs.closeAside('storeLocatorAppointment');
                    return;
                }
                // Mettre à jour l'étape courante et l'état des étapes
                this.stepperData.currentStep = targetStep;
                this.stepperData.steps = this.stepperData.steps.map(step => ({
                    ...step,
                    completed: step.id < targetStep
                }));
            },
            resetStepperData() {
                // Réinitialiser avec les valeurs par défaut
                this.stepperData = Alpine.persist({
                    ...this.defaultStepperState
                }, 'stepperData');
            }
        });
    });
</script>
<script>
	window.addEventListener('alpine:init', () => {
		console.log('Alpine.js has been initialized');
		Alpine.store('screen', {
			isMobile: window.matchMedia('(max-width: 768px)').matches,
			isTablet: window.matchMedia('(max-width: 1024px)').matches,

			// Méthode d'initialisation pour mettre à jour `isMobile` en fonction de la taille de l'écran
			init() {
				const mobileMedia = window.matchMedia('(max-width: 768px)');
				const tabletMedia = window.matchMedia('(max-width: 1024px)');

				this.isMobile = mobileMedia.matches;
				this.isTablet = tabletMedia.matches;

				const updateScreen = (event, type) => {
					if (type === 'mobile') this.isMobile = event.matches;
					if (type === 'tablet') this.isTablet = event.matches;
				};

				[
					{media: mobileMedia, type: 'mobile'},
					{media: tabletMedia, type: 'tablet'}
				].forEach(({media, type}) => {
					if (typeof media.onchange !== 'object') {
						media.addListener((e) => updateScreen(e, type));
					} else {
						media.addEventListener('change', (e) => updateScreen(e, type));
					}
				});
			}
		});

		Alpine.store('filter', {
			filters: [],
			getFilter(type, value) {
				return this.filters.some((filter) => filter.type === type && filter.value === value);
			},
			toggleFilter(type, value) {
				const filterArray = this.filters.selected[type];
				const index = filterArray.indexOf(value);

				if (index === -1) {
					filterArray.push(value);
				} else {
					filterArray.splice(index, 1);
				}

				this.applyFilters();
			},
			clearFilters() {
				this.filters = [];
			}
		});

		Alpine.store('asideBlocs', {
			asides: [],

			// Ajouter un nouvel aside au tableau
			addAside(name, customProperties = {}) {
				if (!this.asides.find(aside => aside.name === name)) {
					this.asides.push({
						name,
						open: false,
						...customProperties, // Ajoute des propriétés spécifiques
					});
				}
			},


			// Supprimer un aside par son nom
			removeAside(name) {
				this.asides = this.asides.filter(aside => aside.name !== name);
			},

			// Basculer l'état d'ouverture d'un aside
			toggleAside(name) {
				const aside = this.asides.find(aside => aside.name === name);
				if (aside) {
					aside.open = !aside.open;
					document.body.classList.toggle('overflow-hidden', aside.open);
				}
			},

			// Fermer un aside spécifique
			closeAside(name) {
				const aside = this.asides.find(aside => aside.name === name);
				if (aside) {
					aside.open = false;
					document.body.classList.remove('overflow-hidden');
				}
			},

			// Ouvrir un aside spécifique
			openAside(name) {
				const aside = this.asides.find(aside => aside.name === name);
				if (aside) {
					aside.open = true;
					document.body.classList.add('overflow-hidden');
				}
			}
		});

		Alpine.store('locator', {
			allStores: [],          // Liste complète des magasins
			countStore: "",
			filteredStores: [],     // Liste des magasins filtrés
			filteredDistanceStores: null, //Liste des magasins trié par distance
			selectedStore: null,    // Magasin sélectionné
			isAudio: false,         // Type de magasin affiché (true = audio, false = optique)
			loading: true,          // État de chargement
			mapCenter: null,        // Centre lat et lng de la google map
			mapInstance: null,      // Instance de la google map. ( /!\ Toutes les fonctions ne sont pas disponible )

			// Listes des filtres disponibles (extraites des données)
			filterLists: {
				audio: {
					mutuals: [],    // Liste des mutuelles optique disponibles
					brands: [],     // Liste des marques optique disponibles
					types: []       // Contiendra ['optic', 'teleophtalmologie'] selon disponibilité
				},
				optic: {
					mutuals: [],    // Liste des mutuelles optique disponibles
					brands: [],     // Liste des marques optique disponibles
					types: []       // Contiendra ['audio', 'teleophtalmologie'] selon disponibilité
				}
			},

			// Modification des selectedFilters pour inclure les types
			selectedFilters: {
				audio: {
					mutuals: [],    // Mutuelles audio sélectionnées
					brands: [],     // Marques audio sélectionnées
					types: []       // Les types sélectionnés
				},
				optic: {
					mutuals: [],    // Mutuelles audio sélectionnées
					brands: [],     // Marques audio sélectionnées
					types: []       // Les types sélectionnés
				},
				search: ''
			},

			// Défault donnée des prises de rdv
			defaultStepperState: {
				steps: [
					{id: 1, title: 'Où', completed: true},
					{id: 2, title: 'Quoi', completed: false},
					{id: 3, title: 'Quand', completed: false},
					{id: 4, title: 'Qui', completed: false}
				],
				completed: false,
				currentStep: 2,
				code_mur: null,
				type: null,
				data: []
			},

			// Initialisation avec persist
			stepperData: Alpine.$persist(function () {
				return {...this.defaultStepperState}
			}).as('stepperData'),

			// Getters
			get currentFilterList() {
				return this.filterLists[this.currentType];
			},
			get currentType() {
				return this.isAudio ? 'audio' : 'optic';
			},
			get currentSelectedFilters() {
				return this.selectedFilters[this.currentType];
			},
			async init() {
				try {
					// Détection du type depuis l'URL
					try {
						const pathname = window.location.pathname;
						this.isAudio = pathname.includes('/acousticien');
					} catch (error) {
						console.error('Erreur lors de la détection du type depuis l\'URL:', error);
						this.isAudio = false; // Défault value
					}

					await this.loadLibraries();
					await this.loadStores();
					this.extractFiltersFromStores();
					this.applyFilters();
				} finally {
					this.loading = false;
				}
			},

			async loadLibraries() {
				const [geometry] = await Promise.all([
					google.maps.importLibrary("geometry"),
				]);
			},

			async loadStores() {
				try {
					const response = await fetch(`${window.location.origin}/js/json/getListv2.json`);
					const data = await response.json();
					if (data.success) {
						this.countStore = data.data.totalCount ?? data.data.items.length;
						this.allStores = data.data.items;
					}
				} catch (error) {
					console.error('Erreur lors du chargement des magasins:', error);
					this.allStores = [];
				}
			},

			extractFiltersFromStores() {
				const storesArray = Object.values(this.allStores);

				// Pour les magasins Audio
				const audioStores = storesArray.filter(store =>
					store.locations.some(location => location.is_audio)
				);

				// Comptage pour les mutuelles audio
				const audioMutualCounts = new Map();
				audioStores.forEach(store => {
					store.locations
						.filter(location => location.is_audio)
						.forEach(location => {
							(location.mutuelles || []).forEach(mutual => {
								audioMutualCounts.set(mutual, (audioMutualCounts.get(mutual) || 0) + 1);
							});
						});
				});

				// Comptage pour les marques audio
				const audioBrandCounts = new Map();
				audioStores.forEach(store => {
					store.locations
						.filter(location => location.is_audio)
						.forEach(location => {
							(location.brands || []).forEach(brand => {
								audioBrandCounts.set(brand, (audioBrandCounts.get(brand) || 0) + 1);
							});
						});
				});

				// Pour les magasins Optique
				const opticStores = storesArray.filter(store =>
					store.locations.some(location => !location.is_audio)
				);

				// Comptage pour les mutuelles optique
				const opticMutualCounts = new Map();
				opticStores.forEach(store => {
					store.locations
						.filter(location => !location.is_audio)
						.forEach(location => {
							(location.mutuelles || []).forEach(mutual => {
								opticMutualCounts.set(mutual, (opticMutualCounts.get(mutual) || 0) + 1);
							});
						});
				});

				// Comptage pour les marques optique
				const opticBrandCounts = new Map();
				opticStores.forEach(store => {
					store.locations
						.filter(location => !location.is_audio)
						.forEach(location => {
							(location.brands || []).forEach(brand => {
								opticBrandCounts.set(brand, (opticBrandCounts.get(brand) || 0) + 1);
							});
						});
				});

				this.filterLists.audio = {
					mutuals: Array.from(audioMutualCounts.entries())
						.map(([id, count]) => ({id, label: id, count}))
						.sort((a, b) => a.label.localeCompare(b.label)),
					brands: Array.from(audioBrandCounts.entries())
						.map(([id, count]) => ({id, label: id, count}))
						.sort((a, b) => a.label.localeCompare(b.label)),
					types: this.extractAvailableTypes(audioStores, true)
				};

				this.filterLists.optic = {
					mutuals: Array.from(opticMutualCounts.entries())
						.map(([id, count]) => ({id, label: id, count}))
						.sort((a, b) => a.label.localeCompare(b.label)),
					brands: Array.from(opticBrandCounts.entries())
						.map(([id, count]) => ({id, label: id, count}))
						.sort((a, b) => a.label.localeCompare(b.label)),
					types: this.extractAvailableTypes(opticStores, false)
				};
			},

			// Méthode pour extraire les types disponibles
			extractAvailableTypes(stores, isAudio) {
				const types = new Set();
				const counts = {
					[isAudio ? 'optic' : 'audio']: 0,
					teleophtalmologie: 0
				};

				stores.forEach(store => {
					// Vérifie si le magasin a l'autre type de service
					const hasOtherService = store.locations.some(location =>
						isAudio ? !location.is_audio : location.is_audio
					);
					if (hasOtherService) {
						types.add(isAudio ? 'optic' : 'audio');
						counts[isAudio ? 'optic' : 'audio']++;
					}

					// Vérifie si le magasin a la téléophtalmologie
					const hasTelephtalmology = store.locations.some(location =>
						location.attributes.teleophtalmologie?.value === "1"
					);
					if (hasTelephtalmology) {
						types.add('teleophtalmologie');
						counts.teleophtalmologie++;
					}
				});

				// Retourne un tableau d'objets avec le type et son count
				return Array.from(types).sort().map(type => ({
					id: type,
					label: type,
					count: counts[type]
				}));
			},

			// Dans toggleStoreType du store locator
			toggleStoreType(isAudio) {
				if (isAudio !== undefined) {
					this.isAudio = isAudio;
				} else {
					this.isAudio = !this.isAudio;
				}

				// Mise à jour de l'URL
				try {
					const currentUrl = new URL(window.location.href);
					const params = new URLSearchParams(currentUrl.search);

					// Changer le pathname en gardant la même base
					const newPathname = currentUrl.pathname.replace(
						/(\/opticien|\/acousticien)/,
						this.isAudio ? '/acousticien' : '/opticien'
					);

					// Construire la nouvelle URL avec les paramètres existants
					const newUrl = `${currentUrl.origin}${newPathname}${params.toString() ? '?' + params.toString() : ''}`;

					// Mettre à jour l'URL sans recharger la page
					window.history.pushState({}, '', newUrl);
				} catch (error) {
					console.error('Erreur lors de la mise à jour de l\'URL:', error);
				}

				this.selectedStore = null;
				this.clearFilters();
				this.applyFilters();
			},

			updateFilter(category, value) {
				const filters = this.selectedFilters[this.currentType][category];
				const index = filters.indexOf(value.id || value);

				if (index === -1) {
					filters.push(value.id || value);
				} else {
					filters.splice(index, 1);
				}
			},

			updateSearchTerm(term) {
				this.filters.selected.search = term;
				this.applyFilters();
			},

			clearFilters() {
				this.selectedFilters[this.currentType].mutuals = [];
				this.selectedFilters[this.currentType].brands = [];
				this.selectedFilters[this.currentType].types = [];
				this.selectedFilters.search = '';
				this.applyFilters();
			},

			// Application des filtres modifiée
			applyFilters() {
				const searchTerm = this.selectedFilters.search?.toLowerCase() || '';
				const selectedMutuals = this.selectedFilters[this.currentType].mutuals || [];
				const selectedBrands = this.selectedFilters[this.currentType].brands || [];
				const selectedTypes = this.selectedFilters[this.currentType].types || [];

				const filteredStores = Object.values(this.allStores).filter(store => {
					// 1. Vérification du service principal (toujours requis)
					const hasMainService = store.locations.some(location =>
						this.isAudio ? location.is_audio : !location.is_audio
					);
					if (!hasMainService) return false;

					// 2. Vérification des types additionnels si sélectionnés
					if (selectedTypes.length > 0) {
						// Vérifie l'autre service si sélectionné
						const otherServiceType = this.isAudio ? 'optic' : 'audio';
						if (selectedTypes.includes(otherServiceType)) {
							const hasOtherService = store.locations.some(location =>
								this.isAudio ? !location.is_audio : location.is_audio
							);
							if (!hasOtherService) return false;
						}

						// Vérifie la téléophtalmologie si sélectionnée
						if (selectedTypes.includes('teleophtalmologie')) {
							const hasTelephtalmology = store.locations.some(location =>
								location.attributes.teleophtalmologie?.value === "1"
							);
							if (!hasTelephtalmology) return false;
						}
					}

					// Filtres existants inchangés
					if (searchTerm && !this.matchesSearch(store, searchTerm)) return false;

					// Filtre par mutuelles
					if (selectedMutuals.length > 0) {
						const storeMutuals = store.locations
							.filter(location => this.isAudio ? location.is_audio : !location.is_audio)
							.flatMap(location => location.mutuelles || []);
						if (!selectedMutuals.some(mutualId => storeMutuals.includes(mutualId))) {
							return false;
						}
					}

					if (selectedBrands.length > 0) {
						const storeBrands = store.locations
							.filter(location => this.isAudio ? location.is_audio : !location.is_audio)
							.flatMap(location => location.brands || []);
						if (!selectedBrands.some(brandId => storeBrands.includes(brandId))) {
							return false;
						}
					}

					return true;
				});

				this.filteredStores = filteredStores;
				this.updateDistances(filteredStores);
			},

			// Mise à jour de la méthode matchesSearch également si nécessaire
			matchesSearch(store, searchTerm) {
				return store.locations.some(location =>
					location.name?.toLowerCase().includes(searchTerm) ||
					store.city?.toLowerCase().includes(searchTerm) ||
					store.zip?.toLowerCase().includes(searchTerm)
				);
			},

			selectStore(store) {
				this.selectedStore = store;
			},

			isFilterSelected(category, value) {
				return this.selectedFilters[category].includes(value);
			},

			updateDistances(filteredStores) {
				if (!this.mapInstance) return;

				const center = this.mapInstance.getCenter();
				const stores = Object.values(filteredStores || {});

				// Créer de nouveaux objets avec les distances au lieu de modifier les existants
				this.filteredDistanceStores = stores.map(store => {
					const distance = google.maps.geometry.spherical.computeDistanceBetween(
						center,
						{lat: parseFloat(store.lat), lng: parseFloat(store.lng)}
					);

					// Retourner un nouvel objet au lieu de modifier l'original
					return {
						...store,
						distance: (distance / 1000).toFixed(1) + ' km'
					};
				}).sort((a, b) => {
					const distA = parseFloat(a.distance);
					const distB = parseFloat(b.distance);
					return distA - distB;
				});
			},

			setMapCenter(lat, lng, zoom) {
				if (!isNaN(lat) && !isNaN(lng)) {
					this.mapInstance.panTo({lat, lng})
				}
				if (zoom) {
					this.mapInstance.setZoom(zoom)
				}
			},
			goToStore(store) {
				if (!this.mapInstance || !store) return;

				const lat = parseFloat(store.lat);
				const lng = parseFloat(store.lng);

				if (isNaN(lat) || isNaN(lng)) return;

				this.setMapCenter(lat, lng, 15)

				this.selectStore(store);
			},

			// PARTIE: PRISE DE RENDEZ-VOUS
			initStepper(store) {
				this.stepperData = {
					...this.defaultStepperState,
					code_mur: store.mur_code,
					type: this.isAudio,
					data: [{
						title: 'MAGASIN',
						value: `${store.address}, ${store.zip} ${store.city}`
					}]
				};
			},

			updateStepperData(title, value, targetStep) {
				if (!this.stepperData) return;

				// Trouver l'index de la donnée existante
				const currentIndex = this.stepperData.data.findIndex(item => item.title === title);

				// Mise à jour ou ajout des données
				if (currentIndex === -1) {
					// Ajout d'une nouvelle entrée
					this.stepperData.data.push({title, value});
				} else if (this.stepperData.data[currentIndex].value !== value) {
					// Si la valeur change, on supprime les données suivantes
					this.stepperData.data = this.stepperData.data.slice(0, currentIndex + 1);
					this.stepperData.data[currentIndex] = {title, value};
				}

				// Mise à jour du step et des états des étapes si nécessaire
				if (targetStep) {
					this.stepperData.currentStep = targetStep;
					this.stepperData.steps = this.stepperData.steps.map(step => ({
						...step,
						completed: step.id < targetStep
					}));
				}

				// Alpine.persist se charge automatiquement de la persistance
				return this.stepperData;
			},
			completeStepperData(appointmentId) {
				if (!this.stepperData) return;

				// Marquer toutes les étapes comme complétées
				this.stepperData.steps = this.stepperData.steps.map(step => ({
					...step,
					completed: true
				}));

				// Marquer le stepper comme complété et ajouter l'ID du rendez-vous
				this.stepperData.completed = true;
				this.stepperData.appointmentId = appointmentId;
			},
			goToStepperStep(targetStep) {
				// Vérifier que l'étape cible est valide
				if (!this.stepperData || targetStep < 1 || targetStep > 4) return;

				// Si on retourne à l'étape 1, réinitialiser complètement
				if (targetStep === 1) {
					this.resetStepperData();
					this.$store.asideBlocs.closeAside('storeLocatorAppointment');
					return;
				}

				// Mettre à jour l'étape courante et l'état des étapes
				this.stepperData.currentStep = targetStep;
				this.stepperData.steps = this.stepperData.steps.map(step => ({
					...step,
					completed: step.id < targetStep
				}));
			},

			resetStepperData() {
				// Réinitialiser avec les valeurs par défaut
				this.stepperData = Alpine.persist({
					...this.defaultStepperState
				}, 'stepperData');
			}

		});
	});
</script>
/* No context defined. */

No notes defined.