update
This commit is contained in:
219
.paul/phases/08-map-and-cities-widgets/08-01-PLAN.md
Normal file
219
.paul/phases/08-map-and-cities-widgets/08-01-PLAN.md
Normal file
@@ -0,0 +1,219 @@
|
||||
---
|
||||
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
|
||||
---
|
||||
|
||||
<objective>
|
||||
## 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
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## 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
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
No specialized flows configured.
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Cache API + Widget mapki Polski z pinami</name>
|
||||
<files>
|
||||
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
|
||||
</files>
|
||||
<action>
|
||||
**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 `<div class="carei-map">` 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 `<circle>` (piny) na mapie SVG w pozycjach z mapowania
|
||||
- Tooltip: na hover/click nad pinem — `<div class="carei-map__tooltip">` 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
|
||||
</action>
|
||||
<verify>
|
||||
- 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)
|
||||
</verify>
|
||||
<done>AC-1 satisfied: Mapa z dynamicznymi pinami i tooltipami. AC-3 satisfied: Cache 60 min na branch/list.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Widget siatki miast</name>
|
||||
<files>
|
||||
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
|
||||
</files>
|
||||
<action>
|
||||
**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 `<div class="carei-cities">` z siatką miast
|
||||
- HTML: `<span class="carei-cities__item">Miasto</span>` 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
|
||||
</action>
|
||||
<verify>
|
||||
- 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
|
||||
</verify>
|
||||
<done>AC-2 satisfied: Siatka miast dynamiczna, responsywna, unikalne wartości.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>Dwa widgety Elementor: mapa Polski z pinami oddziałów + siatka miast. Cachowanie API 60 min.</what-built>
|
||||
<how-to-verify>
|
||||
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
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## 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
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
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)
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 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
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/08-map-and-cities-widgets/08-01-SUMMARY.md`
|
||||
</output>
|
||||
145
.paul/phases/08-map-and-cities-widgets/08-01-SUMMARY.md
Normal file
145
.paul/phases/08-map-and-cities-widgets/08-01-SUMMARY.md
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
phase: 08-map-and-cities-widgets
|
||||
plan: 01
|
||||
subsystem: ui
|
||||
tags: [elementor, svg, map, api-cache, transient]
|
||||
|
||||
requires:
|
||||
- phase: 01-reservation-form-plugin
|
||||
provides: Softra API client, REST proxy, plugin bootstrap
|
||||
|
||||
provides:
|
||||
- Carei Map widget (SVG map with dynamic pins + tooltips)
|
||||
- Carei Cities widget (city name grid)
|
||||
- Carei Branches widget (branch grid with addresses)
|
||||
- Branches API caching (60min transient)
|
||||
- REST endpoint /branches-full
|
||||
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Branch name cleaning: strip D/L suffixes, NAME_FIXES for truncated names"
|
||||
- "City coordinates: static PHP array mapping normalized names to SVG viewBox coords"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- 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-branches-widget.php
|
||||
modified:
|
||||
- wp-content/plugins/carei-reservation/includes/class-softra-api.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.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
|
||||
|
||||
key-decisions:
|
||||
- "Branch name D=Dworzec, L=Lotnisko — strip suffixes, deduplicate to base city"
|
||||
- "Tooltip format: Oddział [City] + ul. Street + ZipCode City"
|
||||
- "Branches widget added beyond original plan scope"
|
||||
- "Cities separator via CSS ::after to prevent line-start orphan |"
|
||||
|
||||
patterns-established:
|
||||
- "NAME_FIXES const for API name corrections (shared across widgets)"
|
||||
- "get_branches_cached() as single source for all branch data"
|
||||
|
||||
duration: ~45min
|
||||
started: 2026-04-01T17:30:00Z
|
||||
completed: 2026-04-01T18:15:00Z
|
||||
---
|
||||
|
||||
# Phase 8 Plan 01: Map, Cities & Branches Widgets Summary
|
||||
|
||||
**Three Elementor widgets for branch visualization: SVG map with dynamic pins/tooltips, city name list, and full branch grid with addresses. Plus 60-min API cache.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~45min |
|
||||
| Tasks | 3 completed (2 auto + 1 checkpoint) |
|
||||
| Files modified | 7 (3 created, 4 modified) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Mapa Polski z dynamicznymi pinami | Pass | Real SVG (Vector.svg), pins from API, tooltip with address |
|
||||
| AC-2: Siatka miast oddziałowych | Pass | Unique cities, ::after separators, responsive |
|
||||
| AC-3: Cachowanie API 60 min | Pass | `get_branches_cached()` with HOUR_IN_SECONDS transient |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- SVG map of Poland (user-provided Vector.svg) with dynamic red pins positioned by city coordinate mapping, tooltips showing "Oddział [City] / ul. Street / ZipCode City"
|
||||
- Cities widget with deduplicated, title-cased names from API, CSS ::after separators that never orphan to line start
|
||||
- Branches grid widget (5→3→2→1 columns responsive) with full address data from API fields (street, zipCode, city)
|
||||
- Unified branch name cleaning: D/L suffix stripping, NAME_FIXES for truncated API names (BYDGOSZC→BYDGOSZCZ, GORZÓW WIE→GORZÓW WIELKOPOLSKI, RZSZÓW→RZESZÓW, SK-KAM→skip)
|
||||
- 60-min transient cache on /branch/list, used by all three widgets via single `get_branches_cached()` method
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `includes/class-map-widget.php` | Created | SVG map widget with pins, tooltips, city coords |
|
||||
| `includes/class-cities-widget.php` | Created | City name grid widget |
|
||||
| `includes/class-branches-widget.php` | Created | Branch grid with addresses |
|
||||
| `includes/class-softra-api.php` | Modified | Added `get_branches_cached()` with 60min transient |
|
||||
| `includes/class-rest-proxy.php` | Modified | Added `/branches-full` endpoint |
|
||||
| `carei-reservation.php` | Modified | Registered 3 new widgets |
|
||||
| `assets/css/carei-reservation.css` | Modified | Styles for map, cities, branches widgets |
|
||||
| `assets/js/carei-reservation.js` | Modified | Dynamic pin rendering + tooltip logic |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Static city→SVG coordinate mapping in PHP | No runtime geocoding, simple and fast | New cities need manual coord addition |
|
||||
| D suffix = Dworzec, display as "Oddział [City]" | User request for consistent naming | All tooltips show "Oddział X" format |
|
||||
| CSS ::after for city separators | Prevents `\|` orphaning to line start on wrap | Cleaner than separate `<span>` elements |
|
||||
| Added Branches widget (not in original plan) | User requested during execution | Extra widget, no negative impact |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Scope additions | 1 | Branches grid widget added |
|
||||
| Auto-fixed | 3 | API name cleaning, separator orphans, tooltip content |
|
||||
|
||||
**Total impact:** Scope addition was user-requested. Auto-fixes were essential for data quality.
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. API branch names with D/L suffixes causing 39 duplicates**
|
||||
- Found during: checkpoint verification
|
||||
- Issue: API returns "GDAŃSK", "GDAŃSK D", "GDAŃSK L" as separate branches
|
||||
- Fix: Strip suffixes, NAME_FIXES for truncated names, deduplicate
|
||||
- Verification: 17 unique cities displayed
|
||||
|
||||
**2. Separator `|` orphaning to new line start**
|
||||
- Found during: user review
|
||||
- Fix: Replaced `<span>` separator with CSS `::after` pseudo-element
|
||||
|
||||
**3. Tooltip showing raw API description instead of formatted address**
|
||||
- Found during: user review
|
||||
- Fix: Built address from `street`, `zipCode`, `city` API fields
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- All branch visualization widgets operational
|
||||
- API cache in place for all branch data consumers
|
||||
- Branch name cleaning pattern reusable
|
||||
|
||||
**Concerns:**
|
||||
- City coordinates are static — new Softra branches in unmapped cities won't show pins
|
||||
- Some API zipCodes are null (e.g., Białystok)
|
||||
|
||||
**Blockers:** None
|
||||
|
||||
---
|
||||
*Phase: 08-map-and-cities-widgets, Plan: 01*
|
||||
*Completed: 2026-04-01*
|
||||
Reference in New Issue
Block a user