{"id":4307,"date":"2025-12-20T11:07:30","date_gmt":"2025-12-20T03:07:30","guid":{"rendered":"https:\/\/phi-cycling.cc\/encontra-um-distribuidor\/"},"modified":"2026-04-08T09:42:26","modified_gmt":"2026-04-08T01:42:26","slug":"encontra-um-distribuidor","status":"publish","type":"page","link":"https:\/\/phi-cycling.cc\/pt-pt\/encontra-um-distribuidor\/","title":{"rendered":"Encontra um distribuidor"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"4307\" class=\"elementor elementor-4307 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>A nossa rede de distribuidores estende-se por todo o mundo, assegurando que os nossos produtos s\u00e3o facilmente acess\u00edveis onde quer que estejas. Cada distribuidor est\u00e1 bem familiarizado com a cole\u00e7\u00e3o PHi, pelo que pode fornecer aconselhamento especializado e um servi\u00e7o de primeira classe. <\/p><p>Encontra o distribuidor mais pr\u00f3ximo e descobre uma gama de produtos que transformar\u00e3o a tua experi\u00eancia de ciclismo.  <br>Pedala com confian\u00e7a, equipado com o melhor equipamento de ciclismo do planeta.<\/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-69e27016a5807\"><strong>Filter by Country:<\/strong><\/label>\r\n                    <select id=\"stores-country-select-osm-stores-map-69e27016a5807\">\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-69e27016a5807\" placeholder=\"Search by city or other keywords...\" \/>\r\n                    <button id=\"stores-search-btn-osm-stores-map-69e27016a5807\">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-69e27016a5807\" 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-69e27016a5807\" 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-69e27016a5807', 'stores-search-btn-osm-stores-map-69e27016a5807', 'stores-search-input-osm-stores-map-69e27016a5807', 'stores-list-results-osm-stores-map-69e27016a5807', 'stores-country-select-osm-stores-map-69e27016a5807');\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>A nossa rede de distribuidores estende-se por todo o mundo, assegurando que os nossos produtos s\u00e3o facilmente acess\u00edveis onde quer que estejas. Cada distribuidor est\u00e1 bem familiarizado com a cole\u00e7\u00e3o PHi, pelo que pode fornecer aconselhamento especializado e um servi\u00e7o de primeira classe. Encontra o distribuidor mais pr\u00f3ximo e descobre uma gama de produtos que [&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-4307","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/phi-cycling.cc\/pt-pt\/wp-json\/wp\/v2\/pages\/4307","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/phi-cycling.cc\/pt-pt\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/phi-cycling.cc\/pt-pt\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/phi-cycling.cc\/pt-pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/phi-cycling.cc\/pt-pt\/wp-json\/wp\/v2\/comments?post=4307"}],"version-history":[{"count":1,"href":"https:\/\/phi-cycling.cc\/pt-pt\/wp-json\/wp\/v2\/pages\/4307\/revisions"}],"predecessor-version":[{"id":4309,"href":"https:\/\/phi-cycling.cc\/pt-pt\/wp-json\/wp\/v2\/pages\/4307\/revisions\/4309"}],"wp:attachment":[{"href":"https:\/\/phi-cycling.cc\/pt-pt\/wp-json\/wp\/v2\/media?parent=4307"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}