--- phase: 08-map-and-cities-widgets plan: 01 type: execute wave: 1 depends_on: [] files_modified: - wp-content/plugins/carei-reservation/includes/class-map-widget.php - wp-content/plugins/carei-reservation/includes/class-cities-widget.php - wp-content/plugins/carei-reservation/includes/class-softra-api.php - wp-content/plugins/carei-reservation/carei-reservation.php - wp-content/plugins/carei-reservation/assets/css/carei-reservation.css - wp-content/plugins/carei-reservation/assets/js/carei-reservation.js autonomous: false --- ## Goal Dwa nowe widgety Elementor: (1) interaktywna mapa Polski z dynamicznymi pinami oddziałów i tooltipami adresowymi, (2) siatka miast oddziałowych. Plus ujednolicone cachowanie odpowiedzi API Softra (60 min). ## Purpose Wizualna prezentacja sieci oddziałów Carei na stronie — mapa z pinami i lista miast budują zaufanie klienta i ułatwiają wybór lokalizacji. ## Output - `class-map-widget.php` — widget Elementor z mapą SVG Polski + dynamiczne piny - `class-cities-widget.php` — widget Elementor z siatką miast - Rozszerzony cache w `class-softra-api.php` — transient 60 min na `/branch/list` - Zarejestrowane widgety w `carei-reservation.php` - Style i JS w istniejących plikach assets ## Project Context @.paul/PROJECT.md @.paul/ROADMAP.md @.paul/STATE.md ## Source Files @wp-content/plugins/carei-reservation/carei-reservation.php @wp-content/plugins/carei-reservation/includes/class-softra-api.php @wp-content/plugins/carei-reservation/includes/class-search-widget.php (wzorzec widgetu) @wp-content/plugins/carei-reservation/assets/css/carei-reservation.css @wp-content/plugins/carei-reservation/assets/js/carei-reservation.js No specialized flows configured. ## AC-1: Mapa Polski z dynamicznymi pinami oddziałów ```gherkin Given strona z widgetem Carei Map załadowana When dane oddziałów pobrane z API (lub cache) Then na mapie SVG Polski wyświetlają się czerwone piny w lokalizacjach odpowiadających miastom oddziałów And po najechaniu na pin pojawia się tooltip z adresem oddziału (ulica, kod, miasto) And po kliknięciu pina tooltip pozostaje widoczny (toggle) ``` ## AC-2: Siatka miast oddziałowych ```gherkin Given strona z widgetem Carei Cities załadowana When dane oddziałów pobrane z API (lub cache) Then wyświetla się siatka z nazwami miast oddziałów rozdzielonymi separatorem "|" And miasta są unikalne (bez duplikatów nawet jeśli oddział ma wiele branchy w jednym mieście) And układ jest responsywny (grid na desktop, kolumna na mobile) ``` ## AC-3: Cachowanie odpowiedzi API z TTL 60 min ```gherkin Given plugin Carei Reservation aktywny When frontend lub widget pobiera dane oddziałów z /branch/list Then wynik jest cachowany jako WP transient z TTL 60 minut And kolejne żądania w ciągu 60 min nie generują zapytań do Softra API And po upływie TTL dane są odświeżane z API ``` Task 1: Cache API + Widget mapki Polski z pinami wp-content/plugins/carei-reservation/includes/class-softra-api.php, wp-content/plugins/carei-reservation/includes/class-map-widget.php, wp-content/plugins/carei-reservation/carei-reservation.php, wp-content/plugins/carei-reservation/assets/css/carei-reservation.css, wp-content/plugins/carei-reservation/assets/js/carei-reservation.js **1a. Cachowanie `/branch/list` (class-softra-api.php):** - Dodaj metodę `get_branches_cached()` opakowującą `get_branches()` w transient `carei_branches_list` z TTL 60 min (HOUR_IN_SECONDS). - Zmień `get_segments_branches_map()` by korzystała z `get_branches_cached()` zamiast `get_branches()`. - Dodaj endpoint REST `/branches-full` w REST proxy zwracający pełne dane branchy (z description/address) — istniejący `/branches` zwraca tylko nazwy. **1b. Widget mapy (class-map-widget.php):** - Nowa klasa `Carei_Map_Widget extends \Elementor\Widget_Base` - `get_name()` → `'carei-map'`, `get_title()` → `'Carei Map'` - W `render()`: - Pobierz oddziały z REST API `/carei/v1/branches-full` przez JS (lub inline z PHP jeśli cache aktywny) - Wyrenderuj kontener `
` z inline SVG mapy Polski (kontur województw) - SVG mapy: użytkownik dostarczy kod SVG — na razie placeholder z prostym konturem Polski - Mapowanie miast na współrzędne SVG: - W PHP: tablica asocjacyjna `CITY_COORDINATES` mapująca nazwy miast (lowercase, bez polskich znaków) na współrzędne [x, y] w viewBox SVG - Miasta znane w Carei: Warszawa, Białystok, Wrocław, Kraków, Gdańsk, Poznań, Katowice, Łódź, Lublin, Szczecin, Rzeszów, Bydgoszcz, Olsztyn, Kielce, Opole, Zielona Góra, Radom itp. - Przekaż współrzędne do JS przez `wp_localize_script` lub data-attribute - W JS: dynamicznie dodaj `` (piny) na mapie SVG w pozycjach z mapowania - Tooltip: na hover/click nad pinem — `
` z adresem z pola `description` brancha - Styl pina: czerwone kółko (#FF0000), r=6, hover r=8 z transition - Styl tooltipa: ciemne tło (#2F2482), biały tekst, strzałka (jak na Figmie) **1c. Rejestracja widgetu:** - W `carei-reservation.php` w hooku `elementor/widgets/register`: require + register `Carei_Map_Widget` **Avoid:** - NIE używaj zewnętrznych bibliotek mapowych (Leaflet, Google Maps) — to prosta mapa SVG - NIE hardkoduj danych oddziałów — zawsze z API/cache - Widget "Carei Map" widoczny w panelu Elementor - Mapa SVG renderuje się z pinami w lokalizacjach oddziałów - Hover na pinie pokazuje tooltip z adresem - Drugie załadowanie strony nie generuje nowego requestu do Softra (cache aktywny) AC-1 satisfied: Mapa z dynamicznymi pinami i tooltipami. AC-3 satisfied: Cache 60 min na branch/list. Task 2: Widget siatki miast wp-content/plugins/carei-reservation/includes/class-cities-widget.php, wp-content/plugins/carei-reservation/carei-reservation.php, wp-content/plugins/carei-reservation/assets/css/carei-reservation.css, wp-content/plugins/carei-reservation/assets/js/carei-reservation.js **2a. Widget miast (class-cities-widget.php):** - Nowa klasa `Carei_Cities_Widget extends \Elementor\Widget_Base` - `get_name()` → `'carei-cities'`, `get_title()` → `'Carei Cities'` - W `render()`: - Pobierz oddziały z cache/API (ten sam endpoint co mapa) - Wyciągnij unikalne nazwy miast z pola description branchy (parse miasto z adresu) - Renderuj `
` z siatką miast - HTML: `Miasto` rozdzielone separatorem `|` - Layout: CSS grid/flex, 5 kolumn na desktop (jak na Figmie), 2-3 kolumny na tablet, 1 na mobile - Font: Albert Sans, kolor #2F2482 - Miasta dynamiczne — jeśli oddział dodany w Softra, automatycznie pojawia się w widgecie **2b. Rejestracja widgetu:** - W `carei-reservation.php`: require + register `Carei_Cities_Widget` **Avoid:** - NIE hardkoduj listy miast - NIE duplikuj requestów — użyj tego samego cache co mapa - Widget "Carei Cities" widoczny w panelu Elementor - Siatka miast renderuje się z unikalnymi miastami z API - Responsywność: 5 kolumn desktop → mniej na mobile - Brak duplikatów miast AC-2 satisfied: Siatka miast dynamiczna, responsywna, unikalne wartości. Dwa widgety Elementor: mapa Polski z pinami oddziałów + siatka miast. Cachowanie API 60 min. 1. Otwórz stronę w Elementorze, dodaj widget "Carei Map" — sprawdź czy piny są na mapie 2. Najedź na pin — tooltip z adresem powinien się pojawić 3. Dodaj widget "Carei Cities" — sprawdź siatkę miast 4. Sprawdź responsywność (mobile preview w Elementorze) 5. Opcjonalnie: podaj kod SVG mapy Polski do zastąpienia placeholdera Type "approved" to continue, or describe issues to fix ## DO NOT CHANGE - wp-content/plugins/carei-reservation/includes/class-elementor-widget.php (główny widget rezerwacji) - wp-content/plugins/carei-reservation/includes/class-admin-panel.php (admin panel) - Istniejące endpointy REST (nie zmieniaj zachowania `/branches`, `/car-classes` itd.) - Logika formularza rezerwacji w JS (sekcje modal, booking flow) ## SCOPE LIMITS - Mapa to SVG statyczny z dynamicznymi pinami — NIE interaktywna mapa z zoomem - Brak geokodowania w runtime — współrzędne miast zmapowane statycznie w PHP - Tooltip prosty (adres) — bez linków, bez routingu do formularza - Cachowanie tylko na `/branch/list` — inne endpointy bez zmian w tym planie Before declaring plan complete: - [ ] Widget "Carei Map" renderuje mapę z pinami na prawidłowych pozycjach - [ ] Tooltip na hover/click pokazuje adres oddziału - [ ] Widget "Carei Cities" wyświetla unikalne miasta w siatce - [ ] Cache transient `carei_branches_list` aktywny z TTL 60 min - [ ] Responsywność obu widgetów (desktop + mobile) - [ ] Brak błędów JS w konsoli - [ ] Brak broken layouts w istniejących widgetach (rezerwacja, search form) - Oba widgety dostępne w Elementorze i renderują dane z API - Piny na mapie odpowiadają miastom oddziałów z Softra - Tooltips wyświetlają adresy - Siatka miast dynamiczna i responsywna - Cache 60 min działa — brak nadmiarowych requestów do API - Istniejąca funkcjonalność nienaruszona After completion, create `.paul/phases/08-map-and-cities-widgets/08-01-SUMMARY.md`