Widget Mapy Sklepów Medycznych

Kompletna dokumentacja integracji interaktywnej mapy sklepów medycznych NFZ w systemach CRM i stronach internetowych

Przegląd Widget'a Mapy

Widget mapy to gotowe rozwiązanie JavaScript umożliwiające osadzenie interaktywnej mapy sklepów medycznych NFZ na dowolnej stronie internetowej. Widget automatycznie pobiera dane z API eZWM.eCommerce i wyświetla je w przyjaznej dla użytkownika formie.

Główne zalety:

  • Plug & Play: Łatwa integracja w 4 prostych krokach
  • Responsywność: Automatyczne dostosowanie do różnych ekranów
  • Customizacja: Różne motywy kolorystyczne i opcje konfiguracji
  • Performance: Optymalizowane ładowanie danych i cache'owanie
  • Accessibility: Zgodność z WCAG 2.1 oraz obsługa czytników ekranu

Funkcjonalności Widget'a

Interaktywna Mapa

Pełna interakcja - zoom, drag, klikanie markerów. Bazuje na bibliotece Leaflet.js z optymalizacjami dla urządzeń mobilnych.

Popup'y Informacyjne

Szczegółowe informacje o sklepach w eleganckich popup'ach - nazwa, adres, telefon, godziny otwarcia, zakresy NFZ.

Mobile-First Design

Responsywny design z touch-friendly kontrolkami. Automatyczne wykrywanie orientacji i rozmiaru ekranu.

Zaawansowane Filtrowanie

Filtrowanie według zakresu świadczeń NFZ (AS, PO, SP, SO), odległości oraz dostępności konkretnych wyrobów.

Nawigacja GPS

Bezpośrednie przekierowanie do aplikacji nawigacyjnych (Google Maps, Apple Maps) z obliczoną trasą.

Motywy Kolorystyczne

4 gotowe motywy (Default, Medical, Light, Dark) oraz możliwość pełnej customizacji CSS.

Demonstracja na Żywo

Przetestuj widget w akcji. System automatycznie spróbuje pobrać Twoją lokalizację lub użyje przykładowego miasta polskiego.

Uwaga: Demo może wymagać zgody na lokalizację w przeglądarce. W przypadku odmowy zostanie użyte losowe miasto.

Przewodnik Integracji

Załaduj Wymagane Biblioteki

Widget wymaga biblioteki Leaflet.js do obsługi map. Dodaj poniższe znaczniki do sekcji <head> i przed zamknięciem </body>:

<!-- W sekcji <head> -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" 
      integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" 
      crossorigin="" />

<!-- Przed zamknięciem </body> -->
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" 
        integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" 
        crossorigin=""></script>
Używamy najnowszej stabilnej wersji Leaflet.js z oficjalnego CDN oraz integralność plików jest weryfikowana przez hash SHA-256.

Załaduj eZWM Widget Loader

Dodaj nasz specjalny skrypt widget'a, który obsługuje automatyczne wykrywanie środowiska:

<script src="https://ecommerce.ezwm.pl/widgets/ezwm-map-loader.js"></script>
Skrypt automatycznie wykrywa środowisko (dev/prod) i dostosowuje endpoint API. Obsługuje również fallback dla starszych przeglądarek.

Przygotuj Kontener HTML

Utwórz element HTML, w którym zostanie wyrenderowana mapa:

<div id="medical-shops-map" style="width: 100%; height: 400px; border-radius: 8px;">
    <!-- Loader będzie tutaj wyświetlony podczas ładowania -->
    <div class="map-loading" style="display: flex; align-items: center; justify-content: center; height: 100%; background: #f8f9fa;">
        <i class="fas fa-spinner fa-spin fa-2x text-primary"></i>
        <span class="ms-2">Ładowanie mapy...</span>
    </div>
</div>
Ważne: Element musi mieć określoną wysokość (height) w CSS. Minimalna zalecana wysokość to 300px dla urządzeń mobilnych.

Inicjalizuj Widget

Wywołaj metodę inicjalizacji z odpowiednimi parametrami konfiguracyjnymi:

<script>
// Podstawowa inicjalizacja dla określonej lokalizacji
EZWMMapLoader.loadMap('medical-shops-map', {
    latitude: 50.0647,   // Kraków - współrzędne GPS
    longitude: 19.9449,
    theme: 'default',
    promieniWyszukiwania: 25,  // km
    limitSklepow: 15,
    zakresSwiadczenia: null    // wszystkie zakresy NFZ
});

// Zaawansowana inicjalizacja z geolokalizacją użytkownika
document.addEventListener('DOMContentLoaded', function() {
    if (navigator.geolocation && window.location.protocol === 'https:') {
        // Geolokalizacja działa tylko przez HTTPS
        navigator.geolocation.getCurrentPosition(
            function(position) {
                // Sukces - użyj lokalizacji użytkownika
                EZWMMapLoader.loadMap('medical-shops-map', {
                    latitude: position.coords.latitude,
                    longitude: position.coords.longitude,
                    theme: 'medical',
                    promieniWyszukiwania: 20,
                    limitSklepow: 10,
                    zakresSwiadczenia: 'PO'  // tylko produkty ortopedyczne
                });
            },
            function(error) {
                // Błąd geolokalizacji - użyj domyślnej lokalizacji
                console.warn('Geolocation error:', error);
                EZWMMapLoader.loadMap('medical-shops-map', {
                    latitude: 52.2297,   // Warszawa jako fallback
                    longitude: 21.0122,
                    theme: 'light',
                    promieniWyszukiwania: 30,
                    limitSklepow: 12
                });
            },
            {
                timeout: 10000,           // max 10 sekund na geolokalizację
                enableHighAccuracy: true, // wysoka precyzja
                maximumAge: 300000        // cache przez 5 minut
            }
        );
    } else {
        // Fallback dla przeglądarek bez geolokalizacji lub HTTP
        EZWMMapLoader.loadMap('medical-shops-map', {
            latitude: 50.0647,
            longitude: 19.9449,
            theme: 'default'
        });
    }
});
</script>

Opcje Konfiguracji

Parametr Typ Domyślnie Opis Przykład
latitude number wymagane Szerokość geograficzna centrum mapy (-90 do 90) 50.0647
longitude number wymagane Długość geograficzna centrum mapy (-180 do 180) 19.9449
theme string 'default' Motyw kolorystyczny: 'default', 'medical', 'light', 'dark' 'medical'
promieniWyszukiwania number 20 Promień wyszukiwania w kilometrach (1-100) 25
limitSklepow number 10 Maksymalna liczba sklepów do wyświetlenia (1-50) 15
zakresSwiadczenia string|null null Filtr zakresu NFZ: 'AS', 'PO', 'SP', 'SO' lub null (wszystkie) 'PO'
zoomLevel number 12 Początkowy poziom powiększenia mapy (1-18) 14
showUserLocation boolean true Czy pokazać marker lokalizacji użytkownika false
enableClustering boolean true Włącz grupowanie markerów dla lepszej wydajności false

Przykłady Zaawansowanej Integracji

Przykład 1: Widget z filtrowaniem w czasie rzeczywistym

<select id="nfz-filter" class="form-select">
    <option value="">Wszystkie zakresy NFZ</option>
    <option value="AS">Aparaty słuchowe</option>
    <option value="PO">Produkty ortopedyczne</option>
    <option value="SP">Środki pomocnicze</option>
    <option value="SO">Szkła okularowe</option>
</select>
<div id="filtered-map" style="height: 500px;"></div>

<script>
let mapInstance = null;
const baseConfig = {
    latitude: 50.0647,
    longitude: 19.9449,
    theme: 'medical',
    promieniWyszukiwania: 30
};

// Inicjalizacja
EZWMMapLoader.loadMap('filtered-map', baseConfig).then(instance => {
    mapInstance = instance;
});

// Obsługa zmiany filtra
document.getElementById('nfz-filter').addEventListener('change', function(e) {
    const newConfig = {
        ...baseConfig,
        zakresSwiadczenia: e.target.value || null
    };
    
    // Przeładuj mapę z nowym filtrem
    EZWMMapLoader.loadMap('filtered-map', newConfig);
});
</script>

Przykład 2: Integracja z formularzem wyszukiwania

<form id="search-form">
    <input type="text" id="address-input" placeholder="Wpisz adres..." class="form-control">
    <button type="submit">Znajdź sklepy</button>
</form>
<div id="search-results-map" style="height: 400px;"></div>

<script>
document.getElementById('search-form').addEventListener('submit', async function(e) {
    e.preventDefault();
    
    const address = document.getElementById('address-input').value;
    
    try {
        // Geokodowanie adresu (można użyć zewnętrznego serwisu)
        const coords = await geocodeAddress(address);
        
        // Załaduj mapę dla nowej lokalizacji
        await EZWMMapLoader.loadMap('search-results-map', {
            latitude: coords.lat,
            longitude: coords.lng,
            theme: 'light',
            promieniWyszukiwania: 15,
            limitSklepow: 20,
            zoomLevel: 13
        });
        
    } catch (error) {
        console.error('Błąd geokodowania:', error);
        alert('Nie można znaleźć podanego adresu');
    }
});

async function geocodeAddress(address) {
    // Przykład z użyciem Nominatim OpenStreetMap (bezpłatne)
    const response = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(address)}&countrycodes=pl&limit=1`);
    const data = await response.json();
    
    if (data.length === 0) {
        throw new Error('Address not found');
    }
    
    return {
        lat: parseFloat(data[0].lat),
        lng: parseFloat(data[0].lon)
    };
}
</script>

Rozwiązywanie Problemów

Możliwe przyczyny i rozwiązania:
  • Brak biblioteki Leaflet: Upewnij się, że załadowałeś CSS i JS Leaflet.js przed skryptem widget'a
  • Błędne współrzędne: Sprawdź czy latitude (-90 do 90) i longitude (-180 do 180) są poprawne
  • Problemy z CORS: Widget musi być załadowany z domeny HTTPS lub localhost
  • Brak wysokości kontenera: Element musi mieć określoną wysokość w CSS

Optymalizacja dla mobile:
  • Viewport: Dodaj <meta name="viewport" content="width=device-width, initial-scale=1.0">
  • Touch handling: Widget automatycznie obsługuje gesty touch
  • Minimalna wysokość: Ustaw wysokość minimum 300px dla lepszego UX
  • Geolokalizacja: Działa tylko przez HTTPS na mobilnych

Optymalizacja wydajności:
  • Limit sklepów: Zmniejsz limitSklepow do maksymalnie 20 dla lepszej wydajności
  • Clustering: Włącz enableClustering: true dla dużej liczby markerów
  • Promień wyszukiwania: Ogranicz promieniWyszukiwania do rozsądnych wartości (20-50km)
  • Lazy loading: Ładuj widget dopiero gdy jest widoczny na ekranie