{"id":4315,"date":"2025-12-20T11:07:30","date_gmt":"2025-12-20T03:07:30","guid":{"rendered":"https:\/\/phi-cycling.cc\/trouver-un-distributeur\/"},"modified":"2026-04-08T09:42:34","modified_gmt":"2026-04-08T01:42:34","slug":"trouver-un-distributeur","status":"publish","type":"page","link":"https:\/\/phi-cycling.cc\/fr\/trouver-un-distributeur\/","title":{"rendered":"Trouver un distributeur"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"4315\" class=\"elementor elementor-4315 elementor-1261\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-693777e e-flex e-con-boxed e-con e-parent\" data-id=\"693777e\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-e9a1148 elementor-widget elementor-widget-text-editor\" data-id=\"e9a1148\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>Notre r\u00e9seau de distributeurs couvre le monde entier, ce qui garantit que nos produits sont facilement accessibles o\u00f9 que vous soyez. Chaque distributeur conna\u00eet parfaitement la collection PHi, ce qui lui permet de fournir des conseils d&rsquo;expert et un service de premier ordre. <\/p><p>Trouvez le distributeur le plus proche de chez vous et d\u00e9couvrez une gamme de produits qui transformeront votre exp\u00e9rience du v\u00e9lo.  <br>Roulez en toute confiance, \u00e9quip\u00e9 du meilleur mat\u00e9riel cycliste de la plan\u00e8te.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e201d22 elementor-widget elementor-widget-shortcode\" data-id=\"e201d22\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">        <div id=\"osm-stores-container\" class=\"osm-stores-wrapper osm-responsive-layout\">\r\n            <!-- \u63a7\u5236\u533a\uff1a\u7b5b\u9009\u5668\u548c\u641c\u7d22\u6846 -->\r\n            <div class=\"controls-section\">\r\n                <div class=\"stores-country-filter\">\r\n                    <label for=\"stores-country-select-osm-stores-map-69e26ff3355ef\"><strong>Filter by Country:<\/strong><\/label>\r\n                    <select id=\"stores-country-select-osm-stores-map-69e26ff3355ef\">\r\n                        <option value=\"\">All Countries<\/option>\r\n                                                    <option value=\"Denmark\">Denmark<\/option>\r\n                                                    <option value=\"France\">France<\/option>\r\n                                                    <option value=\"Italy\">Italy<\/option>\r\n                                                    <option value=\"United States\">United States<\/option>\r\n                                            <\/select>\r\n                <\/div>\r\n\r\n                <div class=\"stores-search-box\">\r\n                    <input type=\"text\" id=\"stores-search-input-osm-stores-map-69e26ff3355ef\" placeholder=\"Search by city or other keywords...\" \/>\r\n                    <button id=\"stores-search-btn-osm-stores-map-69e26ff3355ef\">Search<\/button>\r\n                <\/div>\r\n            <\/div>\r\n\r\n            <!-- \u4e3b\u4f53\u5185\u5bb9\u533a\uff1a\u5730\u56fe\u548c\u5217\u8868 -->\r\n            <div class=\"main-content-section\">\r\n                <!-- \u5730\u56fe\u533a\u57df -->\r\n                <div id=\"osm-stores-map-69e26ff3355ef\" class=\"store-map-element store-map-js-init\"><\/div>\r\n                \r\n                <!-- \u95e8\u5e97\u5217\u8868\u533a\u57df -->\r\n                <div id=\"stores-list-results-osm-stores-map-69e26ff3355ef\" class=\"stores-list-panel\">\r\n                    <h3>Store Locations<\/h3>\r\n                    <div class=\"stores-list-content\">\r\n                        Loading stores...\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <script type=\"text\/javascript\">\r\n        (function(mapId, buttonId, inputId, resultsId, countrySelectId) {\r\n            'use strict';\r\n\r\n            let globalMap = null;\r\n            let globalMarkers = [];\r\n\r\n            if (document.readyState === 'loading') {\r\n                document.addEventListener('DOMContentLoaded', () => initializeMap(mapId, buttonId, inputId, resultsId, countrySelectId));\r\n            } else {\r\n                initializeMap(mapId, buttonId, inputId, resultsId, countrySelectId);\r\n            }\r\n\r\n            function initializeMap(mapId, buttonId, inputId, resultsId, countrySelectId) {\r\n                try {\r\n                    const mapElement = document.getElementById(mapId);\r\n                    if (!mapElement) {\r\n                        console.error('[StoreMap] ERROR: Map element with ID \"' + mapId + '\" not found.');\r\n                        return;\r\n                    }\r\n\r\n                    if (typeof L === 'undefined') {\r\n                        console.error('[StoreMap] ERROR: Leaflet library (L) is not defined.');\r\n                        mapElement.innerHTML = '<div style=\"color: red; padding: 20px; text-align: center;\">Map failed to load (Missing Leaflet library).<\/div>';\r\n                        return;\r\n                    }\r\n\r\n                    globalMap = L.map(mapId).setView([20, 0], 2);\r\n\r\n                    L.tileLayer('https:\/\/{s}.tile.openstreetmap.org\/{z}\/{x}\/{y}.png', {\r\n                        attribution: '&copy; <a href=\"https:\/\/www.openstreetmap.org\/copyright\">OpenStreetMap<\/a> contributors',\r\n                        maxZoom: 19\r\n                    }).addTo(globalMap);\r\n\r\n                    setTimeout(() => {\r\n                        if (globalMap) globalMap.invalidateSize(true);\r\n                    }, 100);\r\n\r\n                    bindSearchEvents(buttonId, inputId, resultsId);\r\n                    bindCountryFilterEvent(countrySelectId, resultsId);\r\n                    loadAllStores(resultsId);\r\n\r\n                } catch (e) {\r\n                    console.error('[StoreMap] FATAL ERROR during initialization:', e);\r\n                    const elem = document.getElementById(mapId);\r\n                    if (elem) elem.innerHTML = '<div style=\"color: red; padding: 20px; text-align: center;\">Init Error: ' + e.message + '<\/div>';\r\n                }\r\n            }\r\n\r\n            function bindSearchEvents(buttonId, inputId, resultsId) {\r\n                const btn = document.getElementById(buttonId);\r\n                const inp = document.getElementById(inputId);\r\n\r\n                if (btn) {\r\n                    btn.onclick = () => performSearch(inputId, resultsId, null, 'search');\r\n                }\r\n                if (inp) {\r\n                    inp.onkeypress = (e) => { if (e.key === 'Enter') performSearch(inputId, resultsId, null, 'search'); };\r\n                }\r\n            }\r\n\r\n            function bindCountryFilterEvent(selectId, resultsId) {\r\n                const selectElement = document.getElementById(selectId);\r\n                if (selectElement) {\r\n                    selectElement.onchange = (e) => {\r\n                        const selectedCountry = e.target.value;\r\n                        performSearch(null, resultsId, selectedCountry, 'filter');\r\n                    };\r\n                }\r\n            }\r\n\r\n            function loadAllStores(resultsId) {\r\n                performSearch(null, resultsId, '', 'init');\r\n            }\r\n\r\n            async function performSearch(inputId, resultsId, termOverride = null, triggerSource = 'search') {\r\n                let term = termOverride;\r\n                \r\n                if (triggerSource === 'search' && inputId) {\r\n                    const inputElement = document.getElementById(inputId);\r\n                    if (inputElement) {\r\n                        term = inputElement.value.trim();\r\n                    }\r\n                }\r\n                \r\n                if (triggerSource === 'filter' && termOverride !== null) {\r\n                    term = termOverride;\r\n                }\r\n                \r\n                if (triggerSource === 'init') {\r\n                    term = '';\r\n                }\r\n\r\n                try {\r\n                    const formData = new FormData();\r\n                    formData.append('action', 'search_stores');\r\n                    formData.append('search_term', term);\r\n                    formData.append('search_type', triggerSource === 'search' && term ? 'city' : (triggerSource === 'filter' && term ? 'country' : 'all'));\r\n                    formData.append('nonce', Date.now()); \/\/ \u4f7f\u7528\u65f6\u95f4\u6233\u4ee3\u66ffnonce\r\n\r\n                    const response = await fetch(window.ajaxurl || '\/wp-admin\/admin-ajax.php', {\r\n                        method: 'POST',\r\n                        body: formData\r\n                    });\r\n\r\n                    if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);\r\n\r\n                    const data = await response.json();\r\n                    console.log('[StoreMap] AJAX Response Data:', data);\r\n\r\n                    if (data && data.success && Array.isArray(data.data)) {\r\n                        clearMarkers();\r\n                        const stores = data.data;\r\n\r\n                        \/\/ \u66f4\u65b0\u53f3\u4fa7\u5217\u8868\r\n                        if (resultsId) {\r\n                            const resultsElement = document.getElementById(resultsId);\r\n                            if (resultsElement) {\r\n                                let listHtml = '';\r\n                                if (stores.length === 0 && term) {\r\n                                    listHtml = '<p class=\"no-results\">No stores found for \"' + escapeHtml(term) + '\" in this category.<\/p>';\r\n                                } else if (stores.length > 0) {\r\n                                    listHtml = '<div class=\"stores-grid\">';\r\n                                    stores.forEach(s => {\r\n                                        \/\/ \u5b89\u5168\u68c0\u67e5\u6bcf\u4e2a\u5bf9\u8c61\u7684\u5c5e\u6027\r\n                                        if (!s || typeof s !== 'object') return;\r\n                                        \r\n                                        const title = s.title || 'Unknown Store';\r\n                                        const address = s.address || 'Address not provided';\r\n                                        const phone = s.phone || 'Phone not provided';\r\n                                        const city = s.city || 'City not provided';\r\n                                        const country = s.country || 'Country not provided';\r\n                                        const lat = s.lat;\r\n                                        const lng = s.lng;\r\n                                        \r\n                                        listHtml += '<div class=\"store-item\"><h3>' + escapeHtml(title) + '<\/h3>';\r\n                                        listHtml += '<p><strong>Address:<\/strong> ' + escapeHtml(address) + '<\/p>';\r\n                                        listHtml += '<p><strong>Phone:<\/strong> ' + escapeHtml(phone) + '<\/p>';\r\n                                        listHtml += '<p><strong>Location:<\/strong> ' + escapeHtml(city) + ', ' + escapeHtml(country) + '<\/p>';\r\n                                        if (lat && lng) {\r\n                                            listHtml += '<button class=\"view-on-map-btn\" onclick=\"focusOnStore_' + mapId + '(' + lat + ',' + lng + ',\\'' + escapeHtml(title) + '\\',\\'' + escapeHtml(address) + '\\')\">View on Map<\/button>';\r\n                                        }\r\n                                        listHtml += '<\/div>';\r\n                                        if (lat && lng) addMarker(s);\r\n                                    });\r\n                                    listHtml += '<\/div>';\r\n                                } else if (triggerSource === 'init') {\r\n                                    \/\/ \u521d\u59cb\u5316\u65f6\u52a0\u8f7d\u5168\u90e8\r\n                                    listHtml = '<div class=\"stores-grid\">';\r\n                                    stores.forEach(s => {\r\n                                        \/\/ \u5b89\u5168\u68c0\u67e5\u6bcf\u4e2a\u5bf9\u8c61\u7684\u5c5e\u6027\r\n                                        if (!s || typeof s !== 'object') return;\r\n                                        \r\n                                        const title = s.title || 'Unknown Store';\r\n                                        const address = s.address || 'Address not provided';\r\n                                        const phone = s.phone || 'Phone not provided';\r\n                                        const city = s.city || 'City not provided';\r\n                                        const country = s.country || 'Country not provided';\r\n                                        const lat = s.lat;\r\n                                        const lng = s.lng;\r\n                                        \r\n                                        listHtml += '<div class=\"store-item\"><h3>' + escapeHtml(title) + '<\/h3>';\r\n                                        listHtml += '<p><strong>Address:<\/strong> ' + escapeHtml(address) + '<\/p>';\r\n                                        listHtml += '<p><strong>Phone:<\/strong> ' + escapeHtml(phone) + '<\/p>';\r\n                                        listHtml += '<p><strong>Location:<\/strong> ' + escapeHtml(city) + ', ' + escapeHtml(country) + '<\/p>';\r\n                                        if (lat && lng) {\r\n                                            listHtml += '<button class=\"view-on-map-btn\" onclick=\"focusOnStore_' + mapId + '(' + lat + ',' + lng + ',\\'' + escapeHtml(title) + '\\',\\'' + escapeHtml(address) + '\\')\">View on Map<\/button>';\r\n                                        }\r\n                                        listHtml += '<\/div>';\r\n                                        if (lat && lng) addMarker(s);\r\n                                    });\r\n                                    listHtml += '<\/div>';\r\n                                }\r\n                                resultsElement.querySelector('.stores-list-content').innerHTML = listHtml;\r\n                            }\r\n                        } else {\r\n                            stores.forEach(s => {\r\n                                if (s && s.lat && s.lng) addMarker(s);\r\n                            });\r\n                        }\r\n\r\n                        if (stores.length > 0) fitBounds();\r\n                    } else {\r\n                        console.error('[StoreMap] Search API returned invalid data:', data);\r\n                        if (resultsId) {\r\n                            document.getElementById(resultsId).querySelector('.stores-list-content').innerHTML = '<p class=\"error\">API Error: Invalid response format<\/p>';\r\n                        }\r\n                    }\r\n                } catch (error) {\r\n                    console.error('[StoreMap] Error during search:', error);\r\n                    if (resultsId) {\r\n                        document.getElementById(resultsId).querySelector('.stores-list-content').innerHTML = '<p class=\"error\">An error occurred during search: ' + error.message + '<\/p>';\r\n                    }\r\n                }\r\n            }\r\n\r\n            function addMarker(s) {\r\n                if (!globalMap || !s || typeof s !== 'object' || !s.lat || !s.lng) return;\r\n                const lat = parseFloat(s.lat);\r\n                const lng = parseFloat(s.lng);\r\n                if (isNaN(lat) || isNaN(lng)) return;\r\n\r\n                const marker = L.marker([lat, lng]).addTo(globalMap);\r\n\r\n                const title = s.title || 'Unknown Store';\r\n                const address = s.address || 'Address not provided';\r\n                const phone = s.phone || 'Phone not provided';\r\n                const city = s.city || 'City not provided';\r\n                const country = s.country || 'Country not provided';\r\n\r\n                const popupContent = '<div style=\"min-width:200px;\">' +\r\n                    '<h3>' + escapeHtml(title) + '<\/h3>' +\r\n                    '<p><strong>Address:<\/strong> ' + escapeHtml(address) + '<\/p>' +\r\n                    '<p><strong>Phone:<\/strong> ' + escapeHtml(phone) + '<\/p>' +\r\n                    '<p><strong>Location:<\/strong> ' + escapeHtml(city) + ', ' + escapeHtml(country) + '<\/p>' +\r\n                    '<\/div>';\r\n\r\n                marker.bindPopup(popupContent);\r\n                globalMarkers.push(marker);\r\n            }\r\n\r\n            function clearMarkers() {\r\n                if (!globalMap) return;\r\n                globalMarkers.forEach(m => {\r\n                    if (globalMap.hasLayer(m)) globalMap.removeLayer(m);\r\n                });\r\n                globalMarkers = [];\r\n            }\r\n\r\n            function fitBounds() {\r\n                if (!globalMap || globalMarkers.length === 0) return;\r\n                const group = new L.featureGroup(globalMarkers);\r\n                globalMap.fitBounds(group.getBounds().pad(0.1));\r\n            }\r\n\r\n            window['focusOnStore_' + mapId] = function(lat, lng, title, address) {\r\n                if (!globalMap) return;\r\n                const parsedLat = parseFloat(lat);\r\n                const parsedLng = parseFloat(lng);\r\n                if (isNaN(parsedLat) || isNaN(parsedLng)) return;\r\n                globalMap.setView([parsedLat, parsedLng], 14);\r\n                globalMarkers.forEach(m => {\r\n                    const mLat = m.getLatLng().lat;\r\n                    const mLng = m.getLatLng().lng;\r\n                    if (Math.abs(mLat - parsedLat) < 0.0001 && Math.abs(mLng - parsedLng) < 0.0001) m.openPopup();\r\n                });\r\n            };\r\n\r\n            function escapeHtml(text) {\r\n                if (!text) return text;\r\n                const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#039;' };\r\n                return text.replace(\/[&<>\"']\/g, m => map[m]);\r\n            }\r\n\r\n        })('osm-stores-map-69e26ff3355ef', 'stores-search-btn-osm-stores-map-69e26ff3355ef', 'stores-search-input-osm-stores-map-69e26ff3355ef', 'stores-list-results-osm-stores-map-69e26ff3355ef', 'stores-country-select-osm-stores-map-69e26ff3355ef');\r\n        <\/script>\r\n        <\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Notre r\u00e9seau de distributeurs couvre le monde entier, ce qui garantit que nos produits sont facilement accessibles o\u00f9 que vous soyez. Chaque distributeur conna\u00eet parfaitement la collection PHi, ce qui lui permet de fournir des conseils d&rsquo;expert et un service de premier ordre. Trouvez le distributeur le plus proche de chez vous et d\u00e9couvrez une [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-4315","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/phi-cycling.cc\/fr\/wp-json\/wp\/v2\/pages\/4315","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/phi-cycling.cc\/fr\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/phi-cycling.cc\/fr\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/phi-cycling.cc\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/phi-cycling.cc\/fr\/wp-json\/wp\/v2\/comments?post=4315"}],"version-history":[{"count":2,"href":"https:\/\/phi-cycling.cc\/fr\/wp-json\/wp\/v2\/pages\/4315\/revisions"}],"predecessor-version":[{"id":4670,"href":"https:\/\/phi-cycling.cc\/fr\/wp-json\/wp\/v2\/pages\/4315\/revisions\/4670"}],"wp:attachment":[{"href":"https:\/\/phi-cycling.cc\/fr\/wp-json\/wp\/v2\/media?parent=4315"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}