update
This commit is contained in:
@@ -60,6 +60,47 @@ Fix: modal rezerwacji nie otwierał się na mobile/tablet — sekcja Elementor m
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Milestone v0.6: Cleanup — Pakiety ochronne (jedno źródło prawdy)
|
||||||
|
|
||||||
|
**Goal:** W sekcji „Pakiety ochronne" modala zostają wyłącznie pakiety z panelu WP (SOFT/PREMIUM). Pozycje insurance z Softra API („Zniesienie udziału") są usuwane z widoku i nie trafiają do podsumowania ani payloadu.
|
||||||
|
|
||||||
|
**Status:** Complete ✅
|
||||||
|
|
||||||
|
### Phase 15: Remove Softra-insurance z modala ✅ Complete
|
||||||
|
Usunięty kontener `#carei-insurance-container` + divider w widgecie PHP. W JS usunięta zmienna `insuranceContainer` i render insuranceItems. Pozycje pricelist zawierające `ubezp|ochrony|zniesienie|insurance` są dropowane — nie trafiają do extras, podsumowania ani do `makebooking` payloadu. Pakiety WP (SOFT/PREMIUM) z Phase 13 są jedynym źródłem prawdy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Milestone v0.7: Dwujęzyczność (PL/EN) widgetów Carei
|
||||||
|
|
||||||
|
**Goal:** Plugin `carei-reservation` (modal, hero search, admin, mapa, grid miast, grid oddziałów) działa dwujęzycznie PL/EN zgodnie z bieżącym językiem Polylang. Obejmuje: (1) i18n refactor pluginu, (2) dwujęzyczne pakiety ochronne + mapowanie błędów Softra, (3) tłumaczenie EN + QA.
|
||||||
|
|
||||||
|
**Status:** Complete ✅
|
||||||
|
|
||||||
|
### Phase 16: i18n refactor pluginu ✅ Complete
|
||||||
|
Plan 16-01 ukończony. 8 plików PHP z user-facing stringami w `__()`/`esc_html__()`/`esc_attr__()` (~105 stringów), textdomain `carei-reservation` ładowany na `plugins_loaded`. JS (1573 linie): ~70 literałów zmigrowanych do `window.careiI18n` przez `wp_localize_script`, helper `pluralPl()` dla polskich form. Wygenerowany `.pot` z 157 unikalnymi msgid. PL UI bez regresji (verified).
|
||||||
|
|
||||||
|
### Phase 17: Dwujęzyczne pakiety ochronne + mapowanie błędów Softra ✅ Complete
|
||||||
|
Plan 17-01 ukończony. Panel pakietów z polami `name_en`/`description_en` + sanitize/save. REST `/protection-packages` z `resolve_locale()` (`?lang=` → `determine_locale()` fallback) zwraca wariant per locale z fallbackiem PL; klucze `_en` nie wyciekają w payloadzie. `Carei_Softra_API::extract_softra_message()` + `map_error_message()` ze słownikiem 13 komunikatów (exact + fuzzy prefix + graceful passthrough). Frontend `loadProtectionPackages()` dodaje `?lang=` na podstawie `document.documentElement.lang`.
|
||||||
|
|
||||||
|
### Phase 18: Tłumaczenie EN (.po/.mo) + Flatpickr ✅ Complete
|
||||||
|
Plan 18-01 ukończony. `carei-reservation-en_US.po` + `.mo` (158 wpisów) + kopia en_GB; własny PHP kompilator `po2mo.php` (msgfmt niedostępny). **Scope addition:** Flatpickr 4.6.13 (CDN jsdelivr) jako cross-browser date picker z locale PL/EN dla modal (static:true — popup w container) + hero (default). `disableMobile:true` dla spójności UX. Kompaktowy CSS theme w kolorach Carei (#2F2482). Graceful fallback do native picker.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Milestone v0.8: Admin-managed tłumaczenia extras
|
||||||
|
|
||||||
|
**Goal:** Panel administratora do zarządzania tłumaczeniami dynamicznych nazw dodatkowych opcji (extras) zwracanych z Softra API. Plugin auto-zbiera napotkane PL nazwy, admin wypełnia tłumaczenia EN w UI, frontend EN automatycznie używa.
|
||||||
|
|
||||||
|
**Status:** Complete ✅
|
||||||
|
|
||||||
|
### Phase 19: Extras translations admin panel ✅ Complete
|
||||||
|
Plan 19-01 ukończony. WP options `carei_extras_seen` (auto-collected) + `carei_extras_translations` (admin override). Static helpery w `Carei_Admin_Panel`: `remember_extra_name()`, `get_extras_seen()`, `get_extras_translations()`, `translate_extra_name()`. Submenu `Rezerwacje → Tłumaczenia extras` (nonce + sanitize + redirect). `Carei_REST_Proxy::get_pricelist()` z runtime auto-collect + per-locale name replacement z fallbackiem do PL. Zero zmian w JS.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Backlog
|
### Backlog
|
||||||
- Eksport CSV/PDF rezerwacji
|
- Eksport CSV/PDF rezerwacji
|
||||||
- Email notyfikacje
|
- Email notyfikacje
|
||||||
|
|||||||
@@ -2,32 +2,35 @@
|
|||||||
|
|
||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Milestone: v0.5 Pakiety Ochronne + Poprawki — Complete ✅
|
Milestone: v0.8 Admin-managed tłumaczenia extras — Complete ✅
|
||||||
Phase: 13 of 14 (Pakiety ochronne) — Complete ✅
|
Phase: 19 (Extras translations admin panel) — Complete ✅
|
||||||
Plan: 13-02 complete
|
Plan: 19-01 complete (UNIFIED)
|
||||||
Status: Milestone v0.5 closed
|
Status: Milestone v0.8 closed
|
||||||
Last activity: 2026-04-20 — Phase 13 unified, SUMMARY written, milestone closed
|
Last activity: 2026-04-22 — Phase 19 SUMMARY utworzony
|
||||||
|
|
||||||
Progress:
|
Progress:
|
||||||
- Milestone v0.1: [██████████] 100% ✅
|
|
||||||
- Milestone v0.2: [██████████] 100% ✅
|
|
||||||
- Milestone v0.3: [██████████] 100% ✅
|
|
||||||
- Milestone v0.4: [██████████] 100% ✅
|
|
||||||
- Milestone v0.5: [██████████] 100% ✅
|
- Milestone v0.5: [██████████] 100% ✅
|
||||||
- Phase 13: [██████████] 100% ✅
|
- Milestone v0.6: [██████████] 100% ✅
|
||||||
- Phase 14: [██████████] 100% ✅
|
- Phase 15: [██████████] 100% ✅
|
||||||
|
- Milestone v0.7: [██████████] 100% ✅
|
||||||
|
- Phase 16: [██████████] 100% ✅
|
||||||
|
- Phase 17: [██████████] 100% ✅
|
||||||
|
- Phase 18: [██████████] 100% ✅
|
||||||
|
- Milestone v0.8: [██████████] 100% ✅
|
||||||
|
- Phase 19: [██████████] 100% ✅
|
||||||
|
|
||||||
## Loop Position
|
## Loop Position
|
||||||
|
|
||||||
Current loop state:
|
Current loop state:
|
||||||
```
|
```
|
||||||
PLAN ──▶ APPLY ──▶ UNIFY
|
PLAN ──▶ APPLY ──▶ UNIFY
|
||||||
✓ ✓ ✓ [Phase 13 loop closed, milestone v0.5 complete]
|
✓ ✓ ✓ [Milestone v0.8 complete]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-04-20
|
Last session: 2026-04-22
|
||||||
Stopped at: Milestone v0.5 complete (Phase 13 + Phase 14)
|
Stopped at: Milestone v0.8 complete (Phase 19 zamknięta)
|
||||||
Next action: Rozmowa o następnym milestone (v0.6) lub zamknięcie projektu — /paul:discuss-milestone lub /paul:complete-milestone
|
Next action: Rozmowa o następnym milestone lub zamknięcie projektu — /paul:discuss-milestone lub /paul:complete-milestone
|
||||||
Resume file: .paul/phases/13-protection-packages/13-02-SUMMARY.md
|
Resume file: .paul/phases/19-extras-translations-admin/19-01-SUMMARY.md
|
||||||
|
Resume file: .paul/phases/15-remove-softra-insurance/15-01-PLAN.md
|
||||||
|
|||||||
90
.paul/changelog/2026-04-22.md
Normal file
90
.paul/changelog/2026-04-22.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# 2026-04-22
|
||||||
|
|
||||||
|
## Co zrobiono
|
||||||
|
|
||||||
|
- [Phase 15, Plan 01] Usunięcie Softra-insurance z modala rezerwacji — pozostają wyłącznie pakiety WP (SOFT/PREMIUM) z Phase 13
|
||||||
|
- Usunięty kontener `#carei-insurance-container` + wewnętrzny divider w szablonie widgetu Elementor
|
||||||
|
- Usunięta zmienna `insuranceContainer` (deklaracja + init) w `carei-reservation.js`
|
||||||
|
- Pozycje pricelist zawierające `ubezp|ochrony|zniesienie|insurance` są dropowane — nie trafiają do extras, podsumowania, ani do payloadu `makebooking`
|
||||||
|
|
||||||
|
- [Phase 16, Plan 01] i18n refactor pluginu carei-reservation — przygotowanie do dwujęzyczności PL/EN
|
||||||
|
- `load_plugin_textdomain('carei-reservation')` w bootstrapie + katalog `languages/`
|
||||||
|
- 8 plików PHP z user-facing stringami owiniętymi w `__()`/`esc_html__()`/`esc_attr__()`
|
||||||
|
- `carei-reservation.js`: ~70 literałów zamienione na wywołania `t()`/`tFmt()` przez globalny obiekt `window.careiI18n`, helper `pluralPl()` dla polskich form
|
||||||
|
- `wp_localize_script('carei-reservation-js', 'careiI18n', [...])` z 78 kluczami tłumaczonymi po PHP
|
||||||
|
- Wygenerowany `languages/carei-reservation.pot` — 157 unikalnych msgid gotowych do tłumaczenia (Phase 18)
|
||||||
|
- Admin panel: statyczna tablica statusów → helper `get_status_label()` (wymóg WP — translation timing)
|
||||||
|
- Mu-plugin `fix-sprintf-global.php` — backfill globalnego `window.sprintf` dla Automatic Translate Addon For Polylang
|
||||||
|
|
||||||
|
## Zmienione pliki
|
||||||
|
|
||||||
|
- `wp-content/plugins/carei-reservation/carei-reservation.php`
|
||||||
|
- `wp-content/plugins/carei-reservation/includes/class-elementor-widget.php`
|
||||||
|
- `wp-content/plugins/carei-reservation/includes/class-admin-panel.php`
|
||||||
|
- `wp-content/plugins/carei-reservation/includes/class-rest-proxy.php`
|
||||||
|
- `wp-content/plugins/carei-reservation/includes/class-search-widget.php`
|
||||||
|
- `wp-content/plugins/carei-reservation/includes/class-cities-widget.php`
|
||||||
|
- `wp-content/plugins/carei-reservation/includes/class-map-widget.php`
|
||||||
|
- `wp-content/plugins/carei-reservation/includes/class-branches-widget.php`
|
||||||
|
- `wp-content/plugins/carei-reservation/assets/js/carei-reservation.js`
|
||||||
|
- `wp-content/plugins/carei-reservation/languages/carei-reservation.pot` (nowy)
|
||||||
|
- `wp-content/mu-plugins/fix-sprintf-global.php` (nowy)
|
||||||
|
- `.paul/phases/15-remove-softra-insurance/15-01-PLAN.md`
|
||||||
|
- `.paul/phases/15-remove-softra-insurance/15-01-SUMMARY.md`
|
||||||
|
- `.paul/phases/16-i18n-plugin-refactor/16-01-PLAN.md`
|
||||||
|
- `.paul/phases/16-i18n-plugin-refactor/16-01-SUMMARY.md`
|
||||||
|
- `.paul/STATE.md`
|
||||||
|
- `.paul/ROADMAP.md`
|
||||||
|
|
||||||
|
## Phase 17 — Dwujęzyczne pakiety + mapowanie błędów Softra
|
||||||
|
|
||||||
|
- [Phase 17, Plan 01] Pola `name_en`/`description_en` w panelu pakietów SOFT/PREMIUM + sanitize/save
|
||||||
|
- REST `/protection-packages` z helperem `resolve_locale()` (`?lang=` → `determine_locale()` → fallback)
|
||||||
|
- `Carei_Softra_API::extract_softra_message()` — parser JSON odpowiedzi błędu
|
||||||
|
- `Carei_Softra_API::map_error_message()` — słownik 13 typowych PL komunikatów Softra → `__()` z textdomain, exact + fuzzy prefix match, graceful passthrough
|
||||||
|
- Frontend: `loadProtectionPackages()` dodaje `?lang=` na podstawie `document.documentElement.lang`
|
||||||
|
- Pliki: `class-admin-panel.php`, `class-rest-proxy.php`, `class-softra-api.php`, `carei-reservation.js`
|
||||||
|
- `.paul/phases/17-bilingual-packages-and-softra-errors/17-01-PLAN.md` + SUMMARY
|
||||||
|
|
||||||
|
## Phase 18 — EN translation .po/.mo + Flatpickr
|
||||||
|
|
||||||
|
- [Phase 18, Plan 01] Tłumaczenie EN pluginu carei-reservation — 158 wpisów z .pot przetłumaczone na angielski (branża car rental)
|
||||||
|
- Własny PHP kompilator `po2mo.php` (msgfmt niedostępny) — parsuje .po, sortuje, pakuje binarnie wg gettext spec (magic 0x950412de)
|
||||||
|
- Wygenerowane `carei-reservation-en_US.po` + `.mo` (9455 bytes, N=158) oraz `en_GB.po` + `.mo`
|
||||||
|
- **Scope addition:** Flatpickr 4.6.13 (CDN jsdelivr) — cross-browser date picker z locale PL/EN dla modal + hero; `static: true` dla modala (popup w container, bypass focus-trap); `disableMobile: true` dla spójności UX
|
||||||
|
- Kompaktowy CSS theme flatpickr w kolorach Carei (#2F2482): szerokość 260px, font 12–13px, zaokrąglone dni, miękki fioletowy shadow
|
||||||
|
- Graceful fallback — jeśli CDN padnie, wraca do natywnego `datetime-local` pickera
|
||||||
|
|
||||||
|
## Zmienione pliki (Phase 18)
|
||||||
|
|
||||||
|
- `wp-content/plugins/carei-reservation/carei-reservation.php` (Flatpickr enqueue)
|
||||||
|
- `wp-content/plugins/carei-reservation/assets/js/carei-reservation.js` (initDatePickers)
|
||||||
|
- `wp-content/plugins/carei-reservation/assets/css/carei-reservation.css` (kompaktowy theme)
|
||||||
|
- `wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.po` (nowy)
|
||||||
|
- `wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.mo` (nowy)
|
||||||
|
- `wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.po` (nowy)
|
||||||
|
- `wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.mo` (nowy)
|
||||||
|
- `.paul/phases/18-en-translation/18-01-PLAN.md` + SUMMARY
|
||||||
|
|
||||||
|
## Milestone v0.7 — Dwujęzyczność (PL/EN) ✅ COMPLETE
|
||||||
|
|
||||||
|
Plugin carei-reservation działa w pełni dwujęzycznie (PL + EN), z Flatpickr pokrywającym też kalendarze. Phase 16 (infrastruktura i18n) + Phase 17 (bilingual pakiety + Softra errors) + Phase 18 (tłumaczenia + UX).
|
||||||
|
|
||||||
|
## Phase 19 — Extras translations admin panel
|
||||||
|
|
||||||
|
- [Phase 19, Plan 01] Admin panel do zarządzania tłumaczeniami dynamicznych nazw extras z Softra API
|
||||||
|
- Nowe WP options: `carei_extras_seen` (auto-collected PL names), `carei_extras_translations` (admin override PL → EN)
|
||||||
|
- Helpery w `Carei_Admin_Panel`: `remember_extra_name()`, `get_extras_seen()`, `get_extras_translations()`, `translate_extra_name()`
|
||||||
|
- Submenu `wp-admin → Rezerwacje → Tłumaczenia extras` (lista PL + inputy EN + zapis z nonce/sanitize)
|
||||||
|
- `Carei_REST_Proxy::get_pricelist()` — runtime auto-collect + per-locale replacement nazw z fallbackiem do PL
|
||||||
|
- Reuse `resolve_locale()` z Phase 17 — brak duplikacji
|
||||||
|
|
||||||
|
## Zmienione pliki (Phase 19)
|
||||||
|
|
||||||
|
- `wp-content/plugins/carei-reservation/includes/class-admin-panel.php`
|
||||||
|
- `wp-content/plugins/carei-reservation/includes/class-rest-proxy.php`
|
||||||
|
- `.paul/phases/19-extras-translations-admin/19-01-PLAN.md` + SUMMARY
|
||||||
|
|
||||||
|
## Milestone v0.8 — Admin-managed tłumaczenia extras ✅ COMPLETE
|
||||||
|
|
||||||
|
System tłumaczeń pluginu kompletny: statyczne stringi (.po/.mo), bilingual pakiety + błędy Softra, dynamiczne extras z admin override. Użytkownik EN widzi całość w języku angielskim (poza świadomie pominiętymi danymi biznesowymi: nazwy miast/krajów/klas pojazdów).
|
||||||
189
.paul/governance/governance_2026-04-22.jsonl
Normal file
189
.paul/governance/governance_2026-04-22.jsonl
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
{"ts":"2026-04-22T12:06:07Z","tool":"Bash","cmd":"mkdir -p \"C:/visual studio code/projekty/carei.pagedev.pl/.paul/phases/15-remove-softra-insurance\"\",\"description\":\"Create phase 15 dir\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"int","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:07:05Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\phases\\\\15-remove-softra-insurance\\\\15-01-PLAN.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:07:19Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:07:28Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:08:21Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:08:26Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:08:46Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:22:16Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:22:24Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:22:32Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:23:25Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\phases\\\\15-remove-softra-insurance\\\\15-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:23:33Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\changelog\\\\2026-04-22.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:23:39Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:23:46Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:23:51Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:23:57Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T12:24:05Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T13:13:55Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\mu-plugins\\\\fix-sprintf-global.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:24:42Z","tool":"Bash","cmd":"mkdir -p \"C:/visual studio code/projekty/carei.pagedev.pl/.paul/phases/16-i18n-plugin-refactor\"\",\"description\":\"Create phase 16 dir\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interr","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:26:55Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\phases\\\\16-i18n-plugin-refactor\\\\16-01-PLAN.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:27:08Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:27:15Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:27:21Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:27:26Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:27:33Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:33:26Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\carei-reservation.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:33:34Z","tool":"Bash","cmd":"mkdir -p \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/carei-reservation/languages\"\",\"description\":\"Create languages dir\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:33:45Z","tool":"Bash","cmd":"wc -l \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/carei-reservation/includes/\"*.php \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/carei-reservation/ass","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:34:58Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:35:04Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:35:09Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:35:33Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:35:39Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:35:40Z","tool":"Bash","cmd":"php -l \"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-elementor-widget.php\"\",\"description\":\"Validate PHP syntax\"},\","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:35:46Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:35:52Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:35:59Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:05Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:09Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:15Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:21Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:26Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:30Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:34Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:38Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:38Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:44Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:45Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:53Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:36:57Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:37:03Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:37:07Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:37:13Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:37:17Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:37:21Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:37:30Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:37:35Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:37:42Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:37:47Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:37:52Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:37:56Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:38:07Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:38:12Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:38:16Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:38:20Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:38:24Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:38:28Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:38:36Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:38:40Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:38:47Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:38:51Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:38:57Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:39:07Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:39:22Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:39:27Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:39:32Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:39:38Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:39:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:39:54Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:39:59Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:40:08Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:40:13Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:40:19Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:40:24Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:40:29Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:40:44Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:40:49Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:42:53Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-search-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:42:57Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-search-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:01Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-search-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:07Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-search-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:11Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-search-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:13Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-rest-proxy.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:15Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-search-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:17Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-rest-proxy.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:19Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-search-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:23Z","tool":"Bash","cmd":"php -l \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/carei-reservation/includes/class-rest-proxy.php\"\",\"description\":\"Check PHP syntax\"},\"tool_response\":{\"stdout\":\"No","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:25Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-search-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:29Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-cities-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:34Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-map-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:38Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-map-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:43Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-map-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:54Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-branches-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:43:59Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-branches-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:44:03Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-branches-widget.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:45:01Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\carei-reservation.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:46:50Z","tool":"Bash","cmd":"php -l \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/carei-reservation/carei-reservation.php\" 2>&1; php -l \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:52:17Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\phases\\\\16-i18n-plugin-refactor\\\\16-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:52:33Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\changelog\\\\2026-04-22.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:52:39Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:52:45Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:52:50Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:52:58Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:53:08Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:54:50Z","tool":"Bash","cmd":"mkdir -p \"C:/visual studio code/projekty/carei.pagedev.pl/.paul/phases/17-bilingual-packages-and-softra-errors\"\",\"description\":\"Create phase 17 dir\"},\"tool_response\":{\"stdout\":\"\",\"stderr","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:56:59Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\phases\\\\17-bilingual-packages-and-softra-errors\\\\17-01-PLAN.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:57:07Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:57:11Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:57:17Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:57:23Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T17:57:33Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:01:02Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:01:21Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:01:32Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:01:53Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-rest-proxy.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:02:25Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-softra-api.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:02:44Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:02:51Z","tool":"Bash","cmd":"php -l \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/carei-reservation/includes/class-admin-panel.php\" 2>&1; php -l \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:08:15Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\phases\\\\17-bilingual-packages-and-softra-errors\\\\17-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:08:25Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\changelog\\\\2026-04-22.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:08:31Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:08:37Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:08:43Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:08:50Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:09:01Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:10:08Z","tool":"Bash","cmd":"mkdir -p \"C:/visual studio code/projekty/carei.pagedev.pl/.paul/phases/18-en-translation\"; which msgfmt 2>&1 || echo \"msgfmt not available\"\",\"description\":\"Create dir + check msgfmt\"},\"tool_","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:12:19Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\phases\\\\18-en-translation\\\\18-01-PLAN.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:12:26Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:12:31Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:12:36Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:12:44Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:12:51Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:29:56Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\languages\\\\carei-reservation-en_US.po","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:30:05Z","tool":"Bash","cmd":"awk '/^msgstr/' \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.po\" | grep -cP \"[ąćęłńóśźżĄĆĘŁŃÓŚŹŻ]\"; gre","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:30:50Z","tool":"Write","file":"C:\\\\Users\\\\jacek\\\\temp\\\\po2mo.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:30:59Z","tool":"Bash","cmd":"php \"C:/Users/jacek/temp/po2mo.php\" \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.po\" \"C:/visual studio code/projekty/car","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:31:08Z","tool":"Bash","cmd":"cp \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.po\" \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/ca","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:31:19Z","tool":"Bash","cmd":"php -r \"\\\\$f='C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.mo'; \\\\$m=file_get_contents(\\\\$f,false,null,0,28); \\\\$h=un","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:39:29Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\carei-reservation.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:40:07Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:40:21Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:42:39Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:46:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:47:00Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:51:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:57:13Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\phases\\\\18-en-translation\\\\18-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:57:30Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\changelog\\\\2026-04-22.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:57:37Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:57:43Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:57:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:57:55Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:57:59Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T18:58:12Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:21:20Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:24:23Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:25:43Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:27:58Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:30:20Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:34:05Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:36:13Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:37:42Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\assets\\\\js\\\\carei-reservation.js","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:41:22Z","tool":"Bash","cmd":"mkdir -p \"C:/visual studio code/projekty/carei.pagedev.pl/.paul/phases/19-extras-translations-admin\"\",\"description\":\"Create phase 19 dir\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"i","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:43:05Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\phases\\\\19-extras-translations-admin\\\\19-01-PLAN.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:43:15Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:43:24Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:43:30Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:43:35Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:43:43Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:48:36Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:49:06Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-admin-panel.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:50:57Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\wp-content\\\\plugins\\\\carei-reservation\\\\includes\\\\class-rest-proxy.php","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:51:06Z","tool":"Bash","cmd":"php -l \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content/plugins/carei-reservation/includes/class-admin-panel.php\" 2>&1; php -l \"C:/visual studio code/projekty/carei.pagedev.pl/wp-content","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:57:31Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\phases\\\\19-extras-translations-admin\\\\19-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:57:46Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\changelog\\\\2026-04-22.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:57:54Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:58:02Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:58:09Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:58:17Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
|
{"ts":"2026-04-22T19:58:29Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\carei.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/carei.pagedev.pl"}
|
||||||
165
.paul/phases/15-remove-softra-insurance/15-01-PLAN.md
Normal file
165
.paul/phases/15-remove-softra-insurance/15-01-PLAN.md
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
---
|
||||||
|
phase: 15-remove-softra-insurance
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: []
|
||||||
|
files_modified:
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||||
|
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||||
|
autonomous: false
|
||||||
|
delegation: off
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
## Goal
|
||||||
|
Usunąć z modala rezerwacji wyświetlanie pozycji ubezpieczeniowych pobieranych z Softra API (np. "Zniesienie udziału własnego"). W sekcji "Pakiety ochronne" pozostają wyłącznie kafelki SOFT/PREMIUM zarządzane w panelu WP (Phase 13).
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Po wdrożeniu Phase 13 (pakiety WP) w sekcji "Pakiety ochronne" równolegle wyświetlały się dwa źródła: (1) kafelki WP (SOFT/PREMIUM), (2) pozycje z Softra pricelist rozpoznawane heurystyką nazwy ("zniesienie"/"insurance"). Powoduje to dublowanie oferty i konflikt z polityką cenową Carei. Zostawiamy jedno źródło prawdy — panel WP.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
- Dynamiczny kontener `#carei-insurance-container` usunięty z szablonu widgetu
|
||||||
|
- Logika JS klasyfikująca i renderująca `insuranceItems` usunięta
|
||||||
|
- Pozycje Softra rozpoznane jako „zniesienie/insurance” są **odfiltrowywane całkowicie** (nie trafiają ani do kafelków, ani do `extraItems`, ani do podsumowania)
|
||||||
|
- Pakiety WP (SOFT/PREMIUM) działają bez zmian
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.paul/PROJECT.md
|
||||||
|
@.paul/ROADMAP.md
|
||||||
|
@.paul/STATE.md
|
||||||
|
@.paul/phases/13-protection-packages/13-02-SUMMARY.md
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||||
|
@wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<acceptance_criteria>
|
||||||
|
|
||||||
|
## AC-1: Brak renderowania pozycji Softra-insurance
|
||||||
|
```gherkin
|
||||||
|
Given użytkownik otworzył modal rezerwacji i wybrał pojazd/daty
|
||||||
|
When załaduje się lista dodatków z Softra pricelist zawierająca pozycję "Zniesienie udziału własnego"
|
||||||
|
Then pozycja ta NIE pojawia się w sekcji "Pakiety ochronne"
|
||||||
|
And NIE pojawia się w sekcji "Opcje dodatkowe"
|
||||||
|
And w DOM nie istnieje element `#carei-insurance-container`
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-2: Pakiety WP działają bez zmian
|
||||||
|
```gherkin
|
||||||
|
Given w panelu WP są zdefiniowane pakiety SOFT i PREMIUM z ceną/dobę
|
||||||
|
When użytkownik otworzy modal
|
||||||
|
Then w sekcji "Pakiety ochronne" widoczne są wyłącznie kafelki SOFT i PREMIUM
|
||||||
|
And zaznaczenie pakietu dodaje wiersz do podsumowania i do `grandTotal` (bez zmian względem Phase 13)
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-3: Brak wycieku do podsumowania i submit payload
|
||||||
|
```gherkin
|
||||||
|
Given użytkownik przechodzi do podsumowania bez zaznaczania żadnego pakietu WP
|
||||||
|
When pricelist zawierał pozycję „Zniesienie udziału"
|
||||||
|
Then pozycja NIE pojawia się w tabeli podsumowania
|
||||||
|
And nie jest wysyłana w `priceItems` w `makebooking`
|
||||||
|
And całkowity koszt nie zawiera składnika ubezpieczenia Softra
|
||||||
|
```
|
||||||
|
|
||||||
|
</acceptance_criteria>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Usunięcie kontenera insurance z szablonu widgetu</name>
|
||||||
|
<files>wp-content/plugins/carei-reservation/includes/class-elementor-widget.php</files>
|
||||||
|
<action>
|
||||||
|
W `class-elementor-widget.php` (ok. linia 158–161) usunąć blok:
|
||||||
|
- `<div class="carei-form__protection-divider" aria-hidden="true"></div>`
|
||||||
|
- `<div class="carei-form__row" id="carei-insurance-container">...komentarz...</div>`
|
||||||
|
Zachować strukturę sekcji „Pakiety ochronne" z kontenerem `#carei-protection-packages-container` (kafelki SOFT/PREMIUM).
|
||||||
|
Unikać: usuwania dividerów sekcji zewnętrznej ani innych pól formularza. Zmieniamy wyłącznie divider wewnętrzny + kontener insurance.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
grep -n "carei-insurance-container" wp-content/plugins/carei-reservation/includes/class-elementor-widget.php → brak wyników.
|
||||||
|
Ręcznie: otwórz modal, sekcja „Pakiety ochronne" pokazuje tylko kafelki WP.
|
||||||
|
</verify>
|
||||||
|
<done>AC-1 satysfakcjonowane: kontener nie istnieje w DOM.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Usunięcie logiki insurance w carei-reservation.js</name>
|
||||||
|
<files>wp-content/plugins/carei-reservation/assets/js/carei-reservation.js</files>
|
||||||
|
<action>
|
||||||
|
1. Usunąć zmienną `insuranceContainer` z deklaracji (ok. linia 82) oraz jej przypisanie w init (`insuranceContainer = document.getElementById('carei-insurance-container');` ok. linia 107).
|
||||||
|
2. W funkcji renderującej dodatki (ok. linie 503–525):
|
||||||
|
- Usunąć lokalną zmienną `insuranceItems` i blok `if (insuranceContainer) { ... }`.
|
||||||
|
- Zastąpić heurystykę „dopasuj do insurance LUB extras" filtrowaniem typu **drop** — pozycje zawierające w nazwie `zniesienie` lub `insurance` (case-insensitive) mają być **pomijane całkowicie** (nie trafiają do `extraItems`).
|
||||||
|
- Pozostałe pozycje trafiają jak dotychczas do `extraItems` i są renderowane w `extrasContainer`.
|
||||||
|
3. Sprawdzić, że przy budowaniu payloadu (`priceItems` / `makebooking`) nie ma osobnej ścieżki pobierającej z `insuranceContainer` — jeśli jest, usunąć.
|
||||||
|
Unikać: zmian w logice Phase 13 (renderProtectionPackages, onProtectionCardClick, getSelectedProtectionPayload) — ten kod zostaje nietknięty. Nie usuwać `extrasContainer`.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
grep -nE "insuranceContainer|insuranceItems|carei-insurance-container" wp-content/plugins/carei-reservation/assets/js/carei-reservation.js → brak wyników.
|
||||||
|
grep -nE "zniesienie|insurance" wp-content/plugins/carei-reservation/assets/js/carei-reservation.js → tylko w filtrze drop.
|
||||||
|
</verify>
|
||||||
|
<done>AC-1, AC-3 satysfakcjonowane: pozycje Softra-insurance są dropowane przed renderem i przed budową payloadu.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="checkpoint:human-verify" gate="blocking">
|
||||||
|
<what-built>
|
||||||
|
- Usunięty kontener `#carei-insurance-container` i jego divider w szablonie PHP.
|
||||||
|
- Usunięta zmienna + render logic `insuranceItems` w JS; pozycje Softra „Zniesienie udziału"/„insurance" są pomijane.
|
||||||
|
- Pakiety WP (SOFT/PREMIUM) bez zmian.
|
||||||
|
</what-built>
|
||||||
|
<how-to-verify>
|
||||||
|
1. Wypchnij przez SFTP (albo zweryfikuj lokalnie, jeśli dostępny).
|
||||||
|
2. Otwórz stronę z modalem rezerwacji (carei.pagedev.pl) z DevTools.
|
||||||
|
3. Uzupełnij krok 1 formularza tak, aby pricelist z Softra został pobrany (wybór dat/oddziału/klasy).
|
||||||
|
4. Sprawdź sekcję „Pakiety ochronne":
|
||||||
|
- Widoczne TYLKO kafelki SOFT i PREMIUM z panelu WP.
|
||||||
|
- Brak pozycji „Zniesienie udziału własnego" ani żadnej innej z Softra.
|
||||||
|
5. Sprawdź sekcję „Opcje dodatkowe":
|
||||||
|
- Pozycje Softra są (fotelik, GPS itd.), ale brak pozycji zawierających słowo „zniesienie" lub „insurance".
|
||||||
|
6. W DevTools → Elements: `document.getElementById('carei-insurance-container')` → `null`.
|
||||||
|
7. Przejdź do podsumowania bez zaznaczania pakietu WP:
|
||||||
|
- W tabeli brak wiersza ubezpieczenia Softra.
|
||||||
|
- `grandTotal` = suma Softra (bez insurance) (+ pakiet WP jeśli zaznaczony).
|
||||||
|
8. Zaznacz pakiet SOFT → kafelek zaznacza się, wiersz pakietu pojawia się w podsumowaniu, total rośnie o cena×doba.
|
||||||
|
</how-to-verify>
|
||||||
|
<resume-signal>Napisz "approved" aby zamknąć plan, lub opisz problemy do poprawy.</resume-signal>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<boundaries>
|
||||||
|
|
||||||
|
## DO NOT CHANGE
|
||||||
|
- `includes/class-admin-panel.php` — panel WP pakietów (Phase 13 stable)
|
||||||
|
- `includes/class-rest-proxy.php` endpoint `/protection-packages`
|
||||||
|
- Logika Phase 13 w JS: `loadProtectionPackages`, `renderProtectionPackages`, `onProtectionCardClick`, `getSelectedProtectionPayload`, sekcja podsumowania z pakietem WP
|
||||||
|
- Kontener `#carei-protection-packages-container` i jego style
|
||||||
|
- Inne sekcje formularza (segment, daty, lokalizacja, wyjazd zagraniczny, dane osobowe)
|
||||||
|
|
||||||
|
## SCOPE LIMITS
|
||||||
|
- Nie zmieniamy heurystyki klasyfikacji innych pozycji extras
|
||||||
|
- Nie dotykamy backend (PHP) poza szablonem widgetu
|
||||||
|
- Nie ruszamy CSS — osierocone reguły `.carei-form__protection-divider` w CSS mogą zostać (brak użycia == brak efektu)
|
||||||
|
- Nie zmieniamy wywołań API Softra ani żadnych endpointów
|
||||||
|
</boundaries>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Przed zamknięciem planu:
|
||||||
|
- [ ] `grep -n "carei-insurance-container"` → 0 wyników w `includes/` oraz `assets/js/`
|
||||||
|
- [ ] `grep -n "insuranceContainer"` w JS → 0 wyników
|
||||||
|
- [ ] Modal otwiera się, sekcja „Pakiety ochronne" pokazuje tylko SOFT/PREMIUM
|
||||||
|
- [ ] Opcje dodatkowe: brak pozycji „Zniesienie udziału"
|
||||||
|
- [ ] Podsumowanie i makebooking payload wolne od Softra-insurance
|
||||||
|
- [ ] AC-1, AC-2, AC-3 przeszły human-verify
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- Wszystkie tasks zakończone
|
||||||
|
- Checkpoint human-verify zatwierdzony ("approved")
|
||||||
|
- Brak regresji w Phase 13 (pakiety WP) i w pobieraniu extras Softra
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
Po zakończeniu: `.paul/phases/15-remove-softra-insurance/15-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
111
.paul/phases/15-remove-softra-insurance/15-01-SUMMARY.md
Normal file
111
.paul/phases/15-remove-softra-insurance/15-01-SUMMARY.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
---
|
||||||
|
phase: 15-remove-softra-insurance
|
||||||
|
plan: 01
|
||||||
|
subsystem: ui
|
||||||
|
tags: [elementor, reservation-modal, pricelist, protection-packages, polylang-ready]
|
||||||
|
|
||||||
|
requires:
|
||||||
|
- phase: 13-protection-packages
|
||||||
|
provides: WP-managed SOFT/PREMIUM packages + /protection-packages REST endpoint
|
||||||
|
|
||||||
|
provides:
|
||||||
|
- Single source of truth for protection packages (panel WP)
|
||||||
|
- Drop-filter for Softra-insurance items in pricelist rendering
|
||||||
|
|
||||||
|
affects: future-extras-work
|
||||||
|
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "Pricelist drop-filter: pozycje zawierające ubezp/ochrony/zniesienie/insurance są pomijane przed renderem"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created: []
|
||||||
|
modified:
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||||
|
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Drop zamiast kategoryzacji: pozycje Softra-insurance są całkowicie pomijane (nie trafiają do extras ani payloadu)"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Jedno źródło prawdy dla pakietów ochronnych: panel WP (Phase 13)"
|
||||||
|
|
||||||
|
duration: ~10min
|
||||||
|
started: 2026-04-22
|
||||||
|
completed: 2026-04-22
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 15 Plan 01: Remove Softra-insurance z modala — Summary
|
||||||
|
|
||||||
|
**W sekcji „Pakiety ochronne" modala rezerwacji pozostają wyłącznie kafelki SOFT/PREMIUM z panelu WP; pozycje ubezpieczeniowe z Softra API są pomijane przed renderem i przed budową payloadu.**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| Duration | ~10min |
|
||||||
|
| Tasks | 2 auto + 1 human-verify completed |
|
||||||
|
| Files modified | 2 |
|
||||||
|
|
||||||
|
## Acceptance Criteria Results
|
||||||
|
|
||||||
|
| Criterion | Status | Notes |
|
||||||
|
|-----------|--------|-------|
|
||||||
|
| AC-1: Brak renderowania pozycji Softra-insurance | Pass | Kontener `#carei-insurance-container` usunięty z DOM; filtr drop w JS |
|
||||||
|
| AC-2: Pakiety WP działają bez zmian | Pass | Phase 13 logic nietknięta |
|
||||||
|
| AC-3: Brak wycieku do podsumowania i payloadu | Pass | Pozycje są dropowane przed `extraItems.push` — nie trafiają do summary ani do `priceItems` |
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- Usunięty kontener `#carei-insurance-container` + wewnętrzny divider w szablonie Elementor widget
|
||||||
|
- Usunięta zmienna `insuranceContainer` (deklaracja + init) z `carei-reservation.js`
|
||||||
|
- Heurystyka nazewnicza `ubezp|ochrony|zniesienie|insurance` zmieniona z „kategoryzuj jako insurance" na „drop całkowicie"
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
| File | Change | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `wp-content/plugins/carei-reservation/includes/class-elementor-widget.php` | Modified | Usunięty blok insurance-container + divider (linie ~158–161) |
|
||||||
|
| `wp-content/plugins/carei-reservation/assets/js/carei-reservation.js` | Modified | Usunięta zmienna + render insuranceContainer; pozycje Softra-insurance dropowane |
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
| Decision | Rationale | Impact |
|
||||||
|
|----------|-----------|--------|
|
||||||
|
| Drop zamiast oddzielnej kategorii | Phase 13 już dostarcza pakiety WP — Softra-insurance staje się redundantny i konfliktuje z polityką cenową | Prostsza logika, brak sekcji do utrzymania, brak konfliktu dwóch źródeł |
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
| Type | Count | Impact |
|
||||||
|
|------|-------|--------|
|
||||||
|
| Auto-fixed | 0 | — |
|
||||||
|
| Scope additions | 0 | — |
|
||||||
|
| Deferred | 0 | — |
|
||||||
|
|
||||||
|
**Total impact:** Plan wykonany dokładnie jak zaplanowano.
|
||||||
|
|
||||||
|
### Deferred Items
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
**Ready:**
|
||||||
|
- Modal rezerwacji wyświetla jedno źródło pakietów ochronnych (WP)
|
||||||
|
- Payload `makebooking` czysty — bez Softra-insurance
|
||||||
|
|
||||||
|
**Concerns:**
|
||||||
|
- Osierocona reguła CSS `.carei-form__protection-divider` pozostała w pliku (brak użycia = brak efektu wizualnego). Do opcjonalnego cleanupu w backlog.
|
||||||
|
|
||||||
|
**Blockers:** None.
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 15-remove-softra-insurance, Plan: 01*
|
||||||
|
*Completed: 2026-04-22*
|
||||||
316
.paul/phases/16-i18n-plugin-refactor/16-01-PLAN.md
Normal file
316
.paul/phases/16-i18n-plugin-refactor/16-01-PLAN.md
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
---
|
||||||
|
phase: 16-i18n-plugin-refactor
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: []
|
||||||
|
files_modified:
|
||||||
|
- wp-content/plugins/carei-reservation/carei-reservation.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-search-widget.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-cities-widget.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-map-widget.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-branches-widget.php
|
||||||
|
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||||
|
- wp-content/plugins/carei-reservation/languages/carei-reservation.pot (nowy)
|
||||||
|
autonomous: false
|
||||||
|
delegation: off
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
## Goal
|
||||||
|
Przygotować plugin `carei-reservation` do dwujęzyczności: wszystkie user-facing stringi w PHP owinięte w `__()`/`esc_html__()`/`esc_attr__()` z textdomain `carei-reservation`; stringi w JS zmigrowane do `wp_localize_script` (obiekt `careiI18n`) tłumaczony po stronie PHP; textdomain ładowany w bootstrapie; wygenerowany plik `.pot` gotowy do tłumaczenia w Phase 18.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Plugin pokrywa ~100% interakcji użytkownika w języku rezerwacji (modal, hero search, admin panel, widgety mapa/miasta/oddziały). Bez i18n żaden zewnętrzny translator (Polylang, Automatic Translate Addon, Loco) nie jest w stanie tłumaczyć tej zawartości — Polylang widzi tylko treść WordPressa/Elementora, a stringi w JS nie istnieją w DOM-ie serwerowo. i18n-refactor jest twardym blokerem dla Phase 17 i 18 milestone'u v0.7.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
- 8 plików PHP z stringami owiniętymi w funkcje i18n
|
||||||
|
- Bootstrap `carei-reservation.php` ładuje `load_plugin_textdomain` na `plugins_loaded`
|
||||||
|
- `carei-reservation.js` nie ma hardkodowanych stringów PL — używa obiektu `careiI18n`
|
||||||
|
- `class-elementor-widget.php` enqueue `wp_localize_script('carei-reservation', 'careiI18n', [...])` z kluczami zmapowanymi 1:1 na użycia w JS
|
||||||
|
- `languages/carei-reservation.pot` zawiera ~80–150 wpisów gotowych do tłumaczenia
|
||||||
|
- Strona po polsku działa **identycznie jak przed zmianami** (żaden tekst się nie zmienia — tylko jest owinięty)
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.paul/PROJECT.md
|
||||||
|
@.paul/ROADMAP.md
|
||||||
|
@.paul/STATE.md
|
||||||
|
@.paul/phases/13-protection-packages/13-02-SUMMARY.md
|
||||||
|
@.paul/phases/15-remove-softra-insurance/15-01-SUMMARY.md
|
||||||
|
@wp-content/plugins/carei-reservation/carei-reservation.php
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-search-widget.php
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-cities-widget.php
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-map-widget.php
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-branches-widget.php
|
||||||
|
@wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<acceptance_criteria>
|
||||||
|
|
||||||
|
## AC-1: PHP i18n kompletny, textdomain załadowany
|
||||||
|
```gherkin
|
||||||
|
Given plugin carei-reservation jest aktywny
|
||||||
|
When wyszukamy `grep -rn "__\|_e\|esc_html__\|esc_attr__" wp-content/plugins/carei-reservation/`
|
||||||
|
Then każdy user-facing string w plikach PHP jest owinięty w odpowiednią funkcję z textdomain 'carei-reservation'
|
||||||
|
And bootstrap `carei-reservation.php` wywołuje `load_plugin_textdomain('carei-reservation', false, dirname(plugin_basename(__FILE__)) . '/languages/')` na haku `plugins_loaded`
|
||||||
|
And żaden user-facing literał PL nie pozostaje bez opakowania (z wyjątkiem: komentarzy, kluczy meta zaczynających się od `_`, nazw taxonomii jako slugi)
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-2: JS migrated do careiI18n, brak regresji w PL
|
||||||
|
```gherkin
|
||||||
|
Given modal rezerwacji jest otwierany w języku polskim
|
||||||
|
When użytkownik przechodzi przez pełny flow (wybór dat/oddziału/klasy → podsumowanie → booking)
|
||||||
|
Then wszystkie etykiety, komunikaty błędów, placeholdery i nagłówki są identyczne tekstowo jak przed refactorem (AC: parity wizualna 1:1)
|
||||||
|
And plik `carei-reservation.js` NIE zawiera żadnego literału z polskimi znakami diakrytycznymi (ąćęłńóśźż) ani polskich słów kluczowych
|
||||||
|
And w DevTools → Console: `typeof window.careiI18n === 'object'` zwraca `true` po załadowaniu strony
|
||||||
|
And obiekt `careiI18n` zawiera klucze odpowiadające wszystkim zmigrowanym stringom
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-3: .pot wygenerowany, gotowy do tłumaczenia
|
||||||
|
```gherkin
|
||||||
|
Given Task 1 i 2 zakończone
|
||||||
|
When otworzymy `wp-content/plugins/carei-reservation/languages/carei-reservation.pot`
|
||||||
|
Then plik zawiera nagłówek z metadanymi pluginu (Project-Id-Version, Language: en)
|
||||||
|
And zawiera wpisy `msgid` dla wszystkich stringów z plików PHP (włącznie z kluczami z careiI18n w wp_localize_script)
|
||||||
|
And wszystkie `msgstr` są puste (ready for translation)
|
||||||
|
And ilość wpisów >= 80 (szacunek na podstawie skanu)
|
||||||
|
```
|
||||||
|
|
||||||
|
</acceptance_criteria>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: PHP i18n — wrap stringów + textdomain bootstrap</name>
|
||||||
|
<files>
|
||||||
|
wp-content/plugins/carei-reservation/carei-reservation.php,
|
||||||
|
wp-content/plugins/carei-reservation/includes/class-elementor-widget.php,
|
||||||
|
wp-content/plugins/carei-reservation/includes/class-admin-panel.php,
|
||||||
|
wp-content/plugins/carei-reservation/includes/class-rest-proxy.php,
|
||||||
|
wp-content/plugins/carei-reservation/includes/class-search-widget.php,
|
||||||
|
wp-content/plugins/carei-reservation/includes/class-cities-widget.php,
|
||||||
|
wp-content/plugins/carei-reservation/includes/class-map-widget.php,
|
||||||
|
wp-content/plugins/carei-reservation/includes/class-branches-widget.php
|
||||||
|
</files>
|
||||||
|
<action>
|
||||||
|
1. W `carei-reservation.php` w bootstrap pluginu dodać (na haku `plugins_loaded` lub przy init):
|
||||||
|
```php
|
||||||
|
add_action( 'plugins_loaded', function () {
|
||||||
|
load_plugin_textdomain( 'carei-reservation', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
|
||||||
|
} );
|
||||||
|
```
|
||||||
|
2. Utworzyć katalog `wp-content/plugins/carei-reservation/languages/` (pusty placeholder lub `.gitkeep`).
|
||||||
|
3. Dla każdego z 8 plików PHP — owinąć wszystkie user-facing stringi:
|
||||||
|
- Tekst w HTML-u → `esc_html__( 'tekst', 'carei-reservation' )` lub `esc_html_e( 'tekst', 'carei-reservation' )` (wewnątrz `echo`)
|
||||||
|
- Atrybuty (placeholder, aria-label, title, alt) → `esc_attr__( 'tekst', 'carei-reservation' )`
|
||||||
|
- Etykiety kontrolek Elementor (`add_control`, `add_responsive_control`) — `'label' => esc_html__( 'tekst', 'carei-reservation' )`
|
||||||
|
- Labels CPT i meta box (`class-admin-panel.php`) → `__( 'tekst', 'carei-reservation' )` w tablicach labels
|
||||||
|
- Kolumny admin listy, filtry, akcje masowe → `__()` / `esc_html__()`
|
||||||
|
- Komunikaty błędów z REST proxy (`class-rest-proxy.php`) w `WP_Error` → `__( 'komunikat', 'carei-reservation' )`
|
||||||
|
- Tytuły widgetów Elementor i ich opisy (get_title, get_keywords) → `esc_html__()`
|
||||||
|
4. NIE OWIJAJ:
|
||||||
|
- Slugów CPT/post_type/taxonomy (`carei_reservation`)
|
||||||
|
- Kluczy meta (`_carei_protection_package`, `_carei_status`)
|
||||||
|
- Nazw pól w payloadach do Softra API (klucze JSON)
|
||||||
|
- Kluczy tablic konfiguracyjnych, nazw hooków, nazw klas/funkcji
|
||||||
|
- Komentarzy PHP i docblocków
|
||||||
|
- Logów error_log (to stringi techniczne, nie user-facing)
|
||||||
|
- Wartości statusów w DB (`nowe`, `przeczytane`, `zrealizowane`) — zostają jako slug; tłumaczymy tylko UI labels (osobny klucz → label mapping)
|
||||||
|
5. Komunikaty z Softra API (Phase 17 zajmie się mapowaniem — w tym planie NIE ruszamy error message coming FROM Softra). Owijamy tylko nasze własne błędy.
|
||||||
|
6. Użyj konsystentnie `'carei-reservation'` jako textdomain — bez wyjątków.
|
||||||
|
|
||||||
|
Unikaj: globalnego find/replace bez kontekstu — każdy string wymaga decyzji czy jest user-facing czy nie. Nie zmieniaj logiki biznesowej, tylko opakowanie tekstu.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
1. `grep -rn "esc_html__\|__(\|esc_attr__" wp-content/plugins/carei-reservation/includes/ | wc -l` → >= 60 wystąpień
|
||||||
|
2. `grep -rn "load_plugin_textdomain" wp-content/plugins/carei-reservation/` → 1 wystąpienie w bootstrap
|
||||||
|
3. Ręcznie otwórz stronę z modalem po polsku → każdy tekst identyczny jak przed zmianą (nic się nie przetłumaczyło bo .po jeszcze nie ma)
|
||||||
|
4. Otwórz wp-admin → Rezerwacje → lista + szczegóły → wszystkie etykiety po PL identyczne
|
||||||
|
5. `php -l` na każdym z 8 plików → No syntax errors
|
||||||
|
</verify>
|
||||||
|
<done>AC-1 satysfakcjonowane.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: JS i18n — migracja stringów do careiI18n przez wp_localize_script</name>
|
||||||
|
<files>
|
||||||
|
wp-content/plugins/carei-reservation/assets/js/carei-reservation.js,
|
||||||
|
wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||||
|
</files>
|
||||||
|
<action>
|
||||||
|
1. W `class-elementor-widget.php` — w metodzie enqueue skryptów (tam gdzie `wp_enqueue_script('carei-reservation', ...)` ) dodać **po** enqueue:
|
||||||
|
```php
|
||||||
|
wp_localize_script( 'carei-reservation', 'careiI18n', array(
|
||||||
|
'selectSegment' => esc_html__( 'Wybierz segment', 'carei-reservation' ),
|
||||||
|
'selectBranch' => esc_html__( 'Wybierz oddział', 'carei-reservation' ),
|
||||||
|
'selectDates' => esc_html__( 'Wybierz daty', 'carei-reservation' ),
|
||||||
|
'loading' => esc_html__( 'Ładowanie...', 'carei-reservation' ),
|
||||||
|
'errorNetwork' => esc_html__( 'Błąd połączenia. Spróbuj ponownie.', 'carei-reservation' ),
|
||||||
|
'errorRequired' => esc_html__( 'To pole jest wymagane', 'carei-reservation' ),
|
||||||
|
// ...wszystkie pozostałe stringi użyte w JS
|
||||||
|
) );
|
||||||
|
```
|
||||||
|
**Uwaga:** jeśli widget renderuje się w więcej niż jednym miejscu (search widget też ładuje ten sam JS?), upewnij się że `wp_localize_script` leci tylko raz per żądanie (handle `carei-reservation` jest globalny).
|
||||||
|
|
||||||
|
2. W `carei-reservation.js` — odnaleźć WSZYSTKIE polskie stringi literalne i zamienić na odwołania do `careiI18n.kluczX`:
|
||||||
|
- Polskie znaki diakrytyczne (ąćęłńóśźż) są dobrym pierwotnym filtrem do wyszukania
|
||||||
|
- Etykiety w `buildExtraCard`, nagłówki sekcji, komunikaty walidacji, błędy API, teksty przycisków, placeholdery setowane z JS, toast/error summary
|
||||||
|
- Stringi budowane dynamicznie (`'Pakiet ochronny: ' + name + ' — ' + price`) → użyj wrappera z template string: `careiI18n.protectionLine.replace('%name%', name).replace('%price%', price)` i podobnie w PHP `sprintf()` nie jest potrzebny po stronie JS — użyj prostego `.replace()` z placeholderami `%name%`/`%days%`/`%total%`. Alternatywnie: przetłumaczony szablon z `{name}` / `{days}`.
|
||||||
|
- Lista krajów, dni tygodnia, nazwy miesięcy (jeśli są hardkodowane PL) → do `careiI18n` albo do natywnego `Intl.DateTimeFormat(locale)` tam gdzie to możliwe
|
||||||
|
- Konsol.log/error → **NIE ruszaj** (to techniczne)
|
||||||
|
|
||||||
|
3. Dla stringów z formatowaniem liczbowo-pluralnym (np. "1 doba" / "2 doby" / "5 dób") zdefiniuj w careiI18n 3 warianty (`dayOne`, `dayFew`, `dayMany`) i w JS napisz pomocnika `pluralPl(n, one, few, many)` który używa reguł polskich. Dla EN wystarczy `dayOne` / `dayOther`.
|
||||||
|
|
||||||
|
4. Końcowa weryfikacja: `grep -nE "[ąćęłńóśźż]" wp-content/plugins/carei-reservation/assets/js/carei-reservation.js` → zero wyników (poza komentarzami jeśli są).
|
||||||
|
|
||||||
|
Unikaj:
|
||||||
|
- Zmiany logiki biznesowej (walidacja, flow booking, API calls) — tylko tekst
|
||||||
|
- Tworzenia dziesięciu funkcji pomocniczych — jedna `t(key)` z fallbackiem do `key` wystarczy
|
||||||
|
- Zmiany `console.*` komunikatów (techniczne, nie user-facing)
|
||||||
|
- Nie twórz duplikatów stringów — jeśli „Wybierz oddział" występuje w 3 miejscach, ma 1 klucz
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
1. `grep -cP "[ąćęłńóśźż]" wp-content/plugins/carei-reservation/assets/js/carei-reservation.js` → 0 (lub tylko w komentarzach)
|
||||||
|
2. Otwórz modal w przeglądarce → `window.careiI18n` w console → obiekt z kluczami
|
||||||
|
3. Kliknij przez cały flow rezerwacji w języku polskim → tekst identyczny jak przed Task 2
|
||||||
|
4. DevTools Network → brak 404 dla `carei-reservation.js`
|
||||||
|
5. `node --check wp-content/plugins/carei-reservation/assets/js/carei-reservation.js` → OK (walidacja składni)
|
||||||
|
</verify>
|
||||||
|
<done>AC-2 satysfakcjonowane.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 3: Generate .pot file w languages/</name>
|
||||||
|
<files>wp-content/plugins/carei-reservation/languages/carei-reservation.pot</files>
|
||||||
|
<action>
|
||||||
|
1. Opcja A (preferowana jeśli dostępne WP-CLI na serwerze):
|
||||||
|
```bash
|
||||||
|
wp i18n make-pot wp-content/plugins/carei-reservation wp-content/plugins/carei-reservation/languages/carei-reservation.pot --domain=carei-reservation
|
||||||
|
```
|
||||||
|
2. Opcja B (ręcznie, jeśli brak WP-CLI):
|
||||||
|
- Skan wszystkich plików PHP (8 sztuk) i wyciągnięcie wszystkich `__()`, `esc_html__()`, `esc_attr__()` z textdomain `carei-reservation`
|
||||||
|
- Dodatkowo wyciągnięcie kluczy z `careiI18n` w `class-elementor-widget.php` (bo są też owinięte w `esc_html__`)
|
||||||
|
- Stworzenie pliku `.pot` zgodnie z formatem gettext:
|
||||||
|
```
|
||||||
|
# Copyright (C) 2026 Carei
|
||||||
|
# This file is distributed under the same license as the Carei Reservation plugin.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Carei Reservation 1.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2026-04-22\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Generator: manual\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:XXX
|
||||||
|
msgid "Złóż zapytanie o rezerwację"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
[...kolejne wpisy...]
|
||||||
|
```
|
||||||
|
- Każdy msgid pojawia się raz (deduplikacja), wszystkie msgstr puste
|
||||||
|
3. Jeśli opcja A zadziała → zweryfikuj liczbę wpisów (`grep -c "^msgid " plik.pot` >= 80)
|
||||||
|
4. Jeśli używamy B → spróbuj użyć **Loco Translate** (plugin w WP) do skanu i wygenerowania .pot — to najszybsza ścieżka w praktyce (Loco Translate → Plugins → Carei Reservation → Create template). Wtedy Task 3 staje się głównie zadaniem w wp-admin (nie w kodzie), i plik pojawia się w `languages/carei-reservation.pot` generowany przez Loco.
|
||||||
|
|
||||||
|
Unikaj: ręcznego edytowania .pot jeśli WP-CLI lub Loco dostępne. Automatyczne narzędzie nie przegapi stringa.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
1. Plik `languages/carei-reservation.pot` istnieje
|
||||||
|
2. `grep -c "^msgid " wp-content/plugins/carei-reservation/languages/carei-reservation.pot` → >= 80
|
||||||
|
3. Plik kończy się pustą linią i ma poprawny nagłówek (UTF-8)
|
||||||
|
4. Otwórz w POEdit / text editor → czy widoczne są stringi w formacie `msgid "Wybierz oddział"` z pustym `msgstr ""`
|
||||||
|
</verify>
|
||||||
|
<done>AC-3 satysfakcjonowane: .pot gotowy dla Phase 18 (tłumaczenie).</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="checkpoint:human-verify" gate="blocking">
|
||||||
|
<what-built>
|
||||||
|
- 8 plików PHP z hardkodowanymi PL stringami przeniesionymi do `__()`/`esc_html__()`/`esc_attr__()` z textdomain `carei-reservation`
|
||||||
|
- Bootstrap `carei-reservation.php` ładuje textdomain przez `load_plugin_textdomain` na `plugins_loaded`
|
||||||
|
- `carei-reservation.js` bez hardkodowanych PL — wszystkie stringi przez `careiI18n` globalny obiekt
|
||||||
|
- `class-elementor-widget.php` robi `wp_localize_script('carei-reservation', 'careiI18n', [...])` z PHP-owymi tłumaczeniami
|
||||||
|
- `languages/carei-reservation.pot` zawiera >= 80 wpisów gotowych do tłumaczenia
|
||||||
|
- **Strona po polsku działa identycznie jak przed Phase 16** — to jest kluczowe: żaden tekst się nie zmienia, zmienia się TYLKO sposób jego dostarczenia do frontendu
|
||||||
|
</what-built>
|
||||||
|
<how-to-verify>
|
||||||
|
1. Wypchnij cały plugin przez SFTP (całe `wp-content/plugins/carei-reservation/`)
|
||||||
|
2. W wp-admin: Plugins → deaktywuj i aktywuj ponownie „Carei Reservation" (żeby upewnić się że bootstrap nie crashuje)
|
||||||
|
3. Otwórz stronę z modalem rezerwacji PO POLSKU (Polylang = PL)
|
||||||
|
4. Pełen flow:
|
||||||
|
a. Wypełnij krok 1: segment, daty, oddział, klasa
|
||||||
|
b. Sprawdź sekcję „Pakiety ochronne" (SOFT/PREMIUM) — tekst identyczny
|
||||||
|
c. Sprawdź sekcję „Opcje dodatkowe"
|
||||||
|
d. Sekcja „Wyjazd zagraniczny" — wyszukiwarka krajów
|
||||||
|
e. Krok 2: podsumowanie → złóż rezerwację
|
||||||
|
f. Success view
|
||||||
|
5. Sprawdź hero search form (mini formularz w hero) — etykiety, placeholder, CTA
|
||||||
|
6. Sprawdź widgety: mapa Polski (tooltipy), grid miast, grid oddziałów — wszystkie teksty po PL
|
||||||
|
7. Wejdź w wp-admin → Rezerwacje:
|
||||||
|
- Lista z kolumnami, filtrem statusu — etykiety po PL
|
||||||
|
- Kliknij rezerwację → meta box szczegółów po PL
|
||||||
|
- Status dropdown (nowe/przeczytane/zrealizowane) po PL
|
||||||
|
8. Wejdź w wp-admin → Rezerwacje → Pakiety ochronne — formularz edycji SOFT/PREMIUM — etykiety po PL
|
||||||
|
9. DevTools Console (na stronie frontowej):
|
||||||
|
- `window.careiI18n` → obiekt z kluczami
|
||||||
|
- `typeof window.careiI18n.selectBranch` → `'string'` (lub jakikolwiek klucz który był migrowany)
|
||||||
|
10. Sprawdź plik `wp-content/plugins/carei-reservation/languages/carei-reservation.pot` — otwórz, zobacz stringi
|
||||||
|
11. (Opcjonalnie) Zainstaluj Loco Translate → Plugins → Carei Reservation → zobacz że plugin jest rozpoznany jako "translatable"
|
||||||
|
|
||||||
|
**Kryterium przejścia:** PL działa IDENTYCZNIE jak przed zmianą, zero regresji tekstowych.
|
||||||
|
</how-to-verify>
|
||||||
|
<resume-signal>Napisz "approved" aby zamknąć plan, albo opisz które miejsca pokazują nietłumaczone / regresyjne stringi.</resume-signal>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<boundaries>
|
||||||
|
|
||||||
|
## DO NOT CHANGE
|
||||||
|
- Logika biznesowa pluginu: `class-softra-api.php` (API calls, JWT auth, caching) — nie owijamy stringów technicznych ani nie zmieniamy flow
|
||||||
|
- Slugi CPT (`carei_reservation`), kluczy meta (`_carei_*`), nazw taxonomii, statusów w bazie (`nowe`, `przeczytane`, `zrealizowane`)
|
||||||
|
- Struktura payloadów wysyłanych do Softra API (klucze JSON, wartości boolean, nazwy pól)
|
||||||
|
- `class-softra-api.php` — ten plik nie ma user-facing stringów; zostaje nietknięty (mapowanie komunikatów z Softra = Phase 17)
|
||||||
|
- Hooki, nazwy akcji, nazwy filtrów WordPress
|
||||||
|
- Logika Phase 13 (pakiety ochronne): UI labels owijamy, ale struktura danych (REST `/protection-packages`, meta `_carei_protection_package`) bez zmian
|
||||||
|
- Logika Phase 15 (drop Softra-insurance): filtr pozostaje, komunikaty JS też owijamy w careiI18n
|
||||||
|
|
||||||
|
## SCOPE LIMITS
|
||||||
|
- Nie generujemy tłumaczeń EN w tym planie — tylko `.pot`. Phase 18 zajmie się `.po`/`.mo`
|
||||||
|
- Nie dodajemy dwujęzycznych pól w panelu pakietów ochronnych — Phase 17
|
||||||
|
- Nie mapujemy komunikatów z Softra API — Phase 17
|
||||||
|
- Nie tłumaczymy treści dynamicznych z bazy (np. nazwy pakietów w DB zostają po PL na tym etapie — zmieni to Phase 17)
|
||||||
|
- Nie dotykamy CSS
|
||||||
|
- Nie dotykamy treści w Elementorze (strony, widgety natywne) — te już są tłumaczone przez Polylang addon
|
||||||
|
</boundaries>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Przed zamknięciem planu:
|
||||||
|
- [ ] `grep -rn "load_plugin_textdomain" wp-content/plugins/carei-reservation/carei-reservation.php` → 1 wynik
|
||||||
|
- [ ] `grep -rnE "__\(|esc_html__|esc_attr__" wp-content/plugins/carei-reservation/includes/ | wc -l` → >= 60
|
||||||
|
- [ ] `grep -cP "[ąćęłńóśźż]" wp-content/plugins/carei-reservation/assets/js/carei-reservation.js` → 0 (lub tylko komentarze)
|
||||||
|
- [ ] `wp-content/plugins/carei-reservation/languages/carei-reservation.pot` istnieje, >= 80 wpisów msgid
|
||||||
|
- [ ] `php -l` dla wszystkich 8 plików → No syntax errors
|
||||||
|
- [ ] Strona PL działa bez regresji (checkpoint human-verify)
|
||||||
|
- [ ] AC-1, AC-2, AC-3 przeszły weryfikację
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- Wszystkie 3 auto tasks zakończone
|
||||||
|
- Checkpoint human-verify zatwierdzony ("approved")
|
||||||
|
- Brak regresji w języku polskim
|
||||||
|
- `.pot` gotowy do Phase 18
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
Po zakończeniu: `.paul/phases/16-i18n-plugin-refactor/16-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
156
.paul/phases/16-i18n-plugin-refactor/16-01-SUMMARY.md
Normal file
156
.paul/phases/16-i18n-plugin-refactor/16-01-SUMMARY.md
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
---
|
||||||
|
phase: 16-i18n-plugin-refactor
|
||||||
|
plan: 01
|
||||||
|
subsystem: i18n
|
||||||
|
tags: [polylang, gettext, pot, wp-localize-script, textdomain, elementor]
|
||||||
|
|
||||||
|
requires:
|
||||||
|
- phase: 13-protection-packages
|
||||||
|
provides: panel WP pakietów SOFT/PREMIUM
|
||||||
|
- phase: 15-remove-softra-insurance
|
||||||
|
provides: czysty pricelist bez Softra-insurance
|
||||||
|
|
||||||
|
provides:
|
||||||
|
- textdomain 'carei-reservation' ładowany na plugins_loaded
|
||||||
|
- wszystkie user-facing PHP stringi w __()/esc_html__()/esc_attr__()
|
||||||
|
- helper I18N/t()/tFmt()/pluralPl() w carei-reservation.js
|
||||||
|
- wp_localize_script('careiI18n', [...]) z 78 kluczami
|
||||||
|
- languages/carei-reservation.pot (157 msgid)
|
||||||
|
|
||||||
|
affects: [17-bilingual-packages, 18-en-translation]
|
||||||
|
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "Helper I18N + t(key, fallback) w JS z obiektem window.careiI18n"
|
||||||
|
- "pluralPl(n, one, few, many) — polskie reguły pluralizacji (1=one, 2-4 bez 12-14=few, reszta=many)"
|
||||||
|
- "Drop Softra passthrough errors z tłumaczenia — mapowanie zostaje na Phase 17"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- wp-content/plugins/carei-reservation/languages/carei-reservation.pot
|
||||||
|
modified:
|
||||||
|
- wp-content/plugins/carei-reservation/carei-reservation.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-search-widget.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-cities-widget.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-map-widget.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-branches-widget.php
|
||||||
|
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Bootstrap: wp_localize_script dla careiI18n leci w wp_enqueue_scripts (nie w Elementor widget render) — obiekt dostępny na każdej stronie gdzie ładowany jest skrypt"
|
||||||
|
- "Helper get_status_label() zamiast statycznej tablicy labels w Admin panelu — pozwala tłumaczyć w runtime po załadowaniu textdomain"
|
||||||
|
- "Softra API passthrough errors nietłumaczone w Phase 16 — Phase 17 zajmie się mapowaniem"
|
||||||
|
- "Fallback english w t()/tFmt() = oryginalny polski string (żeby bez .po strona dalej działała po polsku)"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Klucze careiI18n: camelCase, grupowane semantycznie (selectX, btnX, labelX, errorX, thX, rejectX, announceX)"
|
||||||
|
- "Multi-line UI stringi z HTML (np. <strong>%count% %unit%</strong>) przez wp_kses po stronie PHP, renderowane jako innerHTML w JS"
|
||||||
|
|
||||||
|
duration: ~45min
|
||||||
|
started: 2026-04-22
|
||||||
|
completed: 2026-04-22
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 16 Plan 01: i18n refactor pluginu — Summary
|
||||||
|
|
||||||
|
**Plugin carei-reservation przygotowany do dwujęzyczności: 8 plików PHP owiniętych w funkcje gettext, JS zmigrowany do obiektu `window.careiI18n` przez `wp_localize_script`, wygenerowany `.pot` z 157 unikalnymi msgid gotowy do tłumaczenia w Phase 18.**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| Duration | ~45min |
|
||||||
|
| Tasks | 3 auto + 1 human-verify completed |
|
||||||
|
| Files modified | 9 |
|
||||||
|
| Files created | 1 (.pot) |
|
||||||
|
| Delegation | 5 agentów równolegle (1 elementor-widget, 1 admin-panel, 1 JS, 1 dla 4 widgetów, 1 .pot) |
|
||||||
|
|
||||||
|
## Acceptance Criteria Results
|
||||||
|
|
||||||
|
| Criterion | Status | Notes |
|
||||||
|
|-----------|--------|-------|
|
||||||
|
| AC-1: PHP i18n kompletny, textdomain załadowany | Pass | `load_plugin_textdomain` na plugins_loaded; ~105 stringów PHP owiniętych |
|
||||||
|
| AC-2: JS migrated do careiI18n, brak regresji w PL | Pass | `window.careiI18n` = 78 kluczy, grep diakrytyków w JS = 0 poza fallbackami/API payloads; human-verify PL OK |
|
||||||
|
| AC-3: .pot wygenerowany | Pass | 157 msgid, UTF-8, nagłówek poprawny, msgstr puste |
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- **8 plików PHP** z user-facing stringami w funkcjach i18n (carei-reservation, elementor-widget, admin-panel, rest-proxy, search/cities/map/branches widgets)
|
||||||
|
- **`carei-reservation.js`** (1573 linie) — ~70 literałów przez `t()`/`tFmt()` + helper `pluralPl()` dla polskich form
|
||||||
|
- **`wp_localize_script('careiI18n', [...])`** z 78 kluczami w bootstrapie
|
||||||
|
- **`.pot`** gotowy do tłumaczenia — 157 unikalnych msgid w `languages/`
|
||||||
|
- Dekonstrukcja admin-panelu: statyczna tablica labels statusów rozbita na helper `get_status_label()` (wymóg WP — translation w runtime)
|
||||||
|
- Zgoda RODO z osadzonym `<a>` — owinięcie przez `wp_kses(__(...))`
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
| File | Change | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `carei-reservation.php` | Modified | load_plugin_textdomain + wp_localize_script('careiI18n', 78 kluczy) |
|
||||||
|
| `includes/class-elementor-widget.php` | Modified | ~30 stringów (Elementor controls + HTML modal) |
|
||||||
|
| `includes/class-admin-panel.php` | Modified | ~55 stringów + helper get_status_label() |
|
||||||
|
| `includes/class-rest-proxy.php` | Modified | 2 stringi (Invalid nonce, Softra not configured) |
|
||||||
|
| `includes/class-search-widget.php` | Modified | 8 stringów (hero search form) |
|
||||||
|
| `includes/class-cities-widget.php` | Modified | 1 string (get_title) |
|
||||||
|
| `includes/class-map-widget.php` | Modified | 3 stringi (Oddział %s, ul. %s, get_title) |
|
||||||
|
| `includes/class-branches-widget.php` | Modified | 3 stringi |
|
||||||
|
| `assets/js/carei-reservation.js` | Modified | ~70 literałów → t()/tFmt() + helpery + pluralPl() |
|
||||||
|
| `languages/carei-reservation.pot` | Created | 157 msgid, UTF-8, ready for Phase 18 |
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
| Decision | Rationale | Impact |
|
||||||
|
|----------|-----------|--------|
|
||||||
|
| `careiI18n` w bootstrapie (nie w Elementor render) | Skrypt jest rejestrowany w bootstrapie → localize leci raz, dostępne globalnie | Upraszcza architekturę, nie ma per-widget duplikacji |
|
||||||
|
| Fallback w `t('key', 'polski string')` | Bez .mo plik dalej działa po polsku; bez niego crashowałoby `undefined` | Zero-downtime dla PL, bezpieczne wrap bez straty funkcjonalności |
|
||||||
|
| Softra error messages nietłumaczone | Zewnętrzne API zwraca polskie błędy — mapowanie = Phase 17 | Zostaje jasna granica między „nasze" a „Softra" stringami |
|
||||||
|
| Statuses slugi niezmienione (`nowe`/`przeczytane`/`zrealizowane`) | Dane w DB, klucze logiki; tylko UI labels idą przez `get_status_label()` | Brak regresji w filtrach i warunkach logicznych |
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
| Type | Count | Impact |
|
||||||
|
|------|-------|--------|
|
||||||
|
| Auto-fixed | 1 | Admin panel: statyczna tablica statuses → helper (konieczne dla WP i18n timing) |
|
||||||
|
| Scope additions | 0 | — |
|
||||||
|
| Deferred | 1 | Softra API error mapping (świadomie przełożone na Phase 17) |
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. [Admin panel] Statyczna tablica `$statuses` z labels nie może być tłumaczona przed init**
|
||||||
|
- Found during: Task 1 (admin-panel refactor)
|
||||||
|
- Issue: Tablica jako property klasy = translation wywoływane przed załadowaniem textdomain → pusty string
|
||||||
|
- Fix: Rozbicie na helper `get_status_label($key)` z switchem, wywoływany w runtime po plugins_loaded
|
||||||
|
- Files: class-admin-panel.php
|
||||||
|
- Verification: human-verify PL — statusy na liście, w dropdown i meta boxie wyglądają identycznie jak przed
|
||||||
|
|
||||||
|
### Deferred Items
|
||||||
|
|
||||||
|
- Phase 17: mapowanie ~10-15 typowych komunikatów Softra API na lokalizowane wersje (np. `Brak pojazdów w tym terminie` → reject key + tłumaczenie)
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
**Ready:**
|
||||||
|
- `.pot` z 157 msgid — gotowy input dla Phase 18 (tłumaczenie na EN przez GPT-4.1 mini lub Loco Translate)
|
||||||
|
- Infrastruktura i18n kompletna — Phase 17 i 18 mogą budować na niej bez refactoru
|
||||||
|
- Polski UI działa bez regresji (confirmed by user: „język polski jest ok.")
|
||||||
|
|
||||||
|
**Concerns:**
|
||||||
|
- `window.careiI18n` rośnie (78 kluczy) — dla performance można rozważyć lazy-load w przyszłości, ale na razie ~3KB JSON to szum
|
||||||
|
- Niektóre msgid zawierają HTML (`Wybrano: <strong>%count% %unit%</strong>`) — wymagają translatora świadomego tagów; .pot ma to oznaczone wp_kses whitelist
|
||||||
|
- Softra error messages (Phase 17) — w JS są klucze `rejectCarNotFound` itd. jako fallbacki, ale aktualny kod Phase 16 nie używa jeszcze mapowania → Phase 17 musi dodać warstwę w rest-proxy lub softra-api
|
||||||
|
|
||||||
|
**Blockers:** None.
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 16-i18n-plugin-refactor, Plan: 01*
|
||||||
|
*Completed: 2026-04-22*
|
||||||
@@ -0,0 +1,305 @@
|
|||||||
|
---
|
||||||
|
phase: 17-bilingual-packages-and-softra-errors
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: ["16-01"]
|
||||||
|
files_modified:
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-softra-api.php
|
||||||
|
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js (drobne — użycie istniejących reject* kluczy)
|
||||||
|
autonomous: false
|
||||||
|
delegation: off
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
## Goal
|
||||||
|
(1) Panel administratora pakietów ochronnych SOFT/PREMIUM dostaje dodatkowe pola `name_en` i `description_en`. REST endpoint `/protection-packages` zwraca wariant zgodny z aktualnym locale (`get_locale()` lub parametr `?lang=`): PL → pola bazowe, EN → pola `_en` z fallbackiem do bazowych gdy puste. (2) Słownik mapowania ~12–15 typowych komunikatów Softra API (PL stringi zwracane przez zewnętrzny system) na lokalizowane klucze — warstwa w `Carei_Softra_API` / `Carei_REST_Proxy` podmienia `message` w `WP_Error`/response na tekst wg current locale.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Bez Phase 17 użytkownik na wersji EN widziałby (1) polskie nazwy pakietów ochronnych wprowadzone przez admina w panelu WP, (2) polskie komunikaty z Softra API przy konflikcie rezerwacji / braku pojazdu / błędzie walidacji. Phase 17 domyka jedyne dwa źródła „obcych" polskich tekstów pozostałych po Phase 16 — po niej całość UI jest dwujęzyczna bez wyjątków.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
- Panel `Rezerwacje → Pakiety ochronne`: każdy pakiet (SOFT/PREMIUM) ma 4 pola tekstowe zamiast 2 — `name` + `name_en`, `description` + `description_en` (cena `pricePerDay` pozostaje jedna, wspólna)
|
||||||
|
- Option `carei_protection_packages` w DB ma nową strukturę: `soft: {name, name_en, description, description_en, pricePerDay, enabled}`, analogicznie premium
|
||||||
|
- REST `/protection-packages` zwraca wariant zlokalizowany wg `determine_locale()` — klucze `name`/`description` w odpowiedzi są już właściwe dla języka (EN lub PL), frontend nie musi wiedzieć nic o wariantach
|
||||||
|
- Słownik mapowania w `Carei_Softra_API` (nowa metoda `map_error_message( $pl_message )`) — zwraca sparowany klucz tłumaczenia z textdomain `carei-reservation` dla znanych komunikatów, albo oryginał jeśli brak mapowania
|
||||||
|
- Komunikaty w REST response / `WP_Error` przechodzą przez filtr mapowania przed zwróceniem do frontu
|
||||||
|
- Zero regresji w wersji PL — wszystkie pakiety i błędy wyświetlają się identycznie jak po Phase 16
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.paul/PROJECT.md
|
||||||
|
@.paul/ROADMAP.md
|
||||||
|
@.paul/STATE.md
|
||||||
|
@.paul/phases/13-protection-packages/13-02-SUMMARY.md
|
||||||
|
@.paul/phases/16-i18n-plugin-refactor/16-01-SUMMARY.md
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-softra-api.php
|
||||||
|
@wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<acceptance_criteria>
|
||||||
|
|
||||||
|
## AC-1: Panel pakietów obsługuje pola _en
|
||||||
|
```gherkin
|
||||||
|
Given administrator wchodzi w `wp-admin → Rezerwacje → Pakiety ochronne`
|
||||||
|
When wypełnia pola `Nazwa` / `Nazwa (EN)` / `Opis` / `Opis (EN)` dla SOFT i PREMIUM i klika "Zapisz"
|
||||||
|
Then wszystkie 4 pola tekstowe per pakiet są zapisane w option `carei_protection_packages`
|
||||||
|
And po odświeżeniu strony formularz pokazuje zapisane wartości (w tym EN)
|
||||||
|
And walidacja/sanityzacja jest konsystentna między polami PL i EN
|
||||||
|
And brak EN (puste pole `name_en`/`description_en`) jest poprawny — to oznacza „fallback do PL"
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-2: REST endpoint zwraca wariant per locale
|
||||||
|
```gherkin
|
||||||
|
Given w DB są zapisane pakiety z polami PL i EN
|
||||||
|
When frontend PL woła `/wp-json/carei/v1/protection-packages` (locale = pl_PL)
|
||||||
|
Then odpowiedź zawiera `soft.name` i `soft.description` z wariantu PL
|
||||||
|
And analogicznie dla premium
|
||||||
|
|
||||||
|
When frontend EN (Polylang język EN) woła ten sam endpoint
|
||||||
|
Then odpowiedź zawiera `soft.name` = wartość `name_en` (lub PL jeśli pole EN puste)
|
||||||
|
And `soft.description` = `description_en` z fallbackiem
|
||||||
|
And struktura odpowiedzi nie zmienia kluczy (`name`, `description`, `pricePerDay`, `enabled`) — tylko treści
|
||||||
|
And frontend JS nie wymaga żadnej zmiany logicznej (oprócz ewentualnego `?lang=` jeśli Polylang nie ustawia locale serwerowo)
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-3: Błędy Softra mapowane na lokalizowane stringi
|
||||||
|
```gherkin
|
||||||
|
Given użytkownik próbuje zarezerwować pojazd niedostępny w danym terminie
|
||||||
|
When Softra API zwraca message "Brak dostępnego pojazdu w wybranym terminie" (lub podobny)
|
||||||
|
Then `Carei_Softra_API::map_error_message()` rozpoznaje string
|
||||||
|
And zwraca lokalizowany wariant zgodny z current locale (`__('rejectCarNotFound', ...)` lub bezpośredni tekst EN)
|
||||||
|
And REST response `message` zawiera tekst w języku zgodnym z UI
|
||||||
|
And dla nieznanych komunikatów — przepuszcza oryginał (graceful fallback)
|
||||||
|
And słownik pokrywa co najmniej 12 typowych komunikatów (lista w Task 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
</acceptance_criteria>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Panel admina — pola name_en / description_en + zapis/odczyt</name>
|
||||||
|
<files>wp-content/plugins/carei-reservation/includes/class-admin-panel.php</files>
|
||||||
|
<action>
|
||||||
|
1. Zaktualizuj `get_protection_packages_defaults()` — dodaj domyślne klucze `name_en` i `description_en` per pakiet (wartości puste albo angielskie defaulty typu `'Protection SOFT'` / `'Protection PREMIUM'`).
|
||||||
|
2. Zaktualizuj `get_protection_packages()` — metodę czyszczenia/merge z defaultami, żeby obsłużyła nowe pola (użytkownicy z starą strukturą dostają puste EN, bez crasha).
|
||||||
|
3. W `render_protection_packages_page()`:
|
||||||
|
- Dla każdego pakietu (SOFT, PREMIUM) dodać pod polami PL dwa kolejne wiersze:
|
||||||
|
- `<input type="text" name="packages[soft][name_en]">` z labelką `esc_html__( 'Nazwa (EN)', 'carei-reservation' )`
|
||||||
|
- `<textarea name="packages[soft][description_en]">` z labelką `esc_html__( 'Opis (EN)', 'carei-reservation' )`
|
||||||
|
- Wizualnie oddzielone (np. `<small>` note: „puste = fallback do wersji polskiej")
|
||||||
|
- Placeholdery w `esc_attr__()`
|
||||||
|
4. W `handle_protection_packages_save()`:
|
||||||
|
- Sanitize `name_en` przez `sanitize_text_field()`
|
||||||
|
- Sanitize `description_en` przez `sanitize_textarea_field()`
|
||||||
|
- Zapisz oba pola w tej samej strukturze co PL
|
||||||
|
- Nie ruszaj walidacji `pricePerDay` ani `enabled` — bez zmian
|
||||||
|
5. Nie zmieniaj slugów meta, endpointu REST (Task 2), żadnej logiki biznesowej poza polem form.
|
||||||
|
|
||||||
|
Unikaj:
|
||||||
|
- Dodawania nowego mechanizmu po stronie DB (np. osobnej opcji dla EN) — wszystko w istniejącym `carei_protection_packages`
|
||||||
|
- Tłumaczenia opisu na bieżąco w panelu admin — admin wpisuje ręcznie EN
|
||||||
|
- Refaktoru istniejącej struktury defaulti — tylko DODAJ klucze
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
1. Otwórz `wp-admin → Rezerwacje → Pakiety ochronne` — widoczne 4 pola tekstowe per pakiet (PL name, EN name, PL description, EN description) + cena + enabled
|
||||||
|
2. Wpisz testowe wartości w EN, zapisz → po odświeżeniu zachowane
|
||||||
|
3. `get_option( 'carei_protection_packages' )` w wp-admin → Tools → Site Health → Info (lub `wp db query` na locale) zawiera nowe klucze `name_en`, `description_en`
|
||||||
|
4. `php -l class-admin-panel.php` → No syntax errors
|
||||||
|
5. Stary kod readujący `$packages['soft']['name']` dalej działa (brak BC break)
|
||||||
|
</verify>
|
||||||
|
<done>AC-1 satysfakcjonowane.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: REST endpoint per-locale + mapowanie błędów Softra</name>
|
||||||
|
<files>
|
||||||
|
wp-content/plugins/carei-reservation/includes/class-rest-proxy.php,
|
||||||
|
wp-content/plugins/carei-reservation/includes/class-softra-api.php
|
||||||
|
</files>
|
||||||
|
<action>
|
||||||
|
|
||||||
|
**Część A — REST `/protection-packages` zwraca wariant zlokalizowany:**
|
||||||
|
|
||||||
|
1. W `class-rest-proxy.php` odnajdź handler endpointu `/protection-packages` (prawdopodobnie metoda typu `get_protection_packages()`).
|
||||||
|
2. Dodaj helper `resolve_locale()`:
|
||||||
|
```php
|
||||||
|
private function resolve_locale( $request ) {
|
||||||
|
$lang = $request->get_param( 'lang' );
|
||||||
|
if ( $lang && in_array( strtolower( $lang ), array( 'pl', 'en' ), true ) ) {
|
||||||
|
return strtolower( $lang );
|
||||||
|
}
|
||||||
|
$locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
|
||||||
|
return ( 0 === strpos( $locale, 'en' ) ) ? 'en' : 'pl';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- Polylang przy żądaniach REST powinien ustawić locale automatycznie (filter `locale` lub `determine_locale`). Gdyby nie — frontend może dopisać `?lang=en` jawnie (fallback). JS zostaje bez zmian jeśli Polylang dobrze współpracuje z WP REST.
|
||||||
|
3. W handlerze endpointu: po pobraniu `$packages = Carei_Admin_Panel::get_protection_packages()`:
|
||||||
|
- Dla lokalu `'en'`: podmień `$pkg['name']` na `$pkg['name_en']` gdy niepuste, inaczej pozostaw `$pkg['name']`. Analogicznie `description`.
|
||||||
|
- Dla lokalu `'pl'`: bez zmian.
|
||||||
|
- Usuń z odpowiedzi klucze `name_en`/`description_en` (frontend nie musi ich widzieć — unika leakowania i mylenia schematu).
|
||||||
|
4. Odpowiedź REST zachowuje schemat: `{ soft: { name, description, pricePerDay, enabled }, premium: {...} }`.
|
||||||
|
|
||||||
|
**Część B — Mapowanie błędów Softra:**
|
||||||
|
|
||||||
|
5. W `class-softra-api.php` dodaj nową public static method:
|
||||||
|
```php
|
||||||
|
public static function map_error_message( $original_message ) {
|
||||||
|
if ( ! is_string( $original_message ) || '' === trim( $original_message ) ) {
|
||||||
|
return $original_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dict = array(
|
||||||
|
'Brak dostępnego pojazdu w wybranym terminie' => __( 'Brak dostępnego pojazdu w wybranym terminie. Zmień daty lub segment.', 'carei-reservation' ),
|
||||||
|
'Nieprawidłowy zakres dat' => __( 'Nieprawidłowy zakres dat', 'carei-reservation' ),
|
||||||
|
'Nie znaleziono oddziału' => __( 'Nie znaleziono oddziału', 'carei-reservation' ),
|
||||||
|
'Klient o tych danych już istnieje' => __( 'Klient o tych danych już istnieje w systemie', 'carei-reservation' ),
|
||||||
|
'Nieprawidłowy numer PESEL' => __( 'Nieprawidłowy numer PESEL', 'carei-reservation' ),
|
||||||
|
'Cennik wygasł' => __( 'Cennik wygasł. Odśwież formularz i spróbuj ponownie.', 'carei-reservation' ),
|
||||||
|
'Token wygasł' => __( 'Sesja wygasła. Odśwież stronę.', 'carei-reservation' ),
|
||||||
|
'Nieprawidłowe dane logowania' => __( 'Błąd autoryzacji API. Skontaktuj się z administratorem.', 'carei-reservation' ),
|
||||||
|
'Brak uprawnień' => __( 'Brak uprawnień do wykonania operacji.', 'carei-reservation' ),
|
||||||
|
'Błąd serwera' => __( 'Błąd serwera. Spróbuj ponownie za chwilę.', 'carei-reservation' ),
|
||||||
|
'Przekroczono limit rezerwacji' => __( 'Przekroczono limit rezerwacji dla tego klienta.', 'carei-reservation' ),
|
||||||
|
'Nieprawidłowy numer telefonu' => __( 'Podaj poprawny numer telefonu (min. 9 cyfr).', 'carei-reservation' ),
|
||||||
|
'Wymagane pole' => __( 'To pole jest wymagane.', 'carei-reservation' ),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Exact match
|
||||||
|
if ( isset( $dict[ $original_message ] ) ) {
|
||||||
|
return $dict[ $original_message ];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fuzzy: prefix match (Softra bywa mało przewidywalny z końcówkami)
|
||||||
|
foreach ( $dict as $pl_key => $translated ) {
|
||||||
|
if ( 0 === stripos( $original_message, $pl_key ) ) {
|
||||||
|
return $translated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $original_message; // graceful fallback
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- Zasada: `__()` przechodzi przez textdomain `carei-reservation` → dla locale EN pobiera tłumaczenie z `.mo` (Phase 18). W PL zwraca ten sam tekst co msgid (co jest OK — zachowuje polski oryginał).
|
||||||
|
6. Zintegruj mapowanie:
|
||||||
|
- W miejscach gdzie `Carei_Softra_API` zwraca błąd (np. metoda `make_booking`, `get_car_classes`, itp.) — po odebraniu `$response['error']['message']` lub `$response['message']` (sprawdź strukturę w istniejącym kodzie) zawiń przez `self::map_error_message( $msg )` przed utworzeniem `WP_Error` / przed zwrotem.
|
||||||
|
- Jeśli API używa `WP_Error` już z warstwy `rest-proxy` — zaktualizuj tam (przechwyć message, mapuj, przekaż dalej).
|
||||||
|
|
||||||
|
**Część C — Frontend (drobna zmiana w JS):**
|
||||||
|
|
||||||
|
7. W `carei-reservation.js` w miejscach gdzie łapiesz `err.message` z odpowiedzi API przy niepowodzeniu rezerwacji — jeśli obecnie wyświetlasz surowy message, zostaw bez zmian (backend już mapuje). Jeśli masz heurystykę kategoryzacji po kluczach (np. "no car available" → pokaż specjalny alert), rozważ aktualizację. W MINIMUM: upewnij się że fallbacki `rejectCarNotFound` itd. w `careiI18n` są używane gdzie trzeba. Prawdopodobnie zmiana zero/minimal.
|
||||||
|
8. Jeśli frontend musi jawnie dopisać `?lang=` przy requeście `/protection-packages` (bo Polylang nie ustawia locale REST automatycznie) — dodaj to w `loadProtectionPackages()`:
|
||||||
|
```js
|
||||||
|
var lang = (document.documentElement.lang || '').toLowerCase().indexOf('en') === 0 ? 'en' : 'pl';
|
||||||
|
fetch(REST_URL + 'protection-packages?lang=' + lang, {...})
|
||||||
|
```
|
||||||
|
|
||||||
|
Unikaj:
|
||||||
|
- Tłumaczenia komunikatów po stronie JS — wszystko leci z PHP (`__()` rozwiązuje per locale)
|
||||||
|
- Wprowadzania nowego schemat odpowiedzi (`name_pl` / `name_en` w payloadzie) — odpowiedź zawsze ma `name` w właściwym języku
|
||||||
|
- Nadpisywania obecnej logiki reject — tylko dodaj warstwę mapowania
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
1. `php -l` dla obu zmienionych plików → No syntax errors
|
||||||
|
2. W PL (Polylang = PL): `curl /wp-json/carei/v1/protection-packages` → `soft.name` = polska nazwa, `soft.description` = polski opis
|
||||||
|
3. W EN (Polylang = EN) albo `?lang=en`: `soft.name` = angielska nazwa (lub polska fallback gdy puste)
|
||||||
|
4. Odpowiedź REST NIE zawiera kluczy `name_en`/`description_en` (czyste API)
|
||||||
|
5. Wywołanie błędnej rezerwacji (niedostępny pojazd) → komunikat zwrócony do frontendu = tłumaczenie z `__()` zamiast surowego Softra-PL (w EN)
|
||||||
|
6. Nieznany komunikat Softra (spoza słownika) → passthrough bez zmian (graceful)
|
||||||
|
</verify>
|
||||||
|
<done>AC-2, AC-3 satysfakcjonowane.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="checkpoint:human-verify" gate="blocking">
|
||||||
|
<what-built>
|
||||||
|
- Panel `wp-admin → Rezerwacje → Pakiety ochronne` ma teraz 4 pola tekstowe per pakiet (`name`, `name_en`, `description`, `description_en`) + cena + enabled
|
||||||
|
- Option `carei_protection_packages` przechowuje wersje PL i EN każdego pakietu
|
||||||
|
- REST `/protection-packages` zwraca wariant per locale (Polylang EN → pola `_en` z fallbackiem do PL)
|
||||||
|
- Słownik `Carei_Softra_API::map_error_message()` z 12+ wpisami + exact/fuzzy match, wbudowany w ścieżkę zwrotu błędów API
|
||||||
|
- Wersja PL — zero regresji, wersja EN — pakiety i błędy w języku EN (po załadowaniu .mo w Phase 18)
|
||||||
|
</what-built>
|
||||||
|
<how-to-verify>
|
||||||
|
1. Wypchnij zmienione 3–4 pliki przez SFTP
|
||||||
|
2. **Admin panel test:**
|
||||||
|
- `wp-admin → Rezerwacje → Pakiety ochronne`
|
||||||
|
- Sprawdź że są widoczne nowe pola: „Nazwa (EN)", „Opis (EN)" dla SOFT i PREMIUM
|
||||||
|
- Wypełnij testowo: SOFT EN name = „SOFT Protection", EN description = „Basic protection package..."
|
||||||
|
- PREMIUM EN name = „PREMIUM Protection", EN description = „Enhanced protection..."
|
||||||
|
- Kliknij „Zapisz" → odśwież stronę → wartości EN są zachowane
|
||||||
|
3. **Frontend PL (Polylang = PL):**
|
||||||
|
- Otwórz modal rezerwacji → sekcja „Pakiety ochronne"
|
||||||
|
- Kafelki SOFT/PREMIUM pokazują **polskie** nazwy i opisy (jak zapisane w polach PL)
|
||||||
|
- Cena `X.XX zł/doba` — bez zmian
|
||||||
|
4. **Frontend EN (Polylang = EN):**
|
||||||
|
- Przełącz język na EN w switcherze Polylang
|
||||||
|
- Otwórz modal rezerwacji → sekcja „Pakiety ochronne"
|
||||||
|
- Kafelki pokazują **angielskie** nazwy i opisy (wartości z pól `_en`)
|
||||||
|
- Jeśli któreś pole `_en` zostawiłeś puste → frontend powinien pokazać PL wariant (fallback)
|
||||||
|
5. **Test fallbacku:**
|
||||||
|
- Wróć do admin → pole EN jednego pakietu wyczyść
|
||||||
|
- Frontend EN → ten pakiet pokazuje polski oryginał (graceful)
|
||||||
|
6. **Error mapping test:**
|
||||||
|
- Wymuś błąd Softra (np. data w przeszłości albo niedostępny pojazd — jeśli API tak odpowiada)
|
||||||
|
- PL: komunikat w języku polskim (bez zmian)
|
||||||
|
- EN: komunikat po angielsku (po załadowaniu .mo w Phase 18) albo oryginalne PL jeśli Phase 18 jeszcze niedostępne
|
||||||
|
- Uwaga: **dopóki .po/.mo nie istnieją (Phase 18), `__()` zwraca oryginalny polski string w obu językach.** To jest OK — Phase 17 dostarcza infrastrukturę, Phase 18 dostarcza treść.
|
||||||
|
7. **DevTools Network:**
|
||||||
|
- `GET /wp-json/carei/v1/protection-packages` w PL → odpowiedź z PL
|
||||||
|
- `GET /wp-json/carei/v1/protection-packages?lang=en` → odpowiedź z EN
|
||||||
|
- Oba responsy nie zawierają pól `name_en`/`description_en` w payloadzie
|
||||||
|
8. **Brak regresji:**
|
||||||
|
- Cały flow rezerwacji PL działa jak po Phase 16 — nic się nie popsuło
|
||||||
|
|
||||||
|
**Kryterium przejścia:** Admin zapisuje i odczytuje EN pola, REST zwraca wariant per locale, error mapping działa (graceful fallback dla nieznanych), PL bez zmian.
|
||||||
|
</how-to-verify>
|
||||||
|
<resume-signal>Napisz "approved" lub opisz co nie działa.</resume-signal>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<boundaries>
|
||||||
|
|
||||||
|
## DO NOT CHANGE
|
||||||
|
- Schema `carei_reservation` CPT, meta keys (`_carei_*`), statusy w DB
|
||||||
|
- Logika JWT auth w `Carei_Softra_API` (cache, retry)
|
||||||
|
- Phase 13 struktura REST endpointu `/protection-packages` — klucze odpowiedzi (`name`, `description`, `pricePerDay`, `enabled`) pozostają, zmienia się treść per locale
|
||||||
|
- Phase 15 filtr Softra-insurance (drop) — nietknięty
|
||||||
|
- Phase 16 textdomain i wp_localize_script — tylko ROZSZERZAMY (dokładamy nowe klucze jeśli potrzeba), nie refaktorujemy
|
||||||
|
- Frontend JS — minimalne zmiany (ewentualnie `?lang=` parameter), bez refactoru flow
|
||||||
|
|
||||||
|
## SCOPE LIMITS
|
||||||
|
- Nie generujemy tłumaczeń `.po`/`.mo` — to Phase 18
|
||||||
|
- Nie dodajemy mechanizmu Polylang "String Translation" per-post-meta (nadkomplikacja) — option w DB z polami `_en` wystarczy dla 2 pakietów
|
||||||
|
- Nie rozszerzamy słownika Softra na >20 pozycji — 12–15 pokrywa realne przypadki, resztę mapujemy iteracyjnie gdy się pojawią
|
||||||
|
- Nie tłumaczymy statusów rezerwacji w DB (`nowe`/`przeczytane`/`zrealizowane`) — tylko UI labels (już zrobione w Phase 16)
|
||||||
|
- Nie dotykamy Elementora ani treści stron
|
||||||
|
</boundaries>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Przed zamknięciem planu:
|
||||||
|
- [ ] Panel admin pokazuje 4 pola per pakiet (PL + EN name, PL + EN description)
|
||||||
|
- [ ] Zapisanie EN w panelu → dane utrzymują się po reload
|
||||||
|
- [ ] REST `/protection-packages` w PL zwraca polskie, w EN zwraca angielskie (lub PL fallback)
|
||||||
|
- [ ] Odpowiedź REST nie zawiera `name_en`/`description_en` (czyste API)
|
||||||
|
- [ ] `Carei_Softra_API::map_error_message()` istnieje, słownik ma >= 12 wpisów
|
||||||
|
- [ ] Exact match + fuzzy prefix match działają
|
||||||
|
- [ ] Zero regresji w PL (human-verify)
|
||||||
|
- [ ] AC-1, AC-2, AC-3 przeszły weryfikację
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- Wszystkie 2 auto tasks zakończone
|
||||||
|
- Checkpoint human-verify zatwierdzony ("approved")
|
||||||
|
- PL bez regresji
|
||||||
|
- EN: pakiety pokazują wartości z pól `_en` (lub PL fallback)
|
||||||
|
- Infrastruktura mapowania błędów Softra gotowa (tłumaczenia pojawią się po Phase 18)
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
Po zakończeniu: `.paul/phases/17-bilingual-packages-and-softra-errors/17-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
---
|
||||||
|
phase: 17-bilingual-packages-and-softra-errors
|
||||||
|
plan: 01
|
||||||
|
subsystem: i18n
|
||||||
|
tags: [polylang, softra-errors, rest-api, protection-packages, bilingual]
|
||||||
|
|
||||||
|
requires:
|
||||||
|
- phase: 13-protection-packages
|
||||||
|
provides: panel WP pakietów SOFT/PREMIUM + REST /protection-packages
|
||||||
|
- phase: 16-i18n-plugin-refactor
|
||||||
|
provides: textdomain carei-reservation + __() we wszystkich plikach
|
||||||
|
|
||||||
|
provides:
|
||||||
|
- Pola name_en / description_en w panelu pakietów + zapis/odczyt
|
||||||
|
- REST /protection-packages zwraca wariant per locale (z fallbackiem PL)
|
||||||
|
- Carei_Softra_API::extract_softra_message() + map_error_message() ze słownikiem 13 komunikatów
|
||||||
|
- JS loadProtectionPackages() dodaje ?lang= na podstawie document.documentElement.lang
|
||||||
|
|
||||||
|
affects: [18-en-translation]
|
||||||
|
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "Locale resolution: ?lang= param → determine_locale() → get_locale() fallback"
|
||||||
|
- "Softra error mapping: exact match → fuzzy prefix match → graceful passthrough"
|
||||||
|
- "Bilingual fields pattern: base field + _en variant w tej samej WP option, fallback gdy _en puste"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
modified:
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-softra-api.php
|
||||||
|
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Klucze _en nie wyciekają do REST response — frontend dostaje już rozwiązane name/description"
|
||||||
|
- "map_error_message przez __() zamiast surowych PL→EN — czeka na .mo (Phase 18) bez osobnego słownika angielskiego"
|
||||||
|
- "document.documentElement.lang zamiast explicit Polylang API — działa cross-plugin (TranslatePress, WPML) jeśli ktoś zmieni w przyszłości"
|
||||||
|
- "Default name_en w defaults (SOFT Protection / PREMIUM Protection) — admin dostaje sensowną propozycję od razu"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Bilingual option: {base, base_en, base_description, description_en} — jedna opcja w WP DB, fallback gdy puste EN"
|
||||||
|
- "REST locale resolution z priorytetem ?lang=<pl|en> — explicit > implicit (determine_locale)"
|
||||||
|
|
||||||
|
duration: ~25min
|
||||||
|
started: 2026-04-22
|
||||||
|
completed: 2026-04-22
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 17 Plan 01: Dwujęzyczne pakiety + mapowanie błędów Softra — Summary
|
||||||
|
|
||||||
|
**Panel admina pakietów ochronnych zyskał pola name_en/description_en, REST /protection-packages zwraca wariant per locale z fallbackiem, a błędy z Softra API przechodzą przez słownik 13 typowych komunikatów owinięty w __() — infrastruktura EN gotowa dla Phase 18.**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| Duration | ~25min |
|
||||||
|
| Tasks | 2 auto + 1 human-verify completed |
|
||||||
|
| Files modified | 4 |
|
||||||
|
| Delegation | 0 (inline — precyzyjne zmiany) |
|
||||||
|
|
||||||
|
## Acceptance Criteria Results
|
||||||
|
|
||||||
|
| Criterion | Status | Notes |
|
||||||
|
|-----------|--------|-------|
|
||||||
|
| AC-1: Panel pakietów obsługuje pola _en | Pass | 4 pola tekstowe per pakiet, sanitize + zapis w tej samej WP option, fallback gdy EN puste |
|
||||||
|
| AC-2: REST endpoint zwraca wariant per locale | Pass | ?lang= param + determine_locale fallback; _en klucze nie wyciekają |
|
||||||
|
| AC-3: Błędy Softra mapowane | Pass | 13 wpisów w słowniku + exact match + fuzzy prefix + graceful passthrough |
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- **Admin panel:** 4 pola tekstowe per pakiet (PL + EN dla name i description) + placeholder + opis "Puste = fallback do wersji polskiej"
|
||||||
|
- **`Carei_REST_Proxy::resolve_locale()`** — helper z priorytetem `?lang=pl|en` → `determine_locale()` → `get_locale()`
|
||||||
|
- **`/protection-packages`** zwraca `name`/`description` rozwiązane per locale, `_en` klucze ukryte w payloadzie
|
||||||
|
- **`Carei_Softra_API::extract_softra_message()`** — parser JSON odpowiedzi Softra (pola `message`/`error`/`details`/`description`)
|
||||||
|
- **`Carei_Softra_API::map_error_message()`** — 13 typowych komunikatów PL → `__()` z textdomain, exact + fuzzy prefix match
|
||||||
|
- **Integracja w `request()`** — błędy HTTP 4xx/5xx przechodzą przez extract→map przed `WP_Error`
|
||||||
|
- **Frontend `loadProtectionPackages()`** dodaje `?lang=` na podstawie `document.documentElement.lang`
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
| File | Change | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `includes/class-admin-panel.php` | Modified | Pola _en + sanitize/save |
|
||||||
|
| `includes/class-rest-proxy.php` | Modified | resolve_locale() + per-locale response |
|
||||||
|
| `includes/class-softra-api.php` | Modified | extract_softra_message() + map_error_message() + integracja w request() |
|
||||||
|
| `assets/js/carei-reservation.js` | Modified | ?lang= param w loadProtectionPackages() |
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
| Decision | Rationale | Impact |
|
||||||
|
|----------|-----------|--------|
|
||||||
|
| `_en` keys nie w REST response | Czyste API — frontend nie wie o wariantach, dostaje już rozwiązany string | Brak zmian w JS poza lang= param |
|
||||||
|
| `map_error_message` przez `__()` a nie statyczne EN | Użyj istniejącej infrastruktury textdomain — Phase 18 automatycznie dostarcza tłumaczenia | Jeden słownik tłumaczeń, jedno miejsce aktualizacji |
|
||||||
|
| `document.documentElement.lang` zamiast Polylang-specific | Niezależne od konkretnego pluginu i18n; Polylang, TranslatePress, WPML wszystkie ustawiają html@lang | Future-proof przy zmianie wtyczki tłumaczeniowej |
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
| Type | Count | Impact |
|
||||||
|
|------|-------|--------|
|
||||||
|
| Auto-fixed | 0 | — |
|
||||||
|
| Scope additions | 1 | Dodano `extract_softra_message()` — plan zakładał prostsze podejście, ale Softra zwraca różne struktury JSON |
|
||||||
|
| Deferred | 0 | — |
|
||||||
|
|
||||||
|
### Scope additions
|
||||||
|
|
||||||
|
**1. [Robustness] Helper `extract_softra_message()`**
|
||||||
|
- Found during: Task 2b (integracja mapowania w request())
|
||||||
|
- Issue: Softra API zwraca różne struktury błędu (message w root, zagnieżdżone w error, albo raw string) — prosty `$body['message']` by nie pokrył wszystkich przypadków
|
||||||
|
- Fix: Dedykowany parser z iteracją przez typowe klucze (`message`, `error`, `details`, `description`) + rekurencja dla stringa JSON
|
||||||
|
- Files: class-softra-api.php
|
||||||
|
- Verification: Edge cases sprawdzone w głowie — array passthrough, string JSON, nested, empty fallback
|
||||||
|
- Impact: +30 linii kodu, ale gwarantuje że mapowanie działa niezależnie od variantu odpowiedzi Softra
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
**Ready:**
|
||||||
|
- Kompletna infrastruktura dla EN — Phase 18 tylko generuje `.po` → `.mo` i wrzuca w `languages/`
|
||||||
|
- Pakiety ochronne (SOFT/PREMIUM) — admin wypełni pola EN, frontend natychmiast pokaże (nie czeka na .mo)
|
||||||
|
- Błędy Softra — słownik 13 msgid gotowy do tłumaczenia w .po
|
||||||
|
|
||||||
|
**Concerns:**
|
||||||
|
- Jeśli w przyszłości Softra doda nowy komunikat → słownik trzeba ręcznie rozszerzać (mamy fuzzy prefix match, ale nie pokrywa wszystkiego)
|
||||||
|
- Performance `loadProtectionPackages()` — doszedł `?lang=` param; nie wpływa na caching (WordPress nie cachuje tego endpointu, Phase 13 save też inwaliduje)
|
||||||
|
- Admin EN pole może zostać puste → fallback do PL jest poprawny, ale trzeba to wyraźnie komunikować w UI (już jest: "Puste = fallback do wersji polskiej")
|
||||||
|
|
||||||
|
**Blockers:** None. Phase 18 może ruszyć.
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 17-bilingual-packages-and-softra-errors, Plan: 01*
|
||||||
|
*Completed: 2026-04-22*
|
||||||
283
.paul/phases/18-en-translation/18-01-PLAN.md
Normal file
283
.paul/phases/18-en-translation/18-01-PLAN.md
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
---
|
||||||
|
phase: 18-en-translation
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: ["16-01", "17-01"]
|
||||||
|
files_modified:
|
||||||
|
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.po (nowy)
|
||||||
|
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.po (nowy, symlink/kopia en_US)
|
||||||
|
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.mo (nowy, binarny)
|
||||||
|
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.mo (nowy, binarny)
|
||||||
|
autonomous: false
|
||||||
|
delegation: off
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
## Goal
|
||||||
|
Dostarczyć kompletne tłumaczenie EN dla pluginu carei-reservation: przetłumaczyć wszystkie 157 msgid z `carei-reservation.pot` na `msgstr` angielskie w pliku `carei-reservation-en_US.po`, skompilować do `.mo`, zduplikować jako `en_GB` (Polylang w WordPress może używać różnych locale EN — pokrywamy oba najczęstsze warianty). Po uploadzie plików UI pluginu na wersji angielskiej przełącza się całkowicie.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Phase 16 dostarczyła infrastrukturę (`__()`, textdomain, `.pot`), Phase 17 dodała bilingual pakiety + mapowanie błędów Softra. **Phase 18 to jedyna faza, która wizualnie przełączy cały plugin na EN.** Do tej pory użytkownik EN widzi oryginalne polskie stringi — brak `.mo` = brak tłumaczeń, `__('Wybierz segment pojazdu', ...)` zwraca polski msgid.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
- `carei-reservation-en_US.po` — 157 msgstr wypełnionych profesjonalnym angielskim tłumaczeniem branży wynajmu samochodów
|
||||||
|
- `carei-reservation-en_US.mo` — binarny plik zgodny z formatem gettext (magic bytes `0x950412de`, big-endian byte order)
|
||||||
|
- `carei-reservation-en_GB.po` + `.mo` — identyczna kopia (lub drobne różnice `rental` vs `hire`, `license` vs `licence` — opcjonalne)
|
||||||
|
- Po załadowaniu przez `load_plugin_textdomain` (już wpięte w Phase 16) — wszystkie `__()`/`esc_html__()` i `wp_localize_script('careiI18n')` zwracają EN w locale `en_*`
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.paul/PROJECT.md
|
||||||
|
@.paul/ROADMAP.md
|
||||||
|
@.paul/STATE.md
|
||||||
|
@.paul/phases/16-i18n-plugin-refactor/16-01-SUMMARY.md
|
||||||
|
@.paul/phases/17-bilingual-packages-and-softra-errors/17-01-SUMMARY.md
|
||||||
|
@wp-content/plugins/carei-reservation/languages/carei-reservation.pot
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<acceptance_criteria>
|
||||||
|
|
||||||
|
## AC-1: Plik .po kompletnie przetłumaczony
|
||||||
|
```gherkin
|
||||||
|
Given istnieje plik `languages/carei-reservation-en_US.po`
|
||||||
|
When policzymy niepuste msgstr (`grep -c '^msgstr "[^"]' plik.po`)
|
||||||
|
Then wszystkie 157 wpisów ma wypełnione msgstr (nie-puste)
|
||||||
|
And nagłówek zawiera Language: en_US, poprawne Plural-Forms (nplurals=2; plural=(n != 1))
|
||||||
|
And msgid zawierające placeholdery `%count%`, `%name%`, `%price%`, `%status%` itp. mają je **zachowane w msgstr** (nietknięte — te same tokeny)
|
||||||
|
And msgid zawierające tagi HTML (`<strong>...</strong>`) mają je zachowane
|
||||||
|
And tłumaczenie zachowuje branżowy ton: `doba` → `day`, `oddział` → `location`, `pakiet ochronny` → `protection package`, `rezerwacja` → `reservation`, `zł` → `PLN`, itp.
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-2: Plik .mo poprawny binarnie
|
||||||
|
```gherkin
|
||||||
|
Given istnieje plik `languages/carei-reservation-en_US.mo`
|
||||||
|
When otworzymy go jako binary
|
||||||
|
Then pierwsze 4 bajty to magic number `0xde120495` (little-endian) LUB `0x950412de` (big-endian)
|
||||||
|
And zawiera wszystkie tłumaczenia z .po
|
||||||
|
And daje się poprawnie sparsować przez `gettext` (WordPress i18n czyta go bez błędu)
|
||||||
|
And jest < 50 KB (typowy rozmiar dla 157 wpisów)
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-3: EN UI działa po uploadzie
|
||||||
|
```gherkin
|
||||||
|
Given administrator ustawił Polylang locale na en_US albo en_GB
|
||||||
|
When użytkownik otwiera stronę w EN
|
||||||
|
Then modal rezerwacji ma WSZYSTKIE etykiety po angielsku (segment, dates, location, protection packages, booking summary, success)
|
||||||
|
And hero search form po angielsku
|
||||||
|
And widgety (mapa, grid miast, grid oddziałów) po angielsku gdzie są stringi UI
|
||||||
|
And wp-admin → Rezerwacje (dla administratora z EN locale) po angielsku
|
||||||
|
And błędy z Softra API (dla znanych komunikatów) po angielsku
|
||||||
|
And pakiety ochronne pokazują wartości z pól `_en` (lub PL fallback gdy puste — z Phase 17)
|
||||||
|
```
|
||||||
|
|
||||||
|
</acceptance_criteria>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Translate .pot → carei-reservation-en_US.po</name>
|
||||||
|
<files>wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.po</files>
|
||||||
|
<action>
|
||||||
|
1. Przeczytaj `languages/carei-reservation.pot` (157 msgid, ~13.7 KB)
|
||||||
|
2. Dla każdego wpisu msgid wygeneruj angielskie tłumaczenie w msgstr:
|
||||||
|
- **Ton:** profesjonalny, branża wynajmu samochodów (car rental)
|
||||||
|
- **Kluczowe terminy:**
|
||||||
|
- „doba" / „dób" / „doby" → `day` / `days` (bez skomplikowanej pluralizacji — EN ma tylko one/other)
|
||||||
|
- „oddział" → `location` (nie `branch` — bardziej rental-naturalne)
|
||||||
|
- „pakiet ochronny" → `protection package`
|
||||||
|
- „rezerwacja" → `reservation` (nie `booking` — spójne z nazwą pluginu)
|
||||||
|
- „Złóż zapytanie o rezerwację" → `Request a reservation` (skrócone, CTA-friendly)
|
||||||
|
- „klient" → `customer`
|
||||||
|
- „zł" → `PLN`
|
||||||
|
- „Wyjazd zagraniczny" → `International travel`
|
||||||
|
- „Zniesienie udziału" → `Deductible waiver` (jeśli gdzieś zostało)
|
||||||
|
- „najemca" → `renter`
|
||||||
|
- „segment pojazdu" → `vehicle segment`
|
||||||
|
- „miejsce odbioru" / „miejsce zwrotu" → `pickup location` / `return location`
|
||||||
|
- „politykę prywatności" → `privacy policy`
|
||||||
|
- „Pakiet SOFT" → `SOFT Package` (nazwy własne bez zmian)
|
||||||
|
- **Placeholdery:** tokeny typu `%name%`, `%count%`, `%price%`, `%status%`, `%days%`, `%total%`, `%unit%`, `%no%`, `%msg%`, `%perDay%`, `%min%`, `%max%`, `%label%` → **ZACHOWAJ DOKŁADNIE** jak w oryginale (nie tłumacz, nie zmieniaj kolejności placeholderów jeśli to zmieniałoby znaczenie; ale możesz zmienić kolejność słów wokół nich)
|
||||||
|
- **HTML tagi:** `<strong>...</strong>` → ZACHOWAJ
|
||||||
|
- **%s** (sprintf) → ZACHOWAJ
|
||||||
|
3. Zapisz jako `wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.po` z nagłówkiem:
|
||||||
|
```
|
||||||
|
# English (US) translation for Carei Reservation
|
||||||
|
# This file is distributed under the same license as the Carei Reservation plugin.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Carei Reservation 1.0.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2026-04-22 12:00+0000\n"
|
||||||
|
"PO-Revision-Date: 2026-04-22 12:00+0000\n"
|
||||||
|
"Last-Translator: Carei\n"
|
||||||
|
"Language-Team: English\n"
|
||||||
|
"Language: en_US\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Domain: carei-reservation\n"
|
||||||
|
```
|
||||||
|
4. Zachowaj komentarze `#:` z referencjami plik:linia (dla kontekstu tłumacza przy przyszłych zmianach)
|
||||||
|
5. Sanity check per wpis:
|
||||||
|
- msgid niepuste → msgstr niepuste
|
||||||
|
- `%TOKEN%` w msgid → `%TOKEN%` w msgstr (ten sam token)
|
||||||
|
- `<strong>` w msgid → `<strong>` w msgstr (dokładnie ten sam tag)
|
||||||
|
|
||||||
|
Unikaj:
|
||||||
|
- Dosłownego tłumaczenia słowo-w-słowo (kluczowe są frazy rental, nie słownikowe mapowania)
|
||||||
|
- Zachowywania polskich diakrytyków w EN (ąćęłńóśźż nie powinny być w msgstr)
|
||||||
|
- Tłumaczenia nazw własnych (`SOFT`, `PREMIUM`, `Carei`, `Softra`)
|
||||||
|
- Zmiany placeholderów (`%count%` → `%iloscDni%` = katastrofa w runtime)
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
1. Plik `carei-reservation-en_US.po` istnieje
|
||||||
|
2. `grep -c '^msgid "[^"]' plik.po` ≈ 158 (157 + header)
|
||||||
|
3. `grep -c '^msgstr "[^"]' plik.po` ≈ 158 (wszystkie wypełnione)
|
||||||
|
4. `grep -cP "[ąćęłńóśźż]" plik.po` → tylko w msgid (oryginalne PL), zero w msgstr
|
||||||
|
5. Spot-check 10 wpisów: ton angielski branżowy, placeholdery zachowane, HTML zachowany
|
||||||
|
</verify>
|
||||||
|
<done>AC-1 satysfakcjonowane.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Kompilacja .po → .mo (+ kopia en_GB)</name>
|
||||||
|
<files>
|
||||||
|
wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.mo,
|
||||||
|
wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.po,
|
||||||
|
wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.mo
|
||||||
|
</files>
|
||||||
|
<action>
|
||||||
|
**Problem:** `msgfmt` (standardowy kompilator gettext) nie jest dostępny w systemie developera. Python `msgfmt.py` również nie dostępny.
|
||||||
|
|
||||||
|
**Rozwiązanie:** Wygeneruj plik `.mo` binarnie programistycznie. Możliwe ścieżki (wybierz dostępną):
|
||||||
|
|
||||||
|
1. **Preferowana:** Napisz tymczasowy skrypt Node.js w sandbox używający pakietu `gettext-parser` (dostępny przez npm):
|
||||||
|
```js
|
||||||
|
// Wymaga: npm i gettext-parser
|
||||||
|
const fs = require('fs');
|
||||||
|
const gp = require('gettext-parser');
|
||||||
|
const po = fs.readFileSync('carei-reservation-en_US.po');
|
||||||
|
const parsed = gp.po.parse(po);
|
||||||
|
const mo = gp.mo.compile(parsed);
|
||||||
|
fs.writeFileSync('carei-reservation-en_US.mo', mo);
|
||||||
|
```
|
||||||
|
2. **Alternatywa PHP:** Napisz własny skrypt PHP implementujący format `.mo` (magic 0x950412de, header 7×uint32, offset tables, string data). Format jest w dokumentacji gettext: https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
|
||||||
|
- Parsuj `.po` ręcznie (rozpoznaj msgid/msgstr, unescape)
|
||||||
|
- Zbuduj binarny layout: header → offset/length tables for originals → offset/length tables for translations → string blobs (null-terminated)
|
||||||
|
- Zapisz jako binary file
|
||||||
|
3. **Loco Translate (wp-admin):** gdyby powyższe zawiodły — upload `.po` do wp-content/languages/plugins/ i niech Loco Translate w `wp-admin → Loco Translate → Plugins → Carei Reservation → en_US → Sync → Save` wygeneruje `.mo` w server-side.
|
||||||
|
|
||||||
|
**Preferuj opcję 1** (Node.js + gettext-parser) — działa deterministycznie w dev environment, bez potrzeby wp-admin.
|
||||||
|
|
||||||
|
**Po skompilowaniu en_US:**
|
||||||
|
- Skopiuj `.po` jako `carei-reservation-en_GB.po` (zmień `Language: en_US\n` → `Language: en_GB\n` w nagłówku)
|
||||||
|
- Opcjonalnie: drobne poprawki UK-english (license→licence, color→colour, itp.) — na tym etapie zostaw identyczne
|
||||||
|
- Skompiluj `.po` → `.mo` tym samym skryptem
|
||||||
|
|
||||||
|
**Weryfikacja binarnej poprawności `.mo`:**
|
||||||
|
```bash
|
||||||
|
# Pierwsze 4 bajty = magic number (little-endian 0x950412de)
|
||||||
|
xxd -l 4 carei-reservation-en_US.mo
|
||||||
|
# Powinno pokazać: 0000000: de12 0495 (le) lub 9504 12de (be)
|
||||||
|
```
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
1. Pliki istnieją: `en_US.po`, `en_US.mo`, `en_GB.po`, `en_GB.mo` w `languages/`
|
||||||
|
2. Rozmiar `.mo` między 5–50 KB
|
||||||
|
3. Magic number poprawny (xxd/hexdump pierwsze 4 bajty)
|
||||||
|
4. PHP sanity check (opcjonalny): `php -r "$mo=file_get_contents('path.mo'); echo bin2hex(substr($mo,0,4));"` → `de120495` lub `950412de`
|
||||||
|
</verify>
|
||||||
|
<done>AC-2 satysfakcjonowane.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="checkpoint:human-verify" gate="blocking">
|
||||||
|
<what-built>
|
||||||
|
- `carei-reservation-en_US.po` + `.mo` z 157 przetłumaczonymi wpisami
|
||||||
|
- `carei-reservation-en_GB.po` + `.mo` jako kopia en_US
|
||||||
|
- Po uploadzie wszystkie `__()`/`esc_html__()` + `careiI18n` w pluginie zwracają EN przy locale en_*
|
||||||
|
</what-built>
|
||||||
|
<how-to-verify>
|
||||||
|
1. **Deploy:** wypchnij 4 nowe pliki (`carei-reservation-en_US.po`, `.mo`, `en_GB.po`, `.mo`) do `wp-content/plugins/carei-reservation/languages/` na serwer
|
||||||
|
2. **Cache:** wyczyść cache pluginu (jeśli masz WP Rocket / Autoptimize) + opcache PHP (zwykle wystarczy reboot fpm, albo `wp cache flush`)
|
||||||
|
3. **Admin panel pakietów:**
|
||||||
|
- Zaloguj się do wp-admin (jeśli twoja konto admina jest PL → zostawcie PL dla admina, albo zmień WP user locale na EN w Users → Profile → Language: English (United States))
|
||||||
|
- `Rezerwacje → Pakiety ochronne` — labele powinny być po EN (jeśli admin locale = EN)
|
||||||
|
- Uzupełnij pola EN dla SOFT i PREMIUM (np. `SOFT Protection` + `Basic damage coverage with 2000 PLN deductible`; `PREMIUM Protection` + `Full damage waiver, zero deductible`)
|
||||||
|
- Zapisz
|
||||||
|
4. **Frontend EN — modal rezerwacji:**
|
||||||
|
- Przełącz Polylang switcher na EN
|
||||||
|
- Otwórz stronę z przyciskiem → przycisk pokazuje `Request a reservation` (lub podobne)
|
||||||
|
- Modal otwiera się, WSZYSTKIE labele po angielsku: `Vehicle segment`, `From`, `To`, `Pickup location`, `Return location`, `Protection packages`, `Additional options`, `International travel`, `First name`, `Last name`, `Email`, `Phone`, `I agree to the privacy policy`, `Send request`
|
||||||
|
- Pakiety ochronne: `SOFT Protection` / `PREMIUM Protection` z angielskim opisem (z Phase 17)
|
||||||
|
- Komunikaty walidacji (spróbuj wysłać pusty form) po EN: `Enter first name`, `Enter a valid email`, itp.
|
||||||
|
- Podsumowanie → `Reservation summary`, `Subtotal`, `VAT`, `Total`, `Confirm reservation`
|
||||||
|
- Success: `Reservation confirmed`, `Order number: X`
|
||||||
|
5. **Hero search form:** `Vehicle segment`, `From`, `To`, `Pickup location`, `Check availability`
|
||||||
|
6. **Widgety:**
|
||||||
|
- Mapa Polski: tooltipy `Location: {city}`, `ul. {street}` → po EN
|
||||||
|
- Grid miast, grid oddziałów → po EN
|
||||||
|
7. **Błędy Softra:**
|
||||||
|
- Spróbuj zarezerwować niedostępny pojazd / nieprawidłową datę — komunikat po EN (z słownika Phase 17 via `__()`)
|
||||||
|
8. **DevTools sanity:**
|
||||||
|
- `window.careiI18n.selectSegment` → `"Select vehicle segment"` (lub podobne)
|
||||||
|
- `window.careiI18n.dayOne` → `"day"`, `dayOther` → `"days"` (jeśli dodałeś `dayOther` jako en-plural)
|
||||||
|
- Network: `/wp-json/carei/v1/protection-packages?lang=en` → response z EN treścią
|
||||||
|
9. **Powrót do PL:**
|
||||||
|
- Przełącz Polylang na PL
|
||||||
|
- Wszystko wraca do polskiego bez regresji (oryginał zachowany)
|
||||||
|
|
||||||
|
**Kryterium przejścia:** cała UI pluginu w EN (modal + hero + widgety + admin + error messages). Zero polskich literałów przy locale EN (poza treściami z DB które admin zostawił puste → fallback PL z Phase 17).
|
||||||
|
|
||||||
|
**Znane limitacje (NIE blokują approve):**
|
||||||
|
- Treści stron Elementora (nagłówki hero, sekcje marketingowe) — tłumaczone osobno przez Polylang addon (poza scope tej fazy)
|
||||||
|
- Menu, footer, inne treści WP — Polylang / Polylang Strings Translation (poza scope)
|
||||||
|
</how-to-verify>
|
||||||
|
<resume-signal>Napisz "approved" aby zamknąć Milestone v0.7, albo wskaż stringi które pozostały po polsku pomimo EN locale.</resume-signal>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<boundaries>
|
||||||
|
|
||||||
|
## DO NOT CHANGE
|
||||||
|
- Żaden plik PHP/JS pluginu (Phase 16–17 już to załatwiły)
|
||||||
|
- Plik `.pot` (baseline — nie nadpisujemy bazowego template)
|
||||||
|
- Textdomain, mechanizm `load_plugin_textdomain` (Phase 16)
|
||||||
|
- Panel admina pakietów — tylko DANE w bazie mogą być wypełnione przez admina (to nie zmiana kodu)
|
||||||
|
- `carei-reservation.css` — styling niezależny od języka
|
||||||
|
- `mu-plugins/fix-sprintf-global.php` — dalej potrzebny dla Polylang addon
|
||||||
|
|
||||||
|
## SCOPE LIMITS
|
||||||
|
- Nie tłumaczymy innych pluginów (tylko carei-reservation)
|
||||||
|
- Nie tłumaczymy Elementora ani treści stron (Polylang addon)
|
||||||
|
- Nie tłumaczymy nazw krajów w sekcji wyjazdu zagranicznego (dane biznesowe COUNTRY_FLAGS — pozostają po polsku bo backend tak zwraca)
|
||||||
|
- Nie tłumaczymy slugów URL, permalink structures
|
||||||
|
- Nie generujemy dodatkowych locale (fr, de, itp.) — tylko en_US + en_GB
|
||||||
|
- Nie zmieniamy generatora .pot ani pipeline-u i18n
|
||||||
|
</boundaries>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Przed zamknięciem planu:
|
||||||
|
- [ ] `carei-reservation-en_US.po` istnieje, 157 wpisów, msgstr wszystkie wypełnione
|
||||||
|
- [ ] `carei-reservation-en_US.mo` istnieje, magic number poprawny
|
||||||
|
- [ ] `carei-reservation-en_GB.po` + `.mo` — kopia en_US
|
||||||
|
- [ ] Brak polskich diakrytyków w msgstr
|
||||||
|
- [ ] Placeholdery `%TOKEN%` zachowane 1:1 między msgid i msgstr
|
||||||
|
- [ ] HTML tagi zachowane w msgstr
|
||||||
|
- [ ] Human-verify — pełen flow EN bez regresji PL
|
||||||
|
- [ ] AC-1, AC-2, AC-3 pass
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- Task 1–2 ukończone
|
||||||
|
- Checkpoint zatwierdzony
|
||||||
|
- Milestone v0.7 — 100% complete
|
||||||
|
- Plugin carei-reservation pełnoprawnie dwujęzyczny (PL + EN)
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
Po zakończeniu: `.paul/phases/18-en-translation/18-01-SUMMARY.md`
|
||||||
|
Następnie: `/paul:complete-milestone v0.7`
|
||||||
|
</output>
|
||||||
168
.paul/phases/18-en-translation/18-01-SUMMARY.md
Normal file
168
.paul/phases/18-en-translation/18-01-SUMMARY.md
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
---
|
||||||
|
phase: 18-en-translation
|
||||||
|
plan: 01
|
||||||
|
subsystem: i18n
|
||||||
|
tags: [gettext, po, mo, translation, flatpickr, bilingual]
|
||||||
|
|
||||||
|
requires:
|
||||||
|
- phase: 16-i18n-plugin-refactor
|
||||||
|
provides: .pot (157 msgid) + textdomain carei-reservation
|
||||||
|
- phase: 17-bilingual-packages-and-softra-errors
|
||||||
|
provides: bilingual pakiety + mapowanie Softra przez __()
|
||||||
|
|
||||||
|
provides:
|
||||||
|
- carei-reservation-en_US.po + .mo (158 wpisów przetłumaczonych)
|
||||||
|
- carei-reservation-en_GB.po + .mo (kopia en_US)
|
||||||
|
- PHP mini-kompilator po2mo.php (bez potrzeby msgfmt)
|
||||||
|
- Flatpickr jako cross-browser date picker z PL/EN locale (scope addition)
|
||||||
|
- Kompaktowy CSS theme dla flatpickr w kolorach Carei
|
||||||
|
|
||||||
|
affects: []
|
||||||
|
|
||||||
|
tech-stack:
|
||||||
|
added:
|
||||||
|
- Flatpickr 4.6.13 (CDN jsdelivr, enqueued z dependencies)
|
||||||
|
patterns:
|
||||||
|
- "MO compilation: własny parser+packer PHP bez msgfmt/Python"
|
||||||
|
- "Flatpickr static:true dla modala (popup w container) + default dla hero"
|
||||||
|
- "disableMobile:true dla spójności UX PL/EN na wszystkich urządzeniach"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.po
|
||||||
|
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_US.mo
|
||||||
|
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.po
|
||||||
|
- wp-content/plugins/carei-reservation/languages/carei-reservation-en_GB.mo
|
||||||
|
modified:
|
||||||
|
- wp-content/plugins/carei-reservation/carei-reservation.php
|
||||||
|
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||||
|
- wp-content/plugins/carei-reservation/assets/css/carei-reservation.css
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Tłumaczenie przez agenta z pre-zdefiniowanym słownikiem terminów branżowych (~50 mapowań PL→EN car rental)"
|
||||||
|
- "Własny kompilator PHP zamiast msgfmt/Loco — deterministyczny, niezależny od środowiska"
|
||||||
|
- "Flatpickr z CDN jsdelivr — brak lokalnych plików, szybki deploy; fallback do native picker jeśli CDN padnie"
|
||||||
|
- "static:true dla modala — rozwiązuje konflikt focus-trap + z-index"
|
||||||
|
- "disableMobile:true — jednolity UX zamiast native iOS spinner / Android Material"
|
||||||
|
- "en_GB = kopia en_US — brak realnej potrzeby różnicowania UK/US na tym etapie"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Własny po2mo compiler w PHP (stored w ~/temp) — reusable dla przyszłych tłumaczeń"
|
||||||
|
- "CSS override per-klasa flatpickr (compact height/width/spacing) w kolorach Carei #2F2482"
|
||||||
|
|
||||||
|
duration: ~40min
|
||||||
|
started: 2026-04-22
|
||||||
|
completed: 2026-04-22
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 18 Plan 01: EN translation (.po/.mo) + QA — Summary
|
||||||
|
|
||||||
|
**Plugin carei-reservation dostarczony w wersji dwujęzycznej: 158 wpisów przetłumaczonych na EN, skompilowanych do .mo (en_US + en_GB), plus cross-browser Flatpickr jako date picker z locale PL/EN. Milestone v0.7 — 100% complete.**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| Duration | ~40min |
|
||||||
|
| Tasks | 2 auto + 1 human-verify + 1 scope addition (Flatpickr) |
|
||||||
|
| Files created | 4 (.po + .mo × 2 locale) |
|
||||||
|
| Files modified | 3 (bootstrap PHP, JS, CSS) |
|
||||||
|
| Delegation | 1 agent (tłumaczenie .pot → .po) |
|
||||||
|
|
||||||
|
## Acceptance Criteria Results
|
||||||
|
|
||||||
|
| Criterion | Status | Notes |
|
||||||
|
|-----------|--------|-------|
|
||||||
|
| AC-1: Plik .po kompletnie przetłumaczony | Pass | 158 msgstr, placeholdery zachowane, HTML OK, zero PL diakrytyków |
|
||||||
|
| AC-2: Plik .mo poprawny binarnie | Pass | Magic 0x950412de, version 0, N=158, 9455 bytes |
|
||||||
|
| AC-3: EN UI działa po uploadzie | Pass | User confirmed "approved" po pełnym teście |
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- **158 wpisów PL→EN** przez agenta z uzgodnionym słownikiem terminów rental (`doba→day`, `oddział→location`, `zł→PLN`, `pakiet ochronny→protection package`, etc.)
|
||||||
|
- **Własny PHP `.mo` compiler** (po2mo.php, ~150 linii) — parsuje .po, sortuje, pakuje binarnie wg gettext spec
|
||||||
|
- **en_US + en_GB** — dwa locale EN pokryte (Polylang może używać dowolnego)
|
||||||
|
- **Flatpickr scope addition:** CDN enqueue + JS init + kompaktowy CSS w kolorach Carei. Modal z `static:true` (popup w container, bypass focus-trap). Hero z default renderowaniem. Jednolity UX desktop + mobile (`disableMobile:true`).
|
||||||
|
- **Weryfikacja binarna `.mo`** przez PHP: `Magic: 0x950412de, Version: 0, N: 158`
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
| File | Change | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `languages/carei-reservation-en_US.po` | Created | 158 wpisów PL→EN |
|
||||||
|
| `languages/carei-reservation-en_US.mo` | Created | Binarka gettext, 9455 bytes |
|
||||||
|
| `languages/carei-reservation-en_GB.po` | Created | Kopia en_US z `Language: en_GB\n` |
|
||||||
|
| `languages/carei-reservation-en_GB.mo` | Created | Skompilowana en_GB |
|
||||||
|
| `carei-reservation.php` | Modified | Flatpickr enqueue (CDN + pl locale) + deps |
|
||||||
|
| `assets/js/carei-reservation.js` | Modified | `initDatePickers()` + static:true dla modal + graceful fallback |
|
||||||
|
| `assets/css/carei-reservation.css` | Modified | Kompaktowy flatpickr theme w kolorach Carei |
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
| Decision | Rationale | Impact |
|
||||||
|
|----------|-----------|--------|
|
||||||
|
| Własny po2mo.php zamiast msgfmt | msgfmt/Python msgfmt niedostępne w dev env; Loco Translate wymaga wp-admin flow | Deterministic compilation, reusable dla przyszłych locale (fr, de) |
|
||||||
|
| Flatpickr scope addition | User zgłosił potrzebę tłumaczenia kalendarza natywnego (browser używa OS locale) — flatpickr jedyne sensowne rozwiązanie | +40KB JS (CDN), ale spójne UX + locale-aware |
|
||||||
|
| CDN jsdelivr | Szybki deploy, brak zarządzania plikami lokalnymi | Dependency na CDN; graceful fallback do native picker |
|
||||||
|
| `static:true` dla modal | Default popup w body → konflikt z focus-trap + z-index modala | Picker w containerze inputa — kompatybilne z modalem |
|
||||||
|
| `disableMobile:true` | Native mobile (iOS spinner, Android Material) ignoruje strony locale | Jednolity UX PL/EN niezależnie od OS użytkownika |
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
| Type | Count | Impact |
|
||||||
|
|------|-------|--------|
|
||||||
|
| Auto-fixed | 2 | Flatpickr popup ukryty w modalu (static:true), native mobile picker (disableMobile:true) |
|
||||||
|
| Scope additions | 1 | Flatpickr integracja (cross-browser date picker z i18n) — user request podczas apply |
|
||||||
|
| Deferred | 0 | — |
|
||||||
|
|
||||||
|
### Scope additions
|
||||||
|
|
||||||
|
**1. [UX] Flatpickr date picker z i18n**
|
||||||
|
- Found during: Task 3 (human-verify) — user zapytał o tłumaczenie kalendarza
|
||||||
|
- Problem: Natywny `<input type="datetime-local">` używa locale OS przeglądarki, ignoruje WP locale
|
||||||
|
- Fix: Integracja Flatpickr 4.6.13 z CDN — enqueue + init per input + kompaktowy CSS theme
|
||||||
|
- Files: carei-reservation.php, carei-reservation.js, carei-reservation.css
|
||||||
|
- Verification: User "jest ok" po kompaktowym themie; PL kalendarz, EN kalendarz, oba na desktop + mobile
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. [Modal] Flatpickr popup nie otwierał się w modalu**
|
||||||
|
- Issue: Default append do body + focus-trap z Phase 4 → picker focus tracony przed interakcją
|
||||||
|
- Fix: `static: true` w opts — popup renderowany wewnątrz `.carei-form__date-wrap`
|
||||||
|
- Verification: User confirmed kalendarz otwiera się w modalu po zmianie
|
||||||
|
|
||||||
|
**2. [Mobile] Inny kalendarz na mobile vs desktop**
|
||||||
|
- Issue: Flatpickr default `disableMobile: false` → na mobile fallback do native OS picker (iOS spinner / Android Material)
|
||||||
|
- Fix: `disableMobile: true` — flatpickr wszędzie, jednolity wygląd + locale
|
||||||
|
- Verification: User confirmed "na obu jest flatpickr"
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
| Issue | Resolution |
|
||||||
|
|-------|------------|
|
||||||
|
| msgfmt niedostępny lokalnie | Własny PHP kompilator (po2mo.php) — reusable |
|
||||||
|
| Modal calendar nie otwiera | static:true flatpickr option |
|
||||||
|
| Mobile inny picker | disableMobile:true |
|
||||||
|
| Hero kalendarz po polsku mimo EN | Dodanie hero inputs do initDatePickers() (wcześniej tylko modal) |
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
**Milestone v0.7 COMPLETE** — plugin carei-reservation pełnoprawnie dwujęzyczny (PL + EN):
|
||||||
|
- Infrastruktura: textdomain + __() + careiI18n (Phase 16)
|
||||||
|
- Bilingual dane: pakiety pól _en + mapowanie Softra errors (Phase 17)
|
||||||
|
- Tłumaczenia: .po/.mo dla en_US + en_GB (Phase 18)
|
||||||
|
- UX: Flatpickr cross-browser + locale-aware (Phase 18 scope addition)
|
||||||
|
|
||||||
|
**Out of scope dla kolejnych milestones:**
|
||||||
|
- Tłumaczenie treści stron Elementora (Polylang Automatic Translate Addon)
|
||||||
|
- Menu, footer, theme stringi (Polylang String Translation)
|
||||||
|
- Nazwy miast/krajów (dane biznesowe z API Softra)
|
||||||
|
- Inne locale (fr, de) — dodaje się przez sam `.po/.mo` bez zmian w kodzie
|
||||||
|
|
||||||
|
**Blockers:** None.
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 18-en-translation, Plan: 01*
|
||||||
|
*Completed: 2026-04-22*
|
||||||
303
.paul/phases/19-extras-translations-admin/19-01-PLAN.md
Normal file
303
.paul/phases/19-extras-translations-admin/19-01-PLAN.md
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
---
|
||||||
|
phase: 19-extras-translations-admin
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: ["17-01"]
|
||||||
|
files_modified:
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||||
|
autonomous: false
|
||||||
|
delegation: off
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
## Goal
|
||||||
|
Panel administratora do tłumaczenia nazw dodatkowych opcji (extras) zwracanych dynamicznie z API Softra. Plugin auto-zbiera wszystkie napotkane polskie nazwy extras do opcji WP (`carei_extras_seen`). W panelu `Rezerwacje → Tłumaczenia extras` admin widzi listę zebranych nazw PL i obok pole EN do wypełnienia. Gdy frontend pobiera pricelist w locale EN, REST endpoint automatycznie podmienia PL na EN override'y (fallback: PL gdy brak tłumaczenia).
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Phase 18 przetłumaczyła statyczne stringi pluginu. Phase 17 zrobiła słownik błędów Softra (13 ręcznych mapowań). Ale dynamiczne pozycje z `/pricelist/list` (fotelik, GPS, dodatkowy kierowca, łańcuchy śniegowe itp.) są nadal po polsku w wersji EN — bo backend Softra zawsze odpowiada po polsku. Phase 19 zamyka tę lukę: admin ma UI gdzie raz wpisze tłumaczenia, plugin używa ich automatycznie.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
- Option `carei_extras_seen` (array of unique PL names, updated runtime przy każdym pricelist request)
|
||||||
|
- Option `carei_extras_translations` (map PL → EN, managed przez admin)
|
||||||
|
- Submenu `wp-admin → Rezerwacje → Tłumaczenia extras` z formularzem: lista seen names + input EN + submit
|
||||||
|
- Helper `Carei_Admin_Panel::translate_extra_name($pl_name, $locale)` — wg locale + override'u, z fallbackiem do PL
|
||||||
|
- `Carei_REST_Proxy` przy zwracaniu pricelist: dla każdego `$item['name']` → dopisuje do seen + podmienia na EN jeśli locale = en
|
||||||
|
- Frontend bez zmian (JS już ma `?lang=` w requeście)
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.paul/PROJECT.md
|
||||||
|
@.paul/ROADMAP.md
|
||||||
|
@.paul/STATE.md
|
||||||
|
@.paul/phases/17-bilingual-packages-and-softra-errors/17-01-SUMMARY.md
|
||||||
|
@.paul/phases/18-en-translation/18-01-SUMMARY.md
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||||
|
@wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<acceptance_criteria>
|
||||||
|
|
||||||
|
## AC-1: Auto-collect PL names
|
||||||
|
```gherkin
|
||||||
|
Given frontend woła `/wp-json/carei/v1/pricelist` (PL lub EN)
|
||||||
|
When Softra API zwraca `additionalItems` z polskimi nazwami
|
||||||
|
Then każda unikalna nazwa jest dopisana do opcji `carei_extras_seen`
|
||||||
|
And opcja jest arrayem stringów (deduplikowana)
|
||||||
|
And zapis do `carei_extras_seen` NIE blokuje response (fire-and-forget lub szybki update_option)
|
||||||
|
And kolejne żądania nie duplikują wpisów
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-2: Panel admin zarządza tłumaczeniami
|
||||||
|
```gherkin
|
||||||
|
Given admin wchodzi w `wp-admin → Rezerwacje → Tłumaczenia extras`
|
||||||
|
When widzi listę zebranych PL nazw
|
||||||
|
Then dla każdej nazwy jest pole `text` z obecnym tłumaczeniem EN (lub puste)
|
||||||
|
And istnieje przycisk `Zapisz tłumaczenia`
|
||||||
|
And nagłówek strony i etykiety są po EN/PL zgodnie z admin locale (przez __())
|
||||||
|
|
||||||
|
When admin wpisuje tłumaczenia i klika Zapisz
|
||||||
|
Then opcja `carei_extras_translations` zostaje zaktualizowana (sanitize_text_field na każdym value)
|
||||||
|
And redirect z flagą `?carei_saved=1` pokazuje komunikat "Zapisano."
|
||||||
|
And przy następnej wizycie panelu tłumaczenia są wyświetlone zgodnie z zapisem
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-3: REST pricelist zwraca EN nazwy gdy są override
|
||||||
|
```gherkin
|
||||||
|
Given w `carei_extras_translations` są zapisane tłumaczenia np. "Fotelik dziecięcy" → "Child car seat"
|
||||||
|
When frontend EN (Polylang = EN albo `?lang=en`) woła `/pricelist`
|
||||||
|
Then response zawiera `additionalItems` z `name` podmienionym wg override
|
||||||
|
And dla nazw bez override response zawiera oryginalny PL (graceful fallback)
|
||||||
|
And inne pola pozycji (`price`, `code`, `maxPrice` itp.) nietknięte
|
||||||
|
|
||||||
|
When frontend PL woła ten sam endpoint
|
||||||
|
Then response zawiera oryginalne PL nazwy (bez zmian względem obecnego zachowania — zero regresji)
|
||||||
|
```
|
||||||
|
|
||||||
|
</acceptance_criteria>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Admin panel submenu + helper + zapisy w options</name>
|
||||||
|
<files>wp-content/plugins/carei-reservation/includes/class-admin-panel.php</files>
|
||||||
|
<action>
|
||||||
|
1. Dodaj stałe klasy:
|
||||||
|
```php
|
||||||
|
const EXTRAS_SEEN_OPTION = 'carei_extras_seen';
|
||||||
|
const EXTRAS_TRANSLATIONS_OPTION = 'carei_extras_translations';
|
||||||
|
```
|
||||||
|
2. Dodaj static helpery:
|
||||||
|
```php
|
||||||
|
public static function get_extras_seen() {
|
||||||
|
$seen = get_option( self::EXTRAS_SEEN_OPTION, array() );
|
||||||
|
return is_array( $seen ) ? array_values( array_unique( array_filter( array_map( 'strval', $seen ) ) ) ) : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_extras_translations() {
|
||||||
|
$map = get_option( self::EXTRAS_TRANSLATIONS_OPTION, array() );
|
||||||
|
return is_array( $map ) ? $map : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function remember_extra_name( $pl_name ) {
|
||||||
|
$pl_name = trim( (string) $pl_name );
|
||||||
|
if ( $pl_name === '' ) return;
|
||||||
|
$seen = self::get_extras_seen();
|
||||||
|
if ( ! in_array( $pl_name, $seen, true ) ) {
|
||||||
|
$seen[] = $pl_name;
|
||||||
|
sort( $seen );
|
||||||
|
update_option( self::EXTRAS_SEEN_OPTION, $seen, false ); // autoload=false (może być duża lista)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function translate_extra_name( $pl_name, $locale = null ) {
|
||||||
|
if ( $locale === null ) {
|
||||||
|
$locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
|
||||||
|
$locale = ( 0 === strpos( (string) $locale, 'en' ) ) ? 'en' : 'pl';
|
||||||
|
}
|
||||||
|
if ( $locale !== 'en' ) return $pl_name;
|
||||||
|
$map = self::get_extras_translations();
|
||||||
|
if ( isset( $map[ $pl_name ] ) && $map[ $pl_name ] !== '' ) {
|
||||||
|
return $map[ $pl_name ];
|
||||||
|
}
|
||||||
|
return $pl_name; // fallback
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. Zarejestruj submenu w `register_protection_packages_page()` lub osobną metodą `register_extras_translations_page()` (prefer osobną dla czystości):
|
||||||
|
```php
|
||||||
|
add_action( 'admin_menu', array( $this, 'register_extras_translations_page' ) );
|
||||||
|
add_action( 'admin_post_carei_save_extras_translations', array( $this, 'handle_extras_translations_save' ) );
|
||||||
|
```
|
||||||
|
I w konstruktorze dopisz akcje.
|
||||||
|
4. Metoda `register_extras_translations_page()`:
|
||||||
|
```php
|
||||||
|
public function register_extras_translations_page() {
|
||||||
|
add_submenu_page(
|
||||||
|
'edit.php?post_type=' . self::POST_TYPE,
|
||||||
|
__( 'Tłumaczenia extras', 'carei-reservation' ),
|
||||||
|
__( 'Tłumaczenia extras', 'carei-reservation' ),
|
||||||
|
'manage_options',
|
||||||
|
'carei-extras-translations',
|
||||||
|
array( $this, 'render_extras_translations_page' )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
5. Metoda `render_extras_translations_page()`:
|
||||||
|
- Tytuł strony, komunikat `?carei_saved=1`
|
||||||
|
- Formularz POST do `admin-post.php` z nonce
|
||||||
|
- Tabela 2 kolumny: `Nazwa PL` (readonly text), `Nazwa EN` (input text)
|
||||||
|
- Dla każdej nazwy z `get_extras_seen()` — wiersz z inputem `name="translations[PL_NAME]"` i obecnym value
|
||||||
|
- Submit button `Zapisz tłumaczenia`
|
||||||
|
- Info tekst: "Puste pole = fallback do wersji polskiej w wersji EN strony."
|
||||||
|
- Jeśli `get_extras_seen()` pusty: info "Brak zebranych pozycji — otwórz formularz rezerwacji aby załadować pricelist."
|
||||||
|
6. Metoda `handle_extras_translations_save()`:
|
||||||
|
- Check nonce + capability
|
||||||
|
- Iteracja po `$_POST['translations']` (array) — każdy klucz PL, value EN
|
||||||
|
- Sanitize: `sanitize_text_field( wp_unslash( $value ) )`
|
||||||
|
- Zbuduj clean map
|
||||||
|
- `update_option( self::EXTRAS_TRANSLATIONS_OPTION, $clean, false )`
|
||||||
|
- Redirect z `?carei_saved=1`
|
||||||
|
7. Wszystkie user-facing stringi przez `__()`/`esc_html__()`/`esc_attr__()` z textdomain `carei-reservation`.
|
||||||
|
|
||||||
|
Unikaj:
|
||||||
|
- Blokowania request frontendu na zapis seen — `update_option` z autoload=false jest szybki, ale jeśli zaburza latency — użyj `wp_cache_set` przed `update_option` (mikro-optymalizacja, na razie pomiń)
|
||||||
|
- Duplikowania nazw (array_unique)
|
||||||
|
- Auto-tłumaczenia przez AI (scope tego planu: tylko admin override)
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
1. `wp-admin → Rezerwacje → Tłumaczenia extras` — strona się renderuje, widać info "Brak zebranych pozycji" (bo option jeszcze pusta)
|
||||||
|
2. `php -l` class-admin-panel.php → No syntax errors
|
||||||
|
3. Wywołanie helpera: `var_dump( Carei_Admin_Panel::translate_extra_name( 'Test', 'en' ) )` → `'Test'` (fallback)
|
||||||
|
</verify>
|
||||||
|
<done>AC-2 (częściowo — render panelu), infrastruktura dla AC-1 i AC-3.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: REST /pricelist auto-collect + per-locale name replacement</name>
|
||||||
|
<files>wp-content/plugins/carei-reservation/includes/class-rest-proxy.php</files>
|
||||||
|
<action>
|
||||||
|
1. Znajdź handler endpointu `/pricelist` (prawdopodobnie `get_pricelist()` lub podobna metoda) w class-rest-proxy.php.
|
||||||
|
2. Przed zwrotem response — iteracja po `additionalItems` (lub odpowiedniku):
|
||||||
|
```php
|
||||||
|
$locale = $this->resolve_locale( $request ); // helper z Phase 17
|
||||||
|
$translations = Carei_Admin_Panel::get_extras_translations();
|
||||||
|
foreach ( $pricelists as &$pricelist ) {
|
||||||
|
if ( ! isset( $pricelist['additionalItems'] ) || ! is_array( $pricelist['additionalItems'] ) ) continue;
|
||||||
|
foreach ( $pricelist['additionalItems'] as &$item ) {
|
||||||
|
if ( ! isset( $item['name'] ) || ! is_string( $item['name'] ) ) continue;
|
||||||
|
$pl_name = trim( $item['name'] );
|
||||||
|
if ( $pl_name === '' ) continue;
|
||||||
|
|
||||||
|
// Auto-collect
|
||||||
|
Carei_Admin_Panel::remember_extra_name( $pl_name );
|
||||||
|
|
||||||
|
// Translate if locale=en and override exists
|
||||||
|
if ( $locale === 'en' && isset( $translations[ $pl_name ] ) && $translations[ $pl_name ] !== '' ) {
|
||||||
|
$item['name'] = $translations[ $pl_name ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset( $item );
|
||||||
|
}
|
||||||
|
unset( $pricelist );
|
||||||
|
```
|
||||||
|
3. Upewnij się że resolve_locale działa tutaj (jest publiczny lub private dostępny w tej samej klasie — Phase 17 dodał jako private, więc OK bo to ta sama klasa).
|
||||||
|
4. Struktura response NIE zmienia się — frontend JS dostaje te same klucze, tylko `name` może być podmienione.
|
||||||
|
5. Jeśli Softra zwraca `error` zamiast success — nie ruszamy, passthrough.
|
||||||
|
|
||||||
|
Unikaj:
|
||||||
|
- Modyfikacji cached response (jeśli pricelist jest cached — lepiej żeby cache też uwzględniał locale, ale na razie zostaw cache PL a mapowanie w locie; jeśli `/pricelist` NIE ma cache po stronie PHP, to proste)
|
||||||
|
- Dodawania `name_en` w response — zachowujemy schemat (tylko `name` rozwiązane per locale)
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
1. `php -l` class-rest-proxy.php → No syntax errors
|
||||||
|
2. Wywołanie z fronta w PL: pricelist names po polsku (bez zmian)
|
||||||
|
3. Wywołanie z fronta w EN: nazwy spotkane po raz pierwszy → zapisane w `carei_extras_seen`, nadal po polsku w response (bo brak tłumaczeń)
|
||||||
|
4. Po wypełnieniu tłumaczeń w adminie i kolejnym requeście EN: nazwy z override podmienione
|
||||||
|
</verify>
|
||||||
|
<done>AC-1 i AC-3 satysfakcjonowane.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="checkpoint:human-verify" gate="blocking">
|
||||||
|
<what-built>
|
||||||
|
- Submenu `wp-admin → Rezerwacje → Tłumaczenia extras` z listą zebranych nazw + pola EN
|
||||||
|
- Helpery w Admin_Panel: `remember_extra_name()`, `get_extras_seen()`, `get_extras_translations()`, `translate_extra_name()`
|
||||||
|
- Auto-collect: każdy request `/pricelist` zbiera unikalne nazwy PL do option `carei_extras_seen`
|
||||||
|
- Per-locale replacement: EN requesty dostają override z `carei_extras_translations` (fallback: PL)
|
||||||
|
- Zero zmian w JS frontendu (już jest `?lang=` z Phase 17)
|
||||||
|
</what-built>
|
||||||
|
<how-to-verify>
|
||||||
|
1. Deploy 2 pliki PHP (`class-admin-panel.php`, `class-rest-proxy.php`) przez SFTP
|
||||||
|
2. **Auto-collect test:**
|
||||||
|
- Otwórz stronę z modalem rezerwacji w PL, wypełnij krok 1 (daty + oddział + klasa) — pricelist się załaduje
|
||||||
|
- Wejdź `wp-admin → Rezerwacje → Tłumaczenia extras` → lista PL nazw jest wypełniona (powinno być ~5–15 pozycji, zależnie od Softra)
|
||||||
|
3. **Admin zapis:**
|
||||||
|
- Wypełnij kilka tłumaczeń EN (np. "Fotelik dziecięcy" → "Child car seat", "GPS / Nawigacja" → "GPS / Navigation", "Dodatkowy kierowca" → "Additional driver")
|
||||||
|
- Kliknij "Zapisz tłumaczenia" → komunikat "Zapisano" pojawia się
|
||||||
|
- Odśwież stronę → tłumaczenia wciąż tam
|
||||||
|
4. **Frontend PL:**
|
||||||
|
- Modal w PL, sekcja "Opcje dodatkowe" — nazwy po polsku (identyczne jak przed zmianą)
|
||||||
|
5. **Frontend EN:**
|
||||||
|
- Polylang switcher → EN
|
||||||
|
- Modal otwiera się, "Additional options" → nazwy które wypełniłeś = EN, reszta = PL (fallback)
|
||||||
|
- `document.documentElement.lang` = "en-*" → `?lang=en` w requeście → backend zwraca EN names
|
||||||
|
6. **DevTools Network:**
|
||||||
|
- `GET /wp-json/carei/v1/pricelist?...&lang=en` → response zawiera `additionalItems[i].name` z EN (gdzie jest override) lub PL (gdzie brak)
|
||||||
|
- `GET ...&lang=pl` → wszystko po PL
|
||||||
|
7. **Test pustego override:**
|
||||||
|
- Wyczyść jedno pole EN w adminie, zapisz
|
||||||
|
- Frontend EN → ta pozycja wraca do PL (fallback)
|
||||||
|
8. **Zero regresji:**
|
||||||
|
- Pełen flow rezerwacji w PL działa bez zmian
|
||||||
|
- Panel pakietów ochronnych (Phase 13/17) bez zmian
|
||||||
|
- Inne widgety (mapa, miasta, oddziały) bez zmian
|
||||||
|
|
||||||
|
**Kryterium przejścia:** admin może raz wypełnić tłumaczenia → frontend EN automatycznie używa. Zero regresji w PL.
|
||||||
|
</how-to-verify>
|
||||||
|
<resume-signal>Napisz "approved" aby zamknąć plan, albo opisz problemy.</resume-signal>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<boundaries>
|
||||||
|
|
||||||
|
## DO NOT CHANGE
|
||||||
|
- Phase 17 struktura `Carei_REST_Proxy::resolve_locale()` (reuse, nie refactor)
|
||||||
|
- Phase 17 helper `Carei_Softra_API::map_error_message()` — osobny mechanizm dla błędów
|
||||||
|
- Phase 13 panel pakietów ochronnych — osobna strona w menu
|
||||||
|
- Schema CPT `carei_reservation`, meta keys, slugi statusów
|
||||||
|
- Struktura payloadów do Softra (klucze JSON `additionalItems`, `price`, `code` itp.) — tylko `name` per locale
|
||||||
|
- Frontend JS — bez zmian (już pobiera z `?lang=`)
|
||||||
|
- Phase 18 `.po`/`.mo` — statyczne stringi, nie ruszamy
|
||||||
|
|
||||||
|
## SCOPE LIMITS
|
||||||
|
- Nie tłumaczymy nazw krajów w sekcji wyjazdu zagranicznego (lookup keys, dane biznesowe COUNTRY_FLAGS)
|
||||||
|
- Nie tłumaczymy nazw klas pojazdów z Softra (np. „Opel Astra Combi") — dane biznesowe
|
||||||
|
- Nie auto-tłumaczymy przez AI — tylko admin override
|
||||||
|
- Nie dodajemy CSV export/import tłumaczeń — jeśli user zgłosi potrzebę, osobna faza
|
||||||
|
- Nie cacheujemy mapy tłumaczeń separately — WP options autoload cache wystarczy
|
||||||
|
- Nie ruszamy `/protection-packages` endpoint (Phase 17 territory)
|
||||||
|
</boundaries>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Przed zamknięciem planu:
|
||||||
|
- [ ] `grep -n "carei_extras_seen\|carei_extras_translations" includes/` → oba klucze w kodzie
|
||||||
|
- [ ] Panel `wp-admin → Rezerwacje → Tłumaczenia extras` renderuje się bez błędów
|
||||||
|
- [ ] Auto-collect działa (po jednym request pricelist, lista seen nie pusta)
|
||||||
|
- [ ] Save tłumaczeń update'uje option z komunikatem success
|
||||||
|
- [ ] REST pricelist PL = PL names, EN z override = EN, EN bez override = PL fallback
|
||||||
|
- [ ] `php -l` na obu plikach → No syntax errors
|
||||||
|
- [ ] Zero regresji w PL (human-verify)
|
||||||
|
- [ ] AC-1, AC-2, AC-3 pass
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- 2 auto tasks ukończone
|
||||||
|
- Checkpoint human-verify zatwierdzony
|
||||||
|
- Plugin pozwala adminowi zarządzać tłumaczeniami extras przez UI
|
||||||
|
- Kompletna dwujęzyczność (Phase 16+17+18+19 razem) — żadna część UI nie pokazuje PL gdy user jest w EN (poza świadomymi wyjątkami: nazwy miast, krajów, klas pojazdów)
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
Po zakończeniu: `.paul/phases/19-extras-translations-admin/19-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
129
.paul/phases/19-extras-translations-admin/19-01-SUMMARY.md
Normal file
129
.paul/phases/19-extras-translations-admin/19-01-SUMMARY.md
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
---
|
||||||
|
phase: 19-extras-translations-admin
|
||||||
|
plan: 01
|
||||||
|
subsystem: i18n
|
||||||
|
tags: [admin-panel, extras, polylang, softra-pricelist, bilingual]
|
||||||
|
|
||||||
|
requires:
|
||||||
|
- phase: 17-bilingual-packages-and-softra-errors
|
||||||
|
provides: resolve_locale() helper w REST proxy
|
||||||
|
|
||||||
|
provides:
|
||||||
|
- Option carei_extras_seen (auto-collected PL names z Softra pricelist)
|
||||||
|
- Option carei_extras_translations (admin override PL → EN)
|
||||||
|
- Submenu wp-admin → Rezerwacje → Tłumaczenia extras
|
||||||
|
- Helpery: remember_extra_name, get_extras_seen, get_extras_translations, translate_extra_name
|
||||||
|
- REST /pricelist per-locale name replacement z fallbackiem do PL
|
||||||
|
|
||||||
|
affects: []
|
||||||
|
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "Auto-collect + admin override: seen list zbiera się runtime, admin wypełnia override, fallback do oryginału"
|
||||||
|
- "Option z autoload=false — lista seen może rosnąć, nie blokuje boot WP"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
modified:
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-admin-panel.php
|
||||||
|
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Auto-collect przez update_option z autoload=false — prosty, brak wymogu osobnej tabeli DB"
|
||||||
|
- "Sortowanie seen alphabetically z SORT_NATURAL | SORT_FLAG_CASE — przyjazna kolejność w UI"
|
||||||
|
- "Zero zmian w JS frontendu — `?lang=` z Phase 17 wystarcza"
|
||||||
|
- "Nonce + sanitize_text_field na EN inputach — standardowa walidacja WP"
|
||||||
|
- "Fallback do PL dla pustych/niezdefiniowanych override'ów — graceful degradation"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Admin panel UI pattern: lista seen (readonly) + input override (editable) + submit z nonce"
|
||||||
|
|
||||||
|
duration: ~20min
|
||||||
|
started: 2026-04-22
|
||||||
|
completed: 2026-04-22
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 19 Plan 01: Extras translations admin panel — Summary
|
||||||
|
|
||||||
|
**Panel `wp-admin → Rezerwacje → Tłumaczenia extras` pozwala administratorowi zarządzać tłumaczeniami dynamicznych nazw opcji dodatkowych zwracanych z Softra API. Auto-collect PL nazw + override EN + fallback do PL. Milestone v0.8 — 100% complete.**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| Duration | ~20min |
|
||||||
|
| Tasks | 2 auto + 1 human-verify completed |
|
||||||
|
| Files modified | 2 |
|
||||||
|
| New options in WP DB | 2 (seen, translations) |
|
||||||
|
|
||||||
|
## Acceptance Criteria Results
|
||||||
|
|
||||||
|
| Criterion | Status | Notes |
|
||||||
|
|-----------|--------|-------|
|
||||||
|
| AC-1: Auto-collect PL names | Pass | Każdy request `/pricelist` dopisuje nowe nazwy do `carei_extras_seen` |
|
||||||
|
| AC-2: Panel admin zarządza tłumaczeniami | Pass | Submenu, formularz z nonce, sanitize, redirect z komunikatem |
|
||||||
|
| AC-3: REST pricelist zwraca EN gdy override | Pass | Per-locale replacement z fallbackiem do PL |
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- **2 nowe WP options:** `carei_extras_seen` (lista PL names) + `carei_extras_translations` (map PL → EN)
|
||||||
|
- **4 static helpery** w `Carei_Admin_Panel` — czysta API do używania z innych klas
|
||||||
|
- **Submenu admin** z pełnym UX: lista alfabetyczna, info placeholder dla pustej listy, komunikaty sukcesu, nonce protection
|
||||||
|
- **REST `/pricelist` integration** — runtime auto-collect + per-locale replacement, bez dotykania JS frontendu
|
||||||
|
- **Reuse Phase 17** `resolve_locale()` helper — brak duplikacji logiki
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
| File | Change | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `includes/class-admin-panel.php` | Modified | Stałe, helpery, submenu, render, save handler |
|
||||||
|
| `includes/class-rest-proxy.php` | Modified | `get_pricelist()` auto-collect + per-locale replacement |
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
| Decision | Rationale | Impact |
|
||||||
|
|----------|-----------|--------|
|
||||||
|
| WP options zamiast custom table | Lista seen i map override są małe (<100 wpisów) — nadmierna inżynieria | Szybki deploy, standardowy WP pattern |
|
||||||
|
| `autoload=false` dla options | Lista seen rośnie z czasem — nie ładujemy jej przy każdym request | Zero impact na WP boot performance |
|
||||||
|
| Fallback do PL dla pustego override | User może nie tłumaczyć wszystkiego — nigdy nie crashujemy | Graceful degradation, spójna z Phase 17 |
|
||||||
|
| Sortowanie SORT_NATURAL | UX: "Fotelik 1+2" przed "Fotelik 10" w liście | Przyjazna kolejność w panelu admin |
|
||||||
|
| Zero zmian w JS | Phase 17 `?lang=` już działa, frontend nie musi wiedzieć o override | Minimalna surface area zmian |
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
| Type | Count | Impact |
|
||||||
|
|------|-------|--------|
|
||||||
|
| Auto-fixed | 0 | — |
|
||||||
|
| Scope additions | 0 | — |
|
||||||
|
| Deferred | 0 | — |
|
||||||
|
|
||||||
|
**Total impact:** Plan wykonany 1:1.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
**Milestone v0.8 COMPLETE.**
|
||||||
|
|
||||||
|
**System tłumaczeń teraz kompletny:**
|
||||||
|
- Statyczne stringi PHP/JS: `.po`/`.mo` (Phase 16+18)
|
||||||
|
- Pakiety ochronne: pola `_en` w DB (Phase 17)
|
||||||
|
- Błędy Softra: słownik 13 wpisów w `map_error_message()` (Phase 17)
|
||||||
|
- Dynamiczne extras: admin override (Phase 19)
|
||||||
|
|
||||||
|
**Co pozostaje po polsku w wersji EN (świadome):**
|
||||||
|
- Nazwy miast w widgetach mapa/miasta/oddziały — dane biznesowe
|
||||||
|
- Nazwy krajów w sekcji wyjazdu zagranicznego — lookup COUNTRY_FLAGS
|
||||||
|
- Nazwy klas pojazdów z Softra (np. "Opel Astra Combi") — dane biznesowe
|
||||||
|
|
||||||
|
**Concerns:** None. Admin musi wypełnić override dla nowych Softra extras — akceptowalny manual overhead.
|
||||||
|
|
||||||
|
**Blockers:** None.
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 19-extras-translations-admin, Plan: 01*
|
||||||
|
*Completed: 2026-04-22*
|
||||||
40
.vscode/ftp-kr.sync.cache.json
vendored
40
.vscode/ftp-kr.sync.cache.json
vendored
@@ -19,11 +19,12 @@
|
|||||||
"lmtime": 1775065726885,
|
"lmtime": 1775065726885,
|
||||||
"modified": false
|
"modified": false
|
||||||
},
|
},
|
||||||
|
".claude": {},
|
||||||
".env": {
|
".env": {
|
||||||
"type": "-",
|
"type": "-",
|
||||||
"size": 95,
|
"size": 93,
|
||||||
"lmtime": 1774264688610,
|
"lmtime": 1774264688610,
|
||||||
"modified": false
|
"modified": true
|
||||||
},
|
},
|
||||||
".htaccess": {
|
".htaccess": {
|
||||||
"type": "-",
|
"type": "-",
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
"lmtime": 0,
|
"lmtime": 0,
|
||||||
"modified": false
|
"modified": false
|
||||||
},
|
},
|
||||||
|
".paul": {},
|
||||||
"readme.html": {
|
"readme.html": {
|
||||||
"type": "-",
|
"type": "-",
|
||||||
"size": 7425,
|
"size": 7425,
|
||||||
@@ -99,16 +101,16 @@
|
|||||||
"css": {
|
"css": {
|
||||||
"carei-reservation.css": {
|
"carei-reservation.css": {
|
||||||
"type": "-",
|
"type": "-",
|
||||||
"size": 40069,
|
"size": 42236,
|
||||||
"lmtime": 1775067001902,
|
"lmtime": 1776638170414,
|
||||||
"modified": false
|
"modified": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"js": {
|
"js": {
|
||||||
"carei-reservation.js": {
|
"carei-reservation.js": {
|
||||||
"type": "-",
|
"type": "-",
|
||||||
"size": 63668,
|
"size": 72838,
|
||||||
"lmtime": 1775064606160,
|
"lmtime": 1776638422166,
|
||||||
"modified": false
|
"modified": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,8 +124,14 @@
|
|||||||
"includes": {
|
"includes": {
|
||||||
"class-admin-panel.php": {
|
"class-admin-panel.php": {
|
||||||
"type": "-",
|
"type": "-",
|
||||||
"size": 19203,
|
"size": 27964,
|
||||||
"lmtime": 1774456454972,
|
"lmtime": 1776637707828,
|
||||||
|
"modified": false
|
||||||
|
},
|
||||||
|
"class-branches-widget.php": {
|
||||||
|
"type": "-",
|
||||||
|
"size": 4090,
|
||||||
|
"lmtime": 1775066975977,
|
||||||
"modified": false
|
"modified": false
|
||||||
},
|
},
|
||||||
"class-cities-widget.php": {
|
"class-cities-widget.php": {
|
||||||
@@ -134,9 +142,9 @@
|
|||||||
},
|
},
|
||||||
"class-elementor-widget.php": {
|
"class-elementor-widget.php": {
|
||||||
"type": "-",
|
"type": "-",
|
||||||
"size": 20123,
|
"size": 18213,
|
||||||
"lmtime": 1774871052622,
|
"lmtime": 1776637740783,
|
||||||
"modified": true
|
"modified": false
|
||||||
},
|
},
|
||||||
"class-map-widget.php": {
|
"class-map-widget.php": {
|
||||||
"type": "-",
|
"type": "-",
|
||||||
@@ -146,8 +154,8 @@
|
|||||||
},
|
},
|
||||||
"class-rest-proxy.php": {
|
"class-rest-proxy.php": {
|
||||||
"type": "-",
|
"type": "-",
|
||||||
"size": 11384,
|
"size": 12395,
|
||||||
"lmtime": 1775064502720,
|
"lmtime": 1776637720639,
|
||||||
"modified": false
|
"modified": false
|
||||||
},
|
},
|
||||||
"class-search-widget.php": {
|
"class-search-widget.php": {
|
||||||
@@ -161,12 +169,6 @@
|
|||||||
"size": 9235,
|
"size": 9235,
|
||||||
"lmtime": 1775064492252,
|
"lmtime": 1775064492252,
|
||||||
"modified": false
|
"modified": false
|
||||||
},
|
|
||||||
"class-branches-widget.php": {
|
|
||||||
"type": "-",
|
|
||||||
"size": 4090,
|
|
||||||
"lmtime": 1775066975977,
|
|
||||||
"modified": false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
wp-admin/.DS_Store
vendored
Normal file
BIN
wp-admin/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
wp-content/.DS_Store
vendored
Normal file
BIN
wp-content/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
wp-content/languages/.DS_Store
vendored
Normal file
BIN
wp-content/languages/.DS_Store
vendored
Normal file
Binary file not shown.
22
wp-content/mu-plugins/fix-sprintf-global.php
Normal file
22
wp-content/mu-plugins/fix-sprintf-global.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Plugin Name: Fix: global sprintf backfill
|
||||||
|
* Description: Przywraca globalny `window.sprintf` dla pluginów liczących na starsze API wp.i18n (np. Automatic Translate Addon For Polylang). Naprawia "ReferenceError: sprintf is not defined" w bulk translate.
|
||||||
|
* Version: 1.0.0
|
||||||
|
* Author: Carei
|
||||||
|
*
|
||||||
|
* Musi leżeć w wp-content/mu-plugins/ — WP ładuje stąd automatycznie, bez aktywacji.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action( 'admin_enqueue_scripts', function () {
|
||||||
|
wp_enqueue_script( 'wp-i18n' );
|
||||||
|
wp_add_inline_script(
|
||||||
|
'wp-i18n',
|
||||||
|
'if (window.wp && window.wp.i18n && typeof window.sprintf === "undefined") { window.sprintf = window.wp.i18n.sprintf; }',
|
||||||
|
'after'
|
||||||
|
);
|
||||||
|
}, 999 );
|
||||||
BIN
wp-content/plugins/.DS_Store
vendored
Normal file
BIN
wp-content/plugins/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -1647,3 +1647,94 @@ button.carei-reservation-trigger:hover {
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ──────────────────────────────────────────────────────────────
|
||||||
|
Flatpickr — compact theme + Carei colors
|
||||||
|
────────────────────────────────────────────────────────────── */
|
||||||
|
.flatpickr-calendar {
|
||||||
|
width: 260px !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
box-shadow: 0 6px 20px rgba(47, 36, 130, 0.15);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.flatpickr-calendar.open { z-index: 100000; }
|
||||||
|
.flatpickr-months { padding: 4px 0; }
|
||||||
|
.flatpickr-month { height: 28px; }
|
||||||
|
.flatpickr-current-month {
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 4px 0 0 0;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
.flatpickr-current-month .flatpickr-monthDropdown-months,
|
||||||
|
.flatpickr-current-month input.cur-year {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.flatpickr-prev-month,
|
||||||
|
.flatpickr-next-month {
|
||||||
|
padding: 4px 8px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
.flatpickr-prev-month svg,
|
||||||
|
.flatpickr-next-month svg { width: 12px; height: 12px; }
|
||||||
|
.flatpickr-weekdays { height: 24px; }
|
||||||
|
.flatpickr-weekday {
|
||||||
|
font-size: 11px !important;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6b6b8a !important;
|
||||||
|
}
|
||||||
|
.dayContainer {
|
||||||
|
padding: 2px;
|
||||||
|
width: 252px;
|
||||||
|
min-width: 252px;
|
||||||
|
max-width: 252px;
|
||||||
|
}
|
||||||
|
.flatpickr-day {
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 12px;
|
||||||
|
max-width: 32px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 1px 0;
|
||||||
|
}
|
||||||
|
.flatpickr-day.today { border-color: #2F2482; }
|
||||||
|
.flatpickr-day.selected,
|
||||||
|
.flatpickr-day.startRange,
|
||||||
|
.flatpickr-day.endRange,
|
||||||
|
.flatpickr-day.selected:hover {
|
||||||
|
background: #2F2482 !important;
|
||||||
|
border-color: #2F2482 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.flatpickr-day:hover,
|
||||||
|
.flatpickr-day.prevMonthDay:hover,
|
||||||
|
.flatpickr-day.nextMonthDay:hover {
|
||||||
|
background: rgba(47, 36, 130, 0.08);
|
||||||
|
}
|
||||||
|
.flatpickr-time {
|
||||||
|
height: 32px;
|
||||||
|
border-top: 1px solid rgba(47, 36, 130, 0.12);
|
||||||
|
}
|
||||||
|
.flatpickr-time input {
|
||||||
|
font-size: 13px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
.flatpickr-time .flatpickr-time-separator,
|
||||||
|
.flatpickr-time .flatpickr-am-pm {
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.flatpickr-time input:hover,
|
||||||
|
.flatpickr-time input:focus {
|
||||||
|
background: rgba(47, 36, 130, 0.05);
|
||||||
|
}
|
||||||
|
.flatpickr-time .numInputWrapper span {
|
||||||
|
width: 12px;
|
||||||
|
height: 50%;
|
||||||
|
}
|
||||||
|
@media (max-width: 380px) {
|
||||||
|
.flatpickr-calendar { width: calc(100vw - 24px) !important; }
|
||||||
|
.dayContainer { width: 100%; min-width: 0; max-width: 100%; }
|
||||||
|
.flatpickr-day { max-width: none; }
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,26 @@
|
|||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
// ─── i18n Helpers ─────────────────────────────────────────────
|
||||||
|
var I18N = (window.careiI18n || {});
|
||||||
|
function t(key, fallback) {
|
||||||
|
return (key in I18N) ? I18N[key] : (fallback || key);
|
||||||
|
}
|
||||||
|
function tFmt(key, params, fallback) {
|
||||||
|
var str = t(key, fallback);
|
||||||
|
if (params && typeof str === 'string') {
|
||||||
|
Object.keys(params).forEach(function (k) {
|
||||||
|
str = str.replace(new RegExp('%' + k + '%', 'g'), params[k]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
function pluralPl(n, one, few, many) {
|
||||||
|
if (n === 1) return one;
|
||||||
|
if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) return few;
|
||||||
|
return many;
|
||||||
|
}
|
||||||
|
|
||||||
var REST_URL = (window.careiReservation && window.careiReservation.restUrl) || '/wp-json/carei/v1/';
|
var REST_URL = (window.careiReservation && window.careiReservation.restUrl) || '/wp-json/carei/v1/';
|
||||||
var NONCE = (window.careiReservation && window.careiReservation.nonce) || '';
|
var NONCE = (window.careiReservation && window.careiReservation.nonce) || '';
|
||||||
|
|
||||||
@@ -48,13 +68,13 @@
|
|||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
var status = r.status;
|
var status = r.status;
|
||||||
return r.json().then(function (body) {
|
return r.json().then(function (body) {
|
||||||
var msg = (body && body.message) || (body && body.detail) || ('Błąd API: HTTP ' + status);
|
var msg = (body && body.message) || (body && body.detail) || tFmt('errorApiHttp', {status: status}, 'Błąd API: HTTP %status%');
|
||||||
var err = new Error(msg);
|
var err = new Error(msg);
|
||||||
err.httpStatus = status;
|
err.httpStatus = status;
|
||||||
throw err;
|
throw err;
|
||||||
}).catch(function (parseErr) {
|
}).catch(function (parseErr) {
|
||||||
if (parseErr.httpStatus) throw parseErr;
|
if (parseErr.httpStatus) throw parseErr;
|
||||||
var err = new Error('Błąd API: HTTP ' + status);
|
var err = new Error(tFmt('errorApiHttp', {status: status}, 'Błąd API: HTTP %status%'));
|
||||||
err.httpStatus = status;
|
err.httpStatus = status;
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
@@ -64,13 +84,13 @@
|
|||||||
|
|
||||||
function handleFetchError(err, retryFn, isRetry) {
|
function handleFetchError(err, retryFn, isRetry) {
|
||||||
if (err.name === 'AbortError') {
|
if (err.name === 'AbortError') {
|
||||||
throw new Error('Przekroczono czas oczekiwania. Spróbuj ponownie.');
|
throw new Error(t('errorTimeout', 'Przekroczono czas oczekiwania. Spróbuj ponownie.'));
|
||||||
}
|
}
|
||||||
if (err instanceof TypeError && !isRetry) {
|
if (err instanceof TypeError && !isRetry) {
|
||||||
return retryFn();
|
return retryFn();
|
||||||
}
|
}
|
||||||
if (err instanceof TypeError) {
|
if (err instanceof TypeError) {
|
||||||
throw new Error('Brak połączenia z serwerem. Sprawdź internet i spróbuj ponownie.');
|
throw new Error(t('errorNetwork', 'Brak połączenia z serwerem. Sprawdź internet i spróbuj ponownie.'));
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@@ -79,7 +99,7 @@
|
|||||||
|
|
||||||
var overlay, form, segmentSelect, dateFrom, dateTo, daysCount;
|
var overlay, form, segmentSelect, dateFrom, dateTo, daysCount;
|
||||||
var pickupSelect, returnSelect, returnWrap, sameReturnCheck;
|
var pickupSelect, returnSelect, returnWrap, sameReturnCheck;
|
||||||
var extrasWrapper, extrasContainer, insuranceContainer, abroadSection, abroadToggle, abroadSearch, abroadInput, abroadResults, abroadSelected, errorSummary;
|
var extrasWrapper, extrasContainer, abroadSection, abroadToggle, abroadSearch, abroadInput, abroadResults, abroadSelected, errorSummary;
|
||||||
var summaryOverlay, summaryDetails, summaryTable, summaryTotal, summaryError;
|
var summaryOverlay, summaryDetails, summaryTable, summaryTotal, summaryError;
|
||||||
var summaryBack, summaryConfirm;
|
var summaryBack, summaryConfirm;
|
||||||
var successView, successNumber, successClose;
|
var successView, successNumber, successClose;
|
||||||
@@ -104,7 +124,6 @@
|
|||||||
sameReturnCheck = document.getElementById('carei-same-return');
|
sameReturnCheck = document.getElementById('carei-same-return');
|
||||||
extrasWrapper = document.getElementById('carei-extras-wrapper');
|
extrasWrapper = document.getElementById('carei-extras-wrapper');
|
||||||
extrasContainer = document.getElementById('carei-extras-container');
|
extrasContainer = document.getElementById('carei-extras-container');
|
||||||
insuranceContainer = document.getElementById('carei-insurance-container');
|
|
||||||
protectionContainer = document.getElementById('carei-protection-packages-container');
|
protectionContainer = document.getElementById('carei-protection-packages-container');
|
||||||
abroadSection = document.getElementById('carei-abroad-section');
|
abroadSection = document.getElementById('carei-abroad-section');
|
||||||
abroadToggle = document.getElementById('carei-abroad-toggle');
|
abroadToggle = document.getElementById('carei-abroad-toggle');
|
||||||
@@ -127,6 +146,89 @@
|
|||||||
successClose = document.getElementById('carei-success-close');
|
successClose = document.getElementById('carei-success-close');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Flatpickr date pickers (cross-browser, locale-aware) ─────
|
||||||
|
// If Flatpickr fails to load (CDN blocked, network, etc.), fall back gracefully
|
||||||
|
// to native <input type="datetime-local"> — never break date picking.
|
||||||
|
function initDatePickers() {
|
||||||
|
if (typeof window.flatpickr !== 'function') {
|
||||||
|
console.warn('[carei] Flatpickr not loaded — keeping native datetime-local picker.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var isEn = (document.documentElement.lang || '').toLowerCase().indexOf('en') === 0;
|
||||||
|
var locale = 'default';
|
||||||
|
if (!isEn && window.flatpickr.l10ns && window.flatpickr.l10ns.pl) {
|
||||||
|
locale = window.flatpickr.l10ns.pl;
|
||||||
|
}
|
||||||
|
var baseOpts = {
|
||||||
|
enableTime: true,
|
||||||
|
time_24hr: true,
|
||||||
|
dateFormat: 'Y-m-d\\TH:i',
|
||||||
|
altInput: true,
|
||||||
|
altFormat: isEn ? 'Y-m-d H:i' : 'd.m.Y H:i',
|
||||||
|
minuteIncrement: 15,
|
||||||
|
minDate: 'today',
|
||||||
|
locale: locale,
|
||||||
|
allowInput: false,
|
||||||
|
// Force Flatpickr on mobile too (otherwise falls back to native iOS/Android picker — inconsistent look, locale = OS)
|
||||||
|
disableMobile: true,
|
||||||
|
// With enableTime, flatpickr doesn't auto-close on date click (time still editable).
|
||||||
|
// Auto-close 1.5s after last change — gives user time to adjust time, then dismisses.
|
||||||
|
onChange: function (selectedDates, dateStr, instance) {
|
||||||
|
if (instance._careiCloseTimer) clearTimeout(instance._careiCloseTimer);
|
||||||
|
instance._careiCloseTimer = setTimeout(function () {
|
||||||
|
instance.close();
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Modal inputs — use static:true so popup renders INSIDE input container
|
||||||
|
// (bypasses focus trap + z-index conflicts with modal overlay).
|
||||||
|
var modalTargets = [dateFrom, dateTo];
|
||||||
|
// Hero search inputs — render popup to body (default behavior is fine, no modal).
|
||||||
|
var heroTargets = [
|
||||||
|
document.getElementById('carei-search-date-from'),
|
||||||
|
document.getElementById('carei-search-date-to')
|
||||||
|
];
|
||||||
|
|
||||||
|
function attach(el, opts) {
|
||||||
|
if (!el) return;
|
||||||
|
// If already initialized, destroy previous instance (idempotent re-init)
|
||||||
|
if (el._flatpickr) {
|
||||||
|
try { el._flatpickr.destroy(); } catch (e) {}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
el.setAttribute('type', 'text');
|
||||||
|
window.flatpickr(el, opts);
|
||||||
|
// Bind click on the whole field container — any click (icon, label, padding, input)
|
||||||
|
// opens the flatpickr. Simplest UX.
|
||||||
|
var field = el.closest('.carei-form__field--date') || el.closest('.carei-search-form__field');
|
||||||
|
if (field && !field.dataset.careiFpBound) {
|
||||||
|
field.dataset.careiFpBound = '1';
|
||||||
|
field.style.cursor = 'pointer';
|
||||||
|
var lastCloseAt = 0;
|
||||||
|
if (el._flatpickr) {
|
||||||
|
var origOnClose = el._flatpickr.config.onClose || [];
|
||||||
|
el._flatpickr.config.onClose = (Array.isArray(origOnClose) ? origOnClose.slice() : [origOnClose]).concat([function () { lastCloseAt = Date.now(); }]);
|
||||||
|
}
|
||||||
|
field.addEventListener('click', function (e) {
|
||||||
|
// Ignore clicks that originated inside the flatpickr popup itself.
|
||||||
|
if (e.target.closest && e.target.closest('.flatpickr-calendar')) return;
|
||||||
|
// Debounce: if we just closed (within 300ms, e.g. via date selection), don't reopen.
|
||||||
|
if (Date.now() - lastCloseAt < 300) return;
|
||||||
|
if (el._flatpickr) el._flatpickr.open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[carei] Flatpickr init failed for', el.id, err);
|
||||||
|
try { el.setAttribute('type', 'datetime-local'); } catch (e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var modalOpts = Object.assign({}, baseOpts, { static: true });
|
||||||
|
modalTargets.forEach(function (el) { attach(el, modalOpts); });
|
||||||
|
heroTargets.forEach(function (el) { attach(el, baseOpts); });
|
||||||
|
}
|
||||||
|
|
||||||
// ─── State ────────────────────────────────────────────────────
|
// ─── State ────────────────────────────────────────────────────
|
||||||
|
|
||||||
var mapData = null;
|
var mapData = null;
|
||||||
@@ -247,23 +349,23 @@
|
|||||||
if (Array.isArray(classes) && classes.length > 0) {
|
if (Array.isArray(classes) && classes.length > 0) {
|
||||||
allSegments = classes.map(function (c) {
|
allSegments = classes.map(function (c) {
|
||||||
var val = typeof c === 'string' ? c : (c.name || c);
|
var val = typeof c === 'string' ? c : (c.name || c);
|
||||||
var label = typeof c === 'string' ? ('Segment ' + c) : (c.description || c.name || c);
|
var label = typeof c === 'string' ? tFmt('segmentLabel', {name: c}, 'Segment %name%') : (c.description || c.name || c);
|
||||||
return { value: val, label: label };
|
return { value: val, label: label };
|
||||||
});
|
});
|
||||||
populateSelect(segmentSelect, allSegments, 'Wybierz segment pojazdu');
|
populateSelect(segmentSelect, allSegments, t('selectSegment', 'Wybierz segment pojazdu'));
|
||||||
} else {
|
} else {
|
||||||
populateSelect(segmentSelect, [], 'Brak segmentów');
|
populateSelect(segmentSelect, [], t('noSegments', 'Brak segmentów'));
|
||||||
}
|
}
|
||||||
if (segmentSelect) setSelectLoading(segmentSelect, false);
|
if (segmentSelect) setSelectLoading(segmentSelect, false);
|
||||||
|
|
||||||
if (pickupSelect) {
|
if (pickupSelect) {
|
||||||
populateSelect(pickupSelect, [], 'Najpierw wybierz segment');
|
populateSelect(pickupSelect, [], t('pickupPlaceholder', 'Najpierw wybierz segment'));
|
||||||
pickupSelect.disabled = true;
|
pickupSelect.disabled = true;
|
||||||
}
|
}
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
console.error('Failed to load initial data:', err);
|
console.error('Failed to load initial data:', err);
|
||||||
if (segmentSelect) {
|
if (segmentSelect) {
|
||||||
populateSelect(segmentSelect, [], 'Błąd ładowania');
|
populateSelect(segmentSelect, [], t('errorLoading', 'Błąd ładowania'));
|
||||||
setSelectLoading(segmentSelect, false);
|
setSelectLoading(segmentSelect, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -274,7 +376,7 @@
|
|||||||
function onSegmentChange() {
|
function onSegmentChange() {
|
||||||
var selectedSegment = segmentSelect ? segmentSelect.value : '';
|
var selectedSegment = segmentSelect ? segmentSelect.value : '';
|
||||||
if (!selectedSegment || !mapData || !pickupSelect) {
|
if (!selectedSegment || !mapData || !pickupSelect) {
|
||||||
if (pickupSelect) { populateSelect(pickupSelect, [], 'Najpierw wybierz segment'); pickupSelect.disabled = true; }
|
if (pickupSelect) { populateSelect(pickupSelect, [], t('pickupPlaceholder', 'Najpierw wybierz segment')); pickupSelect.disabled = true; }
|
||||||
hideExtras();
|
hideExtras();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -289,10 +391,10 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (filteredOptions.length > 0) {
|
if (filteredOptions.length > 0) {
|
||||||
populateSelect(pickupSelect, filteredOptions, 'Miejsce odbioru');
|
populateSelect(pickupSelect, filteredOptions, t('pickupLabel', 'Miejsce odbioru'));
|
||||||
pickupSelect.disabled = false;
|
pickupSelect.disabled = false;
|
||||||
} else {
|
} else {
|
||||||
populateSelect(pickupSelect, [], 'Brak lokalizacji dla tego segmentu');
|
populateSelect(pickupSelect, [], t('noPickupForSegment', 'Brak lokalizacji dla tego segmentu'));
|
||||||
pickupSelect.disabled = true;
|
pickupSelect.disabled = true;
|
||||||
}
|
}
|
||||||
if (returnSelect) {
|
if (returnSelect) {
|
||||||
@@ -301,7 +403,7 @@
|
|||||||
if (b.city) label += ' — ' + b.city;
|
if (b.city) label += ' — ' + b.city;
|
||||||
return { value: b.name, label: label };
|
return { value: b.name, label: label };
|
||||||
});
|
});
|
||||||
populateSelect(returnSelect, returnOptions, 'Miejsce zwrotu');
|
populateSelect(returnSelect, returnOptions, t('returnLabel', 'Miejsce zwrotu'));
|
||||||
}
|
}
|
||||||
hideExtras();
|
hideExtras();
|
||||||
}
|
}
|
||||||
@@ -353,7 +455,7 @@
|
|||||||
function checkPastAndWarn(input, label) {
|
function checkPastAndWarn(input, label) {
|
||||||
if (!input || !input.value) return;
|
if (!input || !input.value) return;
|
||||||
if (input.value < getNowLocal()) {
|
if (input.value < getNowLocal()) {
|
||||||
warnPastDate(input, label + ' — data lub godzina już minęły');
|
warnPastDate(input, tFmt('warnPastDate', {label: label}, '%label% — data lub godzina już minęły'));
|
||||||
} else {
|
} else {
|
||||||
clearDateError(input);
|
clearDateError(input);
|
||||||
}
|
}
|
||||||
@@ -367,13 +469,13 @@
|
|||||||
if (!dateMinListenersBound) {
|
if (!dateMinListenersBound) {
|
||||||
if (dateFrom) {
|
if (dateFrom) {
|
||||||
dateFrom.addEventListener('change', function () {
|
dateFrom.addEventListener('change', function () {
|
||||||
checkPastAndWarn(dateFrom, 'Rozpoczęcie');
|
checkPastAndWarn(dateFrom, t('dateStart', 'Rozpoczęcie'));
|
||||||
if (dateTo && dateFrom.value) dateTo.setAttribute('min', dateFrom.value);
|
if (dateTo && dateFrom.value) dateTo.setAttribute('min', dateFrom.value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (dateTo) {
|
if (dateTo) {
|
||||||
dateTo.addEventListener('change', function () {
|
dateTo.addEventListener('change', function () {
|
||||||
checkPastAndWarn(dateTo, 'Zakończenie');
|
checkPastAndWarn(dateTo, t('dateEnd', 'Zakończenie'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
dateMinListenersBound = true;
|
dateMinListenersBound = true;
|
||||||
@@ -417,16 +519,18 @@
|
|||||||
if (!dateFrom || !dateTo || !daysCount) return;
|
if (!dateFrom || !dateTo || !daysCount) return;
|
||||||
var from = new Date(dateFrom.value), to = new Date(dateTo.value);
|
var from = new Date(dateFrom.value), to = new Date(dateTo.value);
|
||||||
if (isNaN(from.getTime()) || isNaN(to.getTime()) || to <= from) {
|
if (isNaN(from.getTime()) || isNaN(to.getTime()) || to <= from) {
|
||||||
daysCount.innerHTML = 'Wybrano: <strong>0 dni</strong>'; return;
|
daysCount.innerHTML = tFmt('daysCount', {count: 0, unit: t('dayMany', 'dni')}, 'Wybrano: <strong>%count% %unit%</strong>'); return;
|
||||||
}
|
}
|
||||||
var diff = Math.ceil((to - from) / 86400000);
|
var diff = Math.ceil((to - from) / 86400000);
|
||||||
daysCount.innerHTML = 'Wybrano: <strong>' + diff + ' ' + (diff === 1 ? 'dzień' : 'dni') + '</strong>';
|
var dayUnit = diff === 1 ? t('dayWord', 'dzień') : t('daysWord', 'dni');
|
||||||
|
daysCount.innerHTML = tFmt('daysCount', {count: diff, unit: dayUnit}, 'Wybrano: <strong>%count% %unit%</strong>');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Protection Packages (WP-managed: SOFT, PREMIUM) ──────────
|
// ─── Protection Packages (WP-managed: SOFT, PREMIUM) ──────────
|
||||||
|
|
||||||
function loadProtectionPackages() {
|
function loadProtectionPackages() {
|
||||||
return fetch(REST_URL + 'protection-packages', {
|
var lang = (document.documentElement.lang || '').toLowerCase().indexOf('en') === 0 ? 'en' : 'pl';
|
||||||
|
return fetch(REST_URL + 'protection-packages?lang=' + lang, {
|
||||||
credentials: 'same-origin'
|
credentials: 'same-origin'
|
||||||
}).then(function (r) { return r.ok ? r.json() : null; })
|
}).then(function (r) { return r.ok ? r.json() : null; })
|
||||||
.then(function (data) {
|
.then(function (data) {
|
||||||
@@ -444,7 +548,7 @@
|
|||||||
var pkg = protectionPackages[key];
|
var pkg = protectionPackages[key];
|
||||||
if (!pkg) return;
|
if (!pkg) return;
|
||||||
var price = parseFloat(pkg.pricePerDay || 0);
|
var price = parseFloat(pkg.pricePerDay || 0);
|
||||||
var priceLabel = price > 0 ? price.toFixed(0) + ' zł/doba' : 'Gratis';
|
var priceLabel = price > 0 ? tFmt('pricePerDay', {price: price.toFixed(0)}, '%price% zł/doba') : t('free', 'Gratis');
|
||||||
var descHtml = pkg.description ? '<span class="carei-form__protection-package__desc">' + escHtml(pkg.description) + '</span>' : '';
|
var descHtml = pkg.description ? '<span class="carei-form__protection-package__desc">' + escHtml(pkg.description) + '</span>' : '';
|
||||||
var card = document.createElement('label');
|
var card = document.createElement('label');
|
||||||
card.className = 'carei-form__protection-package';
|
card.className = 'carei-form__protection-package';
|
||||||
@@ -500,7 +604,7 @@
|
|||||||
var pricelist = pricelists[0];
|
var pricelist = pricelists[0];
|
||||||
currentPriceListId = pricelist.id;
|
currentPriceListId = pricelist.id;
|
||||||
var items = pricelist.additionalItems;
|
var items = pricelist.additionalItems;
|
||||||
var insuranceItems = [], extraItems = [];
|
var extraItems = [];
|
||||||
abroadItems = [];
|
abroadItems = [];
|
||||||
selectedCountries = {};
|
selectedCountries = {};
|
||||||
if (Array.isArray(items)) {
|
if (Array.isArray(items)) {
|
||||||
@@ -510,19 +614,20 @@
|
|||||||
var code = (item.code || '').toUpperCase();
|
var code = (item.code || '').toUpperCase();
|
||||||
if (code.indexOf('BRAK') === 0 || code.indexOf('BRUD') === 0 || code.indexOf('KARA') === 0 ||
|
if (code.indexOf('BRAK') === 0 || code.indexOf('BRUD') === 0 || code.indexOf('KARA') === 0 ||
|
||||||
code.indexOf('MYCIE USŁU') === 0 || code === 'MYJ WEW') return;
|
code.indexOf('MYCIE USŁU') === 0 || code === 'MYJ WEW') return;
|
||||||
|
// Drop Softra-insurance items — pakiety ochronne są zarządzane w panelu WP (SOFT/PREMIUM).
|
||||||
|
if (name.indexOf('ubezp') !== -1 || name.indexOf('ochrony') !== -1 ||
|
||||||
|
name.indexOf('zniesienie') !== -1 || name.indexOf('insurance') !== -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (name.indexOf('wyjazd za granic') !== -1) {
|
if (name.indexOf('wyjazd za granic') !== -1) {
|
||||||
item._countryName = parseCountryName(item.name);
|
item._countryName = parseCountryName(item.name);
|
||||||
item._countryFlag = getCountryFlag(item._countryName);
|
item._countryFlag = getCountryFlag(item._countryName);
|
||||||
abroadItems.push(item);
|
abroadItems.push(item);
|
||||||
} else if (name.indexOf('ubezp') !== -1 || name.indexOf('ochrony') !== -1 ||
|
|
||||||
name.indexOf('zniesienie') !== -1 || name.indexOf('insurance') !== -1) {
|
|
||||||
insuranceItems.push(item);
|
|
||||||
} else {
|
} else {
|
||||||
extraItems.push(item);
|
extraItems.push(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (insuranceContainer) { insuranceContainer.innerHTML = ''; insuranceItems.forEach(function (item) { insuranceContainer.appendChild(buildExtraCard(item)); }); }
|
|
||||||
if (extrasContainer) { extrasContainer.innerHTML = ''; extraItems.forEach(function (item) { extrasContainer.appendChild(buildExtraCard(item)); }); }
|
if (extrasContainer) { extrasContainer.innerHTML = ''; extraItems.forEach(function (item) { extrasContainer.appendChild(buildExtraCard(item)); }); }
|
||||||
if (abroadSection) { abroadSection.style.display = abroadItems.length > 0 ? '' : 'none'; }
|
if (abroadSection) { abroadSection.style.display = abroadItems.length > 0 ? '' : 'none'; }
|
||||||
renderAbroadSelected();
|
renderAbroadSelected();
|
||||||
@@ -533,8 +638,12 @@
|
|||||||
var price = parseFloat(item.price || item.minPrice || 0);
|
var price = parseFloat(item.price || item.minPrice || 0);
|
||||||
var maxPrice = parseFloat(item.maxPrice || 0);
|
var maxPrice = parseFloat(item.maxPrice || 0);
|
||||||
var priceLabel = (maxPrice > 0 && maxPrice !== price)
|
var priceLabel = (maxPrice > 0 && maxPrice !== price)
|
||||||
? 'od ' + price.toFixed(0) + ' do ' + maxPrice.toFixed(0) + ' zł'
|
? tFmt('priceRange', {min: price.toFixed(0), max: maxPrice.toFixed(0)}, 'od %min% do %max% zł')
|
||||||
: (price > 0 ? price.toFixed(0) + ' zł' + (item.unit === 'doba' ? '/doba' : '') : 'Gratis');
|
: (price > 0
|
||||||
|
? (item.unit === 'doba'
|
||||||
|
? tFmt('pricePerDay', {price: price.toFixed(0)}, '%price% zł/doba')
|
||||||
|
: tFmt('priceSimple', {price: price.toFixed(0)}, '%price% zł'))
|
||||||
|
: t('free', 'Gratis'));
|
||||||
var card = document.createElement('div');
|
var card = document.createElement('div');
|
||||||
card.className = 'carei-form__extra-card';
|
card.className = 'carei-form__extra-card';
|
||||||
card.innerHTML =
|
card.innerHTML =
|
||||||
@@ -652,7 +761,7 @@
|
|||||||
function buildCountryCard(item, isSelected) {
|
function buildCountryCard(item, isSelected) {
|
||||||
var id = item.id || item.code;
|
var id = item.id || item.code;
|
||||||
var price = parseFloat(item.price || item.minPrice || 0);
|
var price = parseFloat(item.price || item.minPrice || 0);
|
||||||
var priceHtml = '<span class="carei-abroad__price-val">' + (price > 0 ? price.toFixed(0) + ' zł' : 'Gratis') + '</span>';
|
var priceHtml = '<span class="carei-abroad__price-val">' + (price > 0 ? tFmt('priceSimple', {price: price.toFixed(0)}, '%price% zł') : t('free', 'Gratis')) + '</span>';
|
||||||
|
|
||||||
var card = document.createElement('div');
|
var card = document.createElement('div');
|
||||||
card.className = 'carei-abroad__card' + (isSelected ? ' carei-abroad__card--selected' : '');
|
card.className = 'carei-abroad__card' + (isSelected ? ' carei-abroad__card--selected' : '');
|
||||||
@@ -660,7 +769,7 @@
|
|||||||
'<span class="carei-abroad__flag">' + escHtml(item._countryFlag) + '</span>' +
|
'<span class="carei-abroad__flag">' + escHtml(item._countryFlag) + '</span>' +
|
||||||
'<span class="carei-abroad__name">' + escHtml(item._countryName) + '</span>' +
|
'<span class="carei-abroad__name">' + escHtml(item._countryName) + '</span>' +
|
||||||
'<span class="carei-abroad__price">' + priceHtml + '</span>' +
|
'<span class="carei-abroad__price">' + priceHtml + '</span>' +
|
||||||
'<span role="button" tabindex="0" class="carei-abroad__action" data-abroad-id="' + escAttr(id) + '" title="' + (isSelected ? 'Usuń' : 'Dodaj') + '">' +
|
'<span role="button" tabindex="0" class="carei-abroad__action" data-abroad-id="' + escAttr(id) + '" title="' + (isSelected ? t('btnRemove', 'Usuń') : t('btnAdd', 'Dodaj')) + '">' +
|
||||||
(isSelected
|
(isSelected
|
||||||
? '<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>'
|
? '<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>'
|
||||||
: '<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 2v10M2 7h10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>'
|
: '<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 2v10M2 7h10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>'
|
||||||
@@ -694,7 +803,7 @@
|
|||||||
if (!select) return;
|
if (!select) return;
|
||||||
select.innerHTML = '';
|
select.innerHTML = '';
|
||||||
var ph = document.createElement('option');
|
var ph = document.createElement('option');
|
||||||
ph.value = ''; ph.disabled = true; ph.selected = true; ph.textContent = placeholder || 'Wybierz...';
|
ph.value = ''; ph.disabled = true; ph.selected = true; ph.textContent = placeholder || t('selectPlaceholder', 'Wybierz...');
|
||||||
select.appendChild(ph);
|
select.appendChild(ph);
|
||||||
options.forEach(function (opt) {
|
options.forEach(function (opt) {
|
||||||
var o = document.createElement('option');
|
var o = document.createElement('option');
|
||||||
@@ -720,15 +829,15 @@
|
|||||||
// ─── Validation ───────────────────────────────────────────────
|
// ─── Validation ───────────────────────────────────────────────
|
||||||
|
|
||||||
var requiredFields = [
|
var requiredFields = [
|
||||||
{ id: 'carei-segment', type: 'select', msg: 'Wybierz segment pojazdu' },
|
{ id: 'carei-segment', type: 'select', msgKey: 'selectSegment', msgFallback: 'Wybierz segment pojazdu' },
|
||||||
{ id: 'carei-date-from', type: 'input', msg: 'Podaj datę rozpoczęcia' },
|
{ id: 'carei-date-from', type: 'input', msgKey: 'enterDateFrom', msgFallback: 'Podaj datę rozpoczęcia' },
|
||||||
{ id: 'carei-date-to', type: 'input', msg: 'Podaj datę zakończenia' },
|
{ id: 'carei-date-to', type: 'input', msgKey: 'enterDateTo', msgFallback: 'Podaj datę zakończenia' },
|
||||||
{ id: 'carei-pickup-branch', type: 'select', msg: 'Wybierz miejsce odbioru' },
|
{ id: 'carei-pickup-branch', type: 'select', msgKey: 'selectPickup', msgFallback: 'Wybierz miejsce odbioru' },
|
||||||
{ id: 'carei-firstname', type: 'input', msg: 'Podaj imię' },
|
{ id: 'carei-firstname', type: 'input', msgKey: 'enterFirstName', msgFallback: 'Podaj imię' },
|
||||||
{ id: 'carei-lastname', type: 'input', msg: 'Podaj nazwisko' },
|
{ id: 'carei-lastname', type: 'input', msgKey: 'enterLastName', msgFallback: 'Podaj nazwisko' },
|
||||||
{ id: 'carei-email', type: 'email', msg: 'Podaj poprawny adres e-mail' },
|
{ id: 'carei-email', type: 'email', msgKey: 'enterEmail', msgFallback: 'Podaj poprawny adres e-mail' },
|
||||||
{ id: 'carei-phone', type: 'phone', msg: 'Podaj numer telefonu (min. 9 cyfr)' },
|
{ id: 'carei-phone', type: 'phone', msgKey: 'enterPhone', msgFallback: 'Podaj numer telefonu (min. 9 cyfr)' },
|
||||||
{ id: 'carei-privacy', type: 'checkbox', msg: 'Wymagana zgoda na Politykę Prywatności' }
|
{ id: 'carei-privacy', type: 'checkbox', msgKey: 'privacyRequired', msgFallback: 'Wymagana zgoda na Politykę Prywatności' }
|
||||||
];
|
];
|
||||||
|
|
||||||
function validateForm() {
|
function validateForm() {
|
||||||
@@ -747,23 +856,23 @@
|
|||||||
else if (f.type === 'pesel') { if (!/^\d{11}$/.test(el.value.trim())) hasError = true; }
|
else if (f.type === 'pesel') { if (!/^\d{11}$/.test(el.value.trim())) hasError = true; }
|
||||||
else if (f.type === 'select') { if (!el.value) hasError = true; }
|
else if (f.type === 'select') { if (!el.value) hasError = true; }
|
||||||
else { if (!el.value.trim()) hasError = true; }
|
else { if (!el.value.trim()) hasError = true; }
|
||||||
if (hasError) { valid = false; markFieldError(el, f.msg, f.type); }
|
if (hasError) { valid = false; markFieldError(el, t(f.msgKey, f.msgFallback), f.type); }
|
||||||
});
|
});
|
||||||
|
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
if (dateFrom && dateFrom.value && new Date(dateFrom.value) < now) {
|
if (dateFrom && dateFrom.value && new Date(dateFrom.value) < now) {
|
||||||
valid = false; markFieldError(dateFrom, 'Data lub godzina rozpoczęcia już minęły', 'input');
|
valid = false; markFieldError(dateFrom, t('dateStartPast', 'Data lub godzina rozpoczęcia już minęły'), 'input');
|
||||||
}
|
}
|
||||||
if (dateTo && dateTo.value && new Date(dateTo.value) < now) {
|
if (dateTo && dateTo.value && new Date(dateTo.value) < now) {
|
||||||
valid = false; markFieldError(dateTo, 'Data lub godzina zakończenia już minęły', 'input');
|
valid = false; markFieldError(dateTo, t('dateEndPast', 'Data lub godzina zakończenia już minęły'), 'input');
|
||||||
}
|
}
|
||||||
if (dateFrom && dateTo && dateFrom.value && dateTo.value) {
|
if (dateFrom && dateTo && dateFrom.value && dateTo.value) {
|
||||||
if (new Date(dateTo.value) <= new Date(dateFrom.value)) {
|
if (new Date(dateTo.value) <= new Date(dateFrom.value)) {
|
||||||
valid = false; markFieldError(dateTo, 'Data zakończenia musi być po dacie rozpoczęcia', 'input');
|
valid = false; markFieldError(dateTo, t('dateEndAfterStart', 'Data zakończenia musi być po dacie rozpoczęcia'), 'input');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sameReturnCheck && !sameReturnCheck.checked && returnSelect && !returnSelect.value) {
|
if (sameReturnCheck && !sameReturnCheck.checked && returnSelect && !returnSelect.value) {
|
||||||
valid = false; markFieldError(returnSelect, 'Wybierz miejsce zwrotu', 'select');
|
valid = false; markFieldError(returnSelect, t('selectReturn', 'Wybierz miejsce zwrotu'), 'select');
|
||||||
}
|
}
|
||||||
if (errorSummary) errorSummary.style.display = valid ? 'none' : 'block';
|
if (errorSummary) errorSummary.style.display = valid ? 'none' : 'block';
|
||||||
return valid;
|
return valid;
|
||||||
@@ -826,11 +935,11 @@
|
|||||||
if (state === 'loading') {
|
if (state === 'loading') {
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
btn.setAttribute('aria-busy', 'true');
|
btn.setAttribute('aria-busy', 'true');
|
||||||
btn.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg> Przetwarzanie...';
|
btn.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg> ' + escHtml(t('btnProcessing', 'Przetwarzanie...'));
|
||||||
} else {
|
} else {
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.setAttribute('aria-busy', 'false');
|
btn.setAttribute('aria-busy', 'false');
|
||||||
btn.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg> Wyślij';
|
btn.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg> ' + escHtml(t('btnSubmit', 'Wyślij'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -859,7 +968,7 @@
|
|||||||
function createCustomerAndShowSummary() {
|
function createCustomerAndShowSummary() {
|
||||||
var fd = collectFormData();
|
var fd = collectFormData();
|
||||||
hideSummaryError();
|
hideSummaryError();
|
||||||
announce('Ładowanie podsumowania...');
|
announce(t('loadingSummary', 'Ładowanie podsumowania...'));
|
||||||
|
|
||||||
apiPost('customer', {
|
apiPost('customer', {
|
||||||
firstName: fd.firstName,
|
firstName: fd.firstName,
|
||||||
@@ -878,11 +987,11 @@
|
|||||||
currentCustomerId = res.customerId;
|
currentCustomerId = res.customerId;
|
||||||
return loadPricingSummary(fd);
|
return loadPricingSummary(fd);
|
||||||
}
|
}
|
||||||
throw new Error(res.rejectReason || 'Nie udało się utworzyć klienta');
|
throw new Error(res.rejectReason || t('errorCustomerCreate', 'Nie udało się utworzyć klienta'));
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
console.error('Customer creation failed:', err);
|
console.error('Customer creation failed:', err);
|
||||||
setSubmitState('ready');
|
setSubmitState('ready');
|
||||||
showFormError('Błąd tworzenia klienta: ' + err.message);
|
showFormError(tFmt('errorCustomerCreatePrefix', {msg: err.message}, 'Błąd tworzenia klienta: %msg%'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -905,7 +1014,7 @@
|
|||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
console.error('Pricing summary failed:', err);
|
console.error('Pricing summary failed:', err);
|
||||||
setSubmitState('ready');
|
setSubmitState('ready');
|
||||||
showFormError('Błąd pobierania podsumowania: ' + err.message);
|
showFormError(tFmt('errorSummaryPrefix', {msg: err.message}, 'Błąd pobierania podsumowania: %msg%'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -965,7 +1074,12 @@
|
|||||||
function buildBookingComments(userMessage) {
|
function buildBookingComments(userMessage) {
|
||||||
var pkg = getSelectedProtectionPayload();
|
var pkg = getSelectedProtectionPayload();
|
||||||
if (!pkg) return userMessage || '';
|
if (!pkg) return userMessage || '';
|
||||||
var pkgLine = 'Pakiet ochronny: ' + pkg.name + ' — ' + pkg.pricePerDay.toFixed(2) + ' zł/doba × ' + pkg.days + ' = ' + pkg.total.toFixed(2) + ' zł (do doliczenia poza systemem)';
|
var pkgLine = tFmt('protectionComment', {
|
||||||
|
name: pkg.name,
|
||||||
|
perDay: pkg.pricePerDay.toFixed(2),
|
||||||
|
days: pkg.days,
|
||||||
|
total: pkg.total.toFixed(2)
|
||||||
|
}, 'Pakiet ochronny: %name% — %perDay% zł/doba × %days% = %total% zł (do doliczenia poza systemem)');
|
||||||
return userMessage ? (pkgLine + '\n\n' + userMessage) : pkgLine;
|
return userMessage ? (pkgLine + '\n\n' + userMessage) : pkgLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1023,7 +1137,7 @@
|
|||||||
|
|
||||||
// Animated transition: form → summary
|
// Animated transition: form → summary
|
||||||
transitionStep(form, summaryOverlay, function () {
|
transitionStep(form, summaryOverlay, function () {
|
||||||
announce('Podsumowanie rezerwacji');
|
announce(t('announceSummary', 'Podsumowanie rezerwacji'));
|
||||||
var title = summaryOverlay.querySelector('.carei-summary__title');
|
var title = summaryOverlay.querySelector('.carei-summary__title');
|
||||||
if (title) title.focus();
|
if (title) title.focus();
|
||||||
});
|
});
|
||||||
@@ -1039,40 +1153,40 @@
|
|||||||
returnLabel = returnSelect.options[returnSelect.selectedIndex].text;
|
returnLabel = returnSelect.options[returnSelect.selectedIndex].text;
|
||||||
}
|
}
|
||||||
var html =
|
var html =
|
||||||
'<div><strong>Segment:</strong> ' + escHtml(segLabel) + '</div>' +
|
'<div><strong>' + escHtml(t('labelSegment', 'Segment')) + ':</strong> ' + escHtml(segLabel) + '</div>' +
|
||||||
'<div><strong>Od:</strong> ' + escHtml(fd.dateFrom.replace('T', ' ')) + '</div>' +
|
'<div><strong>' + escHtml(t('labelFrom', 'Od')) + ':</strong> ' + escHtml(fd.dateFrom.replace('T', ' ')) + '</div>' +
|
||||||
'<div><strong>Do:</strong> ' + escHtml(fd.dateTo.replace('T', ' ')) + '</div>' +
|
'<div><strong>' + escHtml(t('labelTo', 'Do')) + ':</strong> ' + escHtml(fd.dateTo.replace('T', ' ')) + '</div>' +
|
||||||
'<div><strong>Miejsce odbioru:</strong> ' + escHtml(pickupLabel) + '</div>';
|
'<div><strong>' + escHtml(t('labelPickup', 'Miejsce odbioru')) + ':</strong> ' + escHtml(pickupLabel) + '</div>';
|
||||||
if (returnLabel) {
|
if (returnLabel) {
|
||||||
html += '<div><strong>Miejsce zwrotu:</strong> ' + escHtml(returnLabel) + '</div>';
|
html += '<div><strong>' + escHtml(t('labelReturn', 'Miejsce zwrotu')) + ':</strong> ' + escHtml(returnLabel) + '</div>';
|
||||||
}
|
}
|
||||||
html += '<div><strong>Najemca:</strong> ' + escHtml(fd.firstName + ' ' + fd.lastName) + '</div>' +
|
html += '<div><strong>' + escHtml(t('labelRenter', 'Najemca')) + ':</strong> ' + escHtml(fd.firstName + ' ' + fd.lastName) + '</div>' +
|
||||||
'<div><strong>Email:</strong> ' + escHtml(fd.email) + '</div>' +
|
'<div><strong>' + escHtml(t('labelEmail', 'Email')) + ':</strong> ' + escHtml(fd.email) + '</div>' +
|
||||||
'<div><strong>Telefon:</strong> ' + escHtml(fd.phone) + '</div>';
|
'<div><strong>' + escHtml(t('labelPhone', 'Telefon')) + ':</strong> ' + escHtml(fd.phone) + '</div>';
|
||||||
|
|
||||||
// Selected extras
|
// Selected extras
|
||||||
var selectedExtras = getSelectedExtrasForApi();
|
var selectedExtras = getSelectedExtrasForApi();
|
||||||
var pkgForDetails = getSelectedProtectionPayload();
|
var pkgForDetails = getSelectedProtectionPayload();
|
||||||
if (selectedExtras.length > 0 || pkgForDetails) {
|
if (selectedExtras.length > 0 || pkgForDetails) {
|
||||||
html += '<div style="margin-top:8px"><strong>Wybrane opcje:</strong></div><ul style="margin:4px 0 0 16px;padding:0;list-style:disc;">';
|
html += '<div style="margin-top:8px"><strong>' + escHtml(t('labelSelectedOptions', 'Wybrane opcje')) + ':</strong></div><ul style="margin:4px 0 0 16px;padding:0;list-style:disc;">';
|
||||||
selectedExtras.forEach(function (ex) {
|
selectedExtras.forEach(function (ex) {
|
||||||
var totalPrice = ex.priceAfterDiscount * (ex.amount || 1);
|
var totalPrice = ex.priceAfterDiscount * (ex.amount || 1);
|
||||||
var priceInfo = ex.unit === 'doba' && ex.amount > 1
|
var priceInfo = ex.unit === 'doba' && ex.amount > 1
|
||||||
? fmtPrice(ex.priceAfterDiscount) + ' zł/doba × ' + ex.amount + ' = ' + fmtPrice(totalPrice) + ' zł'
|
? tFmt('priceLineDaily', {perDay: fmtPrice(ex.priceAfterDiscount), days: ex.amount, total: fmtPrice(totalPrice)}, '%perDay% zł/doba × %days% = %total% zł')
|
||||||
: fmtPrice(totalPrice) + ' zł';
|
: tFmt('priceSimpleFmt', {price: fmtPrice(totalPrice)}, '%price% zł');
|
||||||
html += '<li>' + escHtml(toSentenceCase(ex.name)) + ' — ' + priceInfo + '</li>';
|
html += '<li>' + escHtml(toSentenceCase(ex.name)) + ' — ' + priceInfo + '</li>';
|
||||||
});
|
});
|
||||||
if (pkgForDetails) {
|
if (pkgForDetails) {
|
||||||
var pkgInfo = pkgForDetails.days > 1
|
var pkgInfo = pkgForDetails.days > 1
|
||||||
? fmtPrice(pkgForDetails.pricePerDay) + ' zł/doba × ' + pkgForDetails.days + ' = ' + fmtPrice(pkgForDetails.total) + ' zł'
|
? tFmt('priceLineDaily', {perDay: fmtPrice(pkgForDetails.pricePerDay), days: pkgForDetails.days, total: fmtPrice(pkgForDetails.total)}, '%perDay% zł/doba × %days% = %total% zł')
|
||||||
: fmtPrice(pkgForDetails.total) + ' zł';
|
: tFmt('priceSimpleFmt', {price: fmtPrice(pkgForDetails.total)}, '%price% zł');
|
||||||
html += '<li>' + escHtml(pkgForDetails.name) + ' — ' + pkgInfo + '</li>';
|
html += '<li>' + escHtml(pkgForDetails.name) + ' — ' + pkgInfo + '</li>';
|
||||||
}
|
}
|
||||||
html += '</ul>';
|
html += '</ul>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fd.message) {
|
if (fd.message) {
|
||||||
html += '<div style="margin-top:8px"><strong>Wiadomość:</strong> ' + escHtml(fd.message) + '</div>';
|
html += '<div style="margin-top:8px"><strong>' + escHtml(t('labelMessage', 'Wiadomość')) + ':</strong> ' + escHtml(fd.message) + '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
summaryDetails.innerHTML = html;
|
summaryDetails.innerHTML = html;
|
||||||
@@ -1082,19 +1196,19 @@
|
|||||||
|
|
||||||
// Price table
|
// Price table
|
||||||
if (summaryTable && summary.pricelist) {
|
if (summaryTable && summary.pricelist) {
|
||||||
var html = '<table><thead><tr><th>Nazwa</th><th>Ilość</th><th>Netto</th><th>Brutto</th></tr></thead><tbody>';
|
var html = '<table><thead><tr><th>' + escHtml(t('thName', 'Nazwa')) + '</th><th>' + escHtml(t('thQuantity', 'Ilość')) + '</th><th>' + escHtml(t('thNet', 'Netto')) + '</th><th>' + escHtml(t('thGross', 'Brutto')) + '</th></tr></thead><tbody>';
|
||||||
summary.pricelist.forEach(function (item) {
|
summary.pricelist.forEach(function (item) {
|
||||||
var rowClass = item.addedBySystem ? ' class="carei-summary__auto-item"' : '';
|
var rowClass = item.addedBySystem ? ' class="carei-summary__auto-item"' : '';
|
||||||
html += '<tr' + rowClass + '>' +
|
html += '<tr' + rowClass + '>' +
|
||||||
'<td>' + escHtml(toSentenceCase(item.name)) + (item.addedBySystem ? ' <small>(auto)</small>' : '') + '</td>' +
|
'<td>' + escHtml(toSentenceCase(item.name)) + (item.addedBySystem ? ' <small>(' + escHtml(t('labelAuto', 'auto')) + ')</small>' : '') + '</td>' +
|
||||||
'<td>' + (item.amount || 1) + ' ' + escHtml(item.unit || '') + '</td>' +
|
'<td>' + (item.amount || 1) + ' ' + escHtml(item.unit || '') + '</td>' +
|
||||||
'<td>' + fmtPrice(item.netValue) + '</td>' +
|
'<td>' + fmtPrice(item.netValue) + '</td>' +
|
||||||
'<td>' + fmtPrice(item.grossValue) + '</td></tr>';
|
'<td>' + fmtPrice(item.grossValue) + '</td></tr>';
|
||||||
});
|
});
|
||||||
if (protectionPayload) {
|
if (protectionPayload) {
|
||||||
html += '<tr class="carei-summary__protection-row">' +
|
html += '<tr class="carei-summary__protection-row">' +
|
||||||
'<td>' + escHtml(protectionPayload.name) + ' <small>(do doliczenia)</small></td>' +
|
'<td>' + escHtml(protectionPayload.name) + ' <small>(' + escHtml(t('labelExtraCharge', 'do doliczenia')) + ')</small></td>' +
|
||||||
'<td>' + protectionPayload.days + ' doba</td>' +
|
'<td>' + protectionPayload.days + ' ' + escHtml(pluralPl(protectionPayload.days, t('dayOne', 'doba'), t('dayFew', 'doby'), t('dayMany', 'dób'))) + '</td>' +
|
||||||
'<td>—</td>' +
|
'<td>—</td>' +
|
||||||
'<td>' + fmtPrice(protectionPayload.total) + '</td></tr>';
|
'<td>' + fmtPrice(protectionPayload.total) + '</td></tr>';
|
||||||
}
|
}
|
||||||
@@ -1108,12 +1222,12 @@
|
|||||||
var protectionTotal = protectionPayload ? protectionPayload.total : 0;
|
var protectionTotal = protectionPayload ? protectionPayload.total : 0;
|
||||||
var grandGross = softraGross + protectionTotal;
|
var grandGross = softraGross + protectionTotal;
|
||||||
var totalsHtml =
|
var totalsHtml =
|
||||||
'<div class="carei-summary__total-row"><span class="carei-summary__total-label">Netto:</span><span class="carei-summary__total-value">' + fmtPrice(summary.totalNetValue) + '</span></div>' +
|
'<div class="carei-summary__total-row"><span class="carei-summary__total-label">' + escHtml(t('thNet', 'Netto')) + ':</span><span class="carei-summary__total-value">' + fmtPrice(summary.totalNetValue) + '</span></div>' +
|
||||||
'<div class="carei-summary__total-row"><span class="carei-summary__total-label">VAT:</span><span class="carei-summary__total-value">' + fmtPrice(summary.totalVatValue) + '</span></div>';
|
'<div class="carei-summary__total-row"><span class="carei-summary__total-label">' + escHtml(t('labelVat', 'VAT')) + ':</span><span class="carei-summary__total-value">' + fmtPrice(summary.totalVatValue) + '</span></div>';
|
||||||
if (protectionPayload) {
|
if (protectionPayload) {
|
||||||
totalsHtml += '<div class="carei-summary__total-row"><span class="carei-summary__total-label">Pakiet ochronny:</span><span class="carei-summary__total-value">' + fmtPrice(protectionPayload.total) + '</span></div>';
|
totalsHtml += '<div class="carei-summary__total-row"><span class="carei-summary__total-label">' + escHtml(t('labelProtectionPackage', 'Pakiet ochronny')) + ':</span><span class="carei-summary__total-value">' + fmtPrice(protectionPayload.total) + '</span></div>';
|
||||||
}
|
}
|
||||||
totalsHtml += '<div class="carei-summary__total-row carei-summary__total-row--gross"><span class="carei-summary__total-label">Do zapłaty:</span><span class="carei-summary__total-value">' + fmtPrice(grandGross) + ' zł</span></div>';
|
totalsHtml += '<div class="carei-summary__total-row carei-summary__total-row--gross"><span class="carei-summary__total-label">' + escHtml(t('labelToPay', 'Do zapłaty')) + ':</span><span class="carei-summary__total-value">' + tFmt('priceSimpleFmt', {price: fmtPrice(grandGross)}, '%price% zł') + '</span></div>';
|
||||||
summaryTotal.innerHTML = totalsHtml;
|
summaryTotal.innerHTML = totalsHtml;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1136,7 +1250,7 @@
|
|||||||
if (summaryConfirm) {
|
if (summaryConfirm) {
|
||||||
summaryConfirm.disabled = true;
|
summaryConfirm.disabled = true;
|
||||||
summaryConfirm.setAttribute('aria-busy', 'true');
|
summaryConfirm.setAttribute('aria-busy', 'true');
|
||||||
summaryConfirm.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg> Rezerwuję...';
|
summaryConfirm.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg> ' + escHtml(t('btnBookingInProgress', 'Rezerwuję...'));
|
||||||
}
|
}
|
||||||
hideSummaryError();
|
hideSummaryError();
|
||||||
|
|
||||||
@@ -1178,7 +1292,7 @@
|
|||||||
showSuccessView(res.reservationNo || res.reservationId);
|
showSuccessView(res.reservationNo || res.reservationId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Error(translateRejectReason(res.rejectReason) || 'Rezerwacja nie powiodła się');
|
throw new Error(translateRejectReason(res.rejectReason) || t('errorBookingFailed', 'Rezerwacja nie powiodła się'));
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
console.error('Booking failed:', err);
|
console.error('Booking failed:', err);
|
||||||
showSummaryError(err.message);
|
showSummaryError(err.message);
|
||||||
@@ -1189,12 +1303,12 @@
|
|||||||
function translateRejectReason(reason) {
|
function translateRejectReason(reason) {
|
||||||
if (!reason) return null;
|
if (!reason) return null;
|
||||||
var map = {
|
var map = {
|
||||||
'CAR_NOT_FOUND': 'Brak dostępnego pojazdu w wybranym terminie. Zmień daty lub segment.',
|
'CAR_NOT_FOUND': t('rejectCarNotFound', 'Brak dostępnego pojazdu w wybranym terminie. Zmień daty lub segment.'),
|
||||||
'INVALID_DATE_RANGE': 'Nieprawidłowy zakres dat',
|
'INVALID_DATE_RANGE': t('rejectInvalidDateRange', 'Nieprawidłowy zakres dat'),
|
||||||
'BRANCH_NOT_FOUND': 'Nie znaleziono oddziału',
|
'BRANCH_NOT_FOUND': t('rejectBranchNotFound', 'Nie znaleziono oddziału'),
|
||||||
'CUSTOMER_ALREADY_EXISTS': 'Klient o tych danych już istnieje w systemie',
|
'CUSTOMER_ALREADY_EXISTS': t('rejectCustomerExists', 'Klient o tych danych już istnieje w systemie'),
|
||||||
'INVALID_PESEL': 'Nieprawidłowy numer PESEL',
|
'INVALID_PESEL': t('rejectInvalidPesel', 'Nieprawidłowy numer PESEL'),
|
||||||
'PRICE_LIST_EXPIRED': 'Cennik wygasł. Odśwież formularz i spróbuj ponownie.'
|
'PRICE_LIST_EXPIRED': t('rejectPriceListExpired', 'Cennik wygasł. Odśwież formularz i spróbuj ponownie.')
|
||||||
};
|
};
|
||||||
return map[reason] || reason;
|
return map[reason] || reason;
|
||||||
}
|
}
|
||||||
@@ -1203,16 +1317,16 @@
|
|||||||
if (summaryConfirm) {
|
if (summaryConfirm) {
|
||||||
summaryConfirm.disabled = false;
|
summaryConfirm.disabled = false;
|
||||||
summaryConfirm.setAttribute('aria-busy', 'false');
|
summaryConfirm.setAttribute('aria-busy', 'false');
|
||||||
summaryConfirm.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg> Potwierdź rezerwację';
|
summaryConfirm.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg> ' + escHtml(t('btnConfirmBooking', 'Potwierdź rezerwację'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Success View ─────────────────────────────────────────────
|
// ─── Success View ─────────────────────────────────────────────
|
||||||
|
|
||||||
function showSuccessView(reservationNo) {
|
function showSuccessView(reservationNo) {
|
||||||
if (successNumber) successNumber.textContent = 'Nr zamówienia: ' + reservationNo;
|
if (successNumber) successNumber.textContent = tFmt('orderNumber', {no: reservationNo}, 'Nr zamówienia: %no%');
|
||||||
transitionStep(summaryOverlay, successView, function () {
|
transitionStep(summaryOverlay, successView, function () {
|
||||||
announce('Rezerwacja potwierdzona');
|
announce(t('announceBookingConfirmed', 'Rezerwacja potwierdzona'));
|
||||||
var title = successView.querySelector('.carei-success__title');
|
var title = successView.querySelector('.carei-success__title');
|
||||||
if (title) title.focus();
|
if (title) title.focus();
|
||||||
});
|
});
|
||||||
@@ -1226,7 +1340,7 @@
|
|||||||
currentCustomerId = null;
|
currentCustomerId = null;
|
||||||
currentReservationId = null;
|
currentReservationId = null;
|
||||||
hideExtras();
|
hideExtras();
|
||||||
if (pickupSelect) { populateSelect(pickupSelect, [], 'Najpierw wybierz segment'); pickupSelect.disabled = true; }
|
if (pickupSelect) { populateSelect(pickupSelect, [], t('pickupPlaceholder', 'Najpierw wybierz segment')); pickupSelect.disabled = true; }
|
||||||
closeModal();
|
closeModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1324,13 +1438,13 @@
|
|||||||
if (Array.isArray(classes) && classes.length > 0) {
|
if (Array.isArray(classes) && classes.length > 0) {
|
||||||
var segments = classes.map(function (c) {
|
var segments = classes.map(function (c) {
|
||||||
var val = typeof c === 'string' ? c : (c.name || c);
|
var val = typeof c === 'string' ? c : (c.name || c);
|
||||||
var label = typeof c === 'string' ? ('Segment ' + c) : (c.description || c.name || c);
|
var label = typeof c === 'string' ? tFmt('segmentLabel', {name: c}, 'Segment %name%') : (c.description || c.name || c);
|
||||||
return { value: val, label: label };
|
return { value: val, label: label };
|
||||||
});
|
});
|
||||||
populateSelect(searchSegment, segments, 'Wybierz segment');
|
populateSelect(searchSegment, segments, t('selectSegmentShort', 'Wybierz segment'));
|
||||||
}
|
}
|
||||||
if (searchPickup) {
|
if (searchPickup) {
|
||||||
populateSelect(searchPickup, [], 'Najpierw wybierz segment');
|
populateSelect(searchPickup, [], t('pickupPlaceholder', 'Najpierw wybierz segment'));
|
||||||
searchPickup.disabled = true;
|
searchPickup.disabled = true;
|
||||||
}
|
}
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
@@ -1354,10 +1468,10 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (opts.length > 0) {
|
if (opts.length > 0) {
|
||||||
populateSelect(searchPickup, opts, 'Miejsce odbioru');
|
populateSelect(searchPickup, opts, t('pickupLabel', 'Miejsce odbioru'));
|
||||||
searchPickup.disabled = false;
|
searchPickup.disabled = false;
|
||||||
} else {
|
} else {
|
||||||
populateSelect(searchPickup, [], 'Brak lokalizacji');
|
populateSelect(searchPickup, [], t('noLocations', 'Brak lokalizacji'));
|
||||||
searchPickup.disabled = true;
|
searchPickup.disabled = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1449,6 +1563,9 @@
|
|||||||
// Inicjalizuj search form niezależnie od modala
|
// Inicjalizuj search form niezależnie od modala
|
||||||
initSearchForm();
|
initSearchForm();
|
||||||
|
|
||||||
|
// Flatpickr on both modal + hero inputs — safe to call even if modal absent
|
||||||
|
initDatePickers();
|
||||||
|
|
||||||
if (!overlay || !form) return;
|
if (!overlay || !form) return;
|
||||||
|
|
||||||
initModal();
|
initModal();
|
||||||
|
|||||||
@@ -52,6 +52,13 @@ require_once CAREI_RESERVATION_PATH . 'includes/class-softra-api.php';
|
|||||||
require_once CAREI_RESERVATION_PATH . 'includes/class-rest-proxy.php';
|
require_once CAREI_RESERVATION_PATH . 'includes/class-rest-proxy.php';
|
||||||
require_once CAREI_RESERVATION_PATH . 'includes/class-admin-panel.php';
|
require_once CAREI_RESERVATION_PATH . 'includes/class-admin-panel.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load plugin textdomain
|
||||||
|
*/
|
||||||
|
add_action( 'plugins_loaded', function () {
|
||||||
|
load_plugin_textdomain( 'carei-reservation', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
|
||||||
|
} );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize plugin on plugins_loaded
|
* Initialize plugin on plugins_loaded
|
||||||
*/
|
*/
|
||||||
@@ -64,7 +71,7 @@ add_action( 'plugins_loaded', function () {
|
|||||||
|
|
||||||
if ( empty( $api_url ) || empty( $username ) || empty( $password ) ) {
|
if ( empty( $api_url ) || empty( $username ) || empty( $password ) ) {
|
||||||
add_action( 'admin_notices', function () {
|
add_action( 'admin_notices', function () {
|
||||||
echo '<div class="notice notice-error"><p><strong>Carei Reservation:</strong> Brak konfiguracji API w pliku .env (url, username, password).</p></div>';
|
echo '<div class="notice notice-error"><p><strong>Carei Reservation:</strong> ' . esc_html__( 'Brak konfiguracji API w pliku .env (url, username, password).', 'carei-reservation' ) . '</p></div>';
|
||||||
} );
|
} );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -103,16 +110,38 @@ add_action( 'elementor/widgets/register', function ( $widgets_manager ) {
|
|||||||
* Enqueue frontend assets
|
* Enqueue frontend assets
|
||||||
*/
|
*/
|
||||||
add_action( 'wp_enqueue_scripts', function () {
|
add_action( 'wp_enqueue_scripts', function () {
|
||||||
|
// Flatpickr — cross-browser, locale-aware date/time picker (CDN)
|
||||||
|
wp_register_style(
|
||||||
|
'carei-flatpickr-css',
|
||||||
|
'https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.css',
|
||||||
|
array(),
|
||||||
|
'4.6.13'
|
||||||
|
);
|
||||||
|
wp_register_script(
|
||||||
|
'carei-flatpickr-js',
|
||||||
|
'https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js',
|
||||||
|
array(),
|
||||||
|
'4.6.13',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
wp_register_script(
|
||||||
|
'carei-flatpickr-pl',
|
||||||
|
'https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/l10n/pl.js',
|
||||||
|
array( 'carei-flatpickr-js' ),
|
||||||
|
'4.6.13',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
wp_register_style(
|
wp_register_style(
|
||||||
'carei-reservation-css',
|
'carei-reservation-css',
|
||||||
CAREI_RESERVATION_URL . 'assets/css/carei-reservation.css',
|
CAREI_RESERVATION_URL . 'assets/css/carei-reservation.css',
|
||||||
array(),
|
array( 'carei-flatpickr-css' ),
|
||||||
CAREI_RESERVATION_VERSION
|
CAREI_RESERVATION_VERSION
|
||||||
);
|
);
|
||||||
wp_register_script(
|
wp_register_script(
|
||||||
'carei-reservation-js',
|
'carei-reservation-js',
|
||||||
CAREI_RESERVATION_URL . 'assets/js/carei-reservation.js',
|
CAREI_RESERVATION_URL . 'assets/js/carei-reservation.js',
|
||||||
array(),
|
array( 'carei-flatpickr-js', 'carei-flatpickr-pl' ),
|
||||||
CAREI_RESERVATION_VERSION,
|
CAREI_RESERVATION_VERSION,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@@ -120,4 +149,115 @@ add_action( 'wp_enqueue_scripts', function () {
|
|||||||
'restUrl' => esc_url_raw( rest_url( 'carei/v1/' ) ),
|
'restUrl' => esc_url_raw( rest_url( 'carei/v1/' ) ),
|
||||||
'nonce' => wp_create_nonce( 'wp_rest' ),
|
'nonce' => wp_create_nonce( 'wp_rest' ),
|
||||||
) );
|
) );
|
||||||
|
|
||||||
|
wp_localize_script( 'carei-reservation-js', 'careiI18n', array(
|
||||||
|
// API / sieć
|
||||||
|
'errorApiHttp' => esc_html__( 'Błąd API: HTTP %status%', 'carei-reservation' ),
|
||||||
|
'errorTimeout' => esc_html__( 'Przekroczono czas oczekiwania. Spróbuj ponownie.', 'carei-reservation' ),
|
||||||
|
'errorNetwork' => esc_html__( 'Brak połączenia z serwerem. Sprawdź internet i spróbuj ponownie.', 'carei-reservation' ),
|
||||||
|
'errorLoading' => esc_html__( 'Błąd ładowania', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Selecty / placeholdery
|
||||||
|
'selectPlaceholder' => esc_html__( 'Wybierz...', 'carei-reservation' ),
|
||||||
|
'selectSegment' => esc_html__( 'Wybierz segment pojazdu', 'carei-reservation' ),
|
||||||
|
'selectSegmentShort' => esc_html__( 'Wybierz segment', 'carei-reservation' ),
|
||||||
|
'selectPickup' => esc_html__( 'Wybierz miejsce odbioru', 'carei-reservation' ),
|
||||||
|
'selectReturn' => esc_html__( 'Wybierz miejsce zwrotu', 'carei-reservation' ),
|
||||||
|
'segmentLabel' => esc_html__( 'Segment %name%', 'carei-reservation' ),
|
||||||
|
'noSegments' => esc_html__( 'Brak segmentów', 'carei-reservation' ),
|
||||||
|
'pickupPlaceholder' => esc_html__( 'Najpierw wybierz segment', 'carei-reservation' ),
|
||||||
|
'pickupLabel' => esc_html__( 'Miejsce odbioru', 'carei-reservation' ),
|
||||||
|
'returnLabel' => esc_html__( 'Miejsce zwrotu', 'carei-reservation' ),
|
||||||
|
'noPickupForSegment' => esc_html__( 'Brak lokalizacji dla tego segmentu', 'carei-reservation' ),
|
||||||
|
'noLocations' => esc_html__( 'Brak lokalizacji', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Daty
|
||||||
|
'dateStart' => esc_html__( 'Rozpoczęcie', 'carei-reservation' ),
|
||||||
|
'dateEnd' => esc_html__( 'Zakończenie', 'carei-reservation' ),
|
||||||
|
'warnPastDate' => esc_html__( '%label% — data lub godzina już minęły', 'carei-reservation' ),
|
||||||
|
'dateStartPast' => esc_html__( 'Data lub godzina rozpoczęcia już minęły', 'carei-reservation' ),
|
||||||
|
'dateEndPast' => esc_html__( 'Data lub godzina zakończenia już minęły', 'carei-reservation' ),
|
||||||
|
'dateEndAfterStart' => esc_html__( 'Data zakończenia musi być po dacie rozpoczęcia', 'carei-reservation' ),
|
||||||
|
'enterDateFrom' => esc_html__( 'Podaj datę rozpoczęcia', 'carei-reservation' ),
|
||||||
|
'enterDateTo' => esc_html__( 'Podaj datę zakończenia', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Dni/Doby (pluralizacja PL)
|
||||||
|
'daysCount' => wp_kses( __( 'Wybrano: <strong>%count% %unit%</strong>', 'carei-reservation' ), array( 'strong' => array() ) ),
|
||||||
|
'dayWord' => esc_html__( 'dzień', 'carei-reservation' ),
|
||||||
|
'daysWord' => esc_html__( 'dni', 'carei-reservation' ),
|
||||||
|
'dayOne' => esc_html__( 'doba', 'carei-reservation' ),
|
||||||
|
'dayFew' => esc_html__( 'doby', 'carei-reservation' ),
|
||||||
|
'dayMany' => esc_html__( 'dób', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Ceny
|
||||||
|
'pricePerDay' => esc_html__( '%price% zł/doba', 'carei-reservation' ),
|
||||||
|
'priceSimple' => esc_html__( '%price% zł', 'carei-reservation' ),
|
||||||
|
'priceSimpleFmt' => esc_html__( '%price% zł', 'carei-reservation' ),
|
||||||
|
'priceRange' => esc_html__( 'od %min% do %max% zł', 'carei-reservation' ),
|
||||||
|
'priceLineDaily' => esc_html__( '%perDay% zł/doba × %days% = %total% zł', 'carei-reservation' ),
|
||||||
|
'free' => esc_html__( 'Gratis', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Zagranica / akcje
|
||||||
|
'btnRemove' => esc_html__( 'Usuń', 'carei-reservation' ),
|
||||||
|
'btnAdd' => esc_html__( 'Dodaj', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Walidacja formularza
|
||||||
|
'enterFirstName' => esc_html__( 'Podaj imię', 'carei-reservation' ),
|
||||||
|
'enterLastName' => esc_html__( 'Podaj nazwisko', 'carei-reservation' ),
|
||||||
|
'enterEmail' => esc_html__( 'Podaj poprawny adres e-mail', 'carei-reservation' ),
|
||||||
|
'enterPhone' => esc_html__( 'Podaj numer telefonu (min. 9 cyfr)', 'carei-reservation' ),
|
||||||
|
'privacyRequired' => esc_html__( 'Wymagana zgoda na Politykę Prywatności', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Przyciski
|
||||||
|
'btnSubmit' => esc_html__( 'Wyślij', 'carei-reservation' ),
|
||||||
|
'btnProcessing' => esc_html__( 'Przetwarzanie...', 'carei-reservation' ),
|
||||||
|
'btnBookingInProgress' => esc_html__( 'Rezerwuję...', 'carei-reservation' ),
|
||||||
|
'btnConfirmBooking' => esc_html__( 'Potwierdź rezerwację', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Flow klienta / rezerwacji
|
||||||
|
'loadingSummary' => esc_html__( 'Ładowanie podsumowania...', 'carei-reservation' ),
|
||||||
|
'errorCustomerCreate' => esc_html__( 'Nie udało się utworzyć klienta', 'carei-reservation' ),
|
||||||
|
'errorCustomerCreatePrefix' => esc_html__( 'Błąd tworzenia klienta: %msg%', 'carei-reservation' ),
|
||||||
|
'errorSummaryPrefix' => esc_html__( 'Błąd pobierania podsumowania: %msg%', 'carei-reservation' ),
|
||||||
|
'errorBookingFailed' => esc_html__( 'Rezerwacja nie powiodła się', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Pakiet ochronny (komentarz booking)
|
||||||
|
'protectionComment' => esc_html__( 'Pakiet ochronny: %name% — %perDay% zł/doba × %days% = %total% zł (do doliczenia poza systemem)', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Podsumowanie — etykiety
|
||||||
|
'labelSegment' => esc_html__( 'Segment', 'carei-reservation' ),
|
||||||
|
'labelFrom' => esc_html__( 'Od', 'carei-reservation' ),
|
||||||
|
'labelTo' => esc_html__( 'Do', 'carei-reservation' ),
|
||||||
|
'labelPickup' => esc_html__( 'Miejsce odbioru', 'carei-reservation' ),
|
||||||
|
'labelReturn' => esc_html__( 'Miejsce zwrotu', 'carei-reservation' ),
|
||||||
|
'labelRenter' => esc_html__( 'Najemca', 'carei-reservation' ),
|
||||||
|
'labelEmail' => esc_html__( 'Email', 'carei-reservation' ),
|
||||||
|
'labelPhone' => esc_html__( 'Telefon', 'carei-reservation' ),
|
||||||
|
'labelSelectedOptions' => esc_html__( 'Wybrane opcje', 'carei-reservation' ),
|
||||||
|
'labelMessage' => esc_html__( 'Wiadomość', 'carei-reservation' ),
|
||||||
|
'labelAuto' => esc_html__( 'auto', 'carei-reservation' ),
|
||||||
|
'labelExtraCharge' => esc_html__( 'do doliczenia', 'carei-reservation' ),
|
||||||
|
'labelVat' => esc_html__( 'VAT', 'carei-reservation' ),
|
||||||
|
'labelProtectionPackage' => esc_html__( 'Pakiet ochronny', 'carei-reservation' ),
|
||||||
|
'labelToPay' => esc_html__( 'Do zapłaty', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Tabela
|
||||||
|
'thName' => esc_html__( 'Nazwa', 'carei-reservation' ),
|
||||||
|
'thQuantity' => esc_html__( 'Ilość', 'carei-reservation' ),
|
||||||
|
'thNet' => esc_html__( 'Netto', 'carei-reservation' ),
|
||||||
|
'thGross' => esc_html__( 'Brutto', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Reject reasons (Softra → PL fallback)
|
||||||
|
'rejectCarNotFound' => esc_html__( 'Brak dostępnego pojazdu w wybranym terminie. Zmień daty lub segment.', 'carei-reservation' ),
|
||||||
|
'rejectInvalidDateRange' => esc_html__( 'Nieprawidłowy zakres dat', 'carei-reservation' ),
|
||||||
|
'rejectBranchNotFound' => esc_html__( 'Nie znaleziono oddziału', 'carei-reservation' ),
|
||||||
|
'rejectCustomerExists' => esc_html__( 'Klient o tych danych już istnieje w systemie', 'carei-reservation' ),
|
||||||
|
'rejectInvalidPesel' => esc_html__( 'Nieprawidłowy numer PESEL', 'carei-reservation' ),
|
||||||
|
'rejectPriceListExpired' => esc_html__( 'Cennik wygasł. Odśwież formularz i spróbuj ponownie.', 'carei-reservation' ),
|
||||||
|
|
||||||
|
// Success / aria
|
||||||
|
'orderNumber' => esc_html__( 'Nr zamówienia: %no%', 'carei-reservation' ),
|
||||||
|
'announceBookingConfirmed' => esc_html__( 'Rezerwacja potwierdzona', 'carei-reservation' ),
|
||||||
|
'announceSummary' => esc_html__( 'Podsumowanie rezerwacji', 'carei-reservation' ),
|
||||||
|
) );
|
||||||
} );
|
} );
|
||||||
|
|||||||
@@ -8,14 +8,28 @@ class Carei_Admin_Panel {
|
|||||||
const POST_TYPE = 'carei_reservation';
|
const POST_TYPE = 'carei_reservation';
|
||||||
const META_PREFIX = '_carei_';
|
const META_PREFIX = '_carei_';
|
||||||
|
|
||||||
const PROTECTION_OPTION = 'carei_protection_packages';
|
const PROTECTION_OPTION = 'carei_protection_packages';
|
||||||
|
const EXTRAS_SEEN_OPTION = 'carei_extras_seen';
|
||||||
|
const EXTRAS_TRANSLATIONS_OPTION = 'carei_extras_translations';
|
||||||
|
|
||||||
private static $statuses = array(
|
private static $statuses = array(
|
||||||
'nowe' => array( 'label' => 'Nowe', 'color' => '#2F2482' ),
|
'nowe' => array( 'color' => '#2F2482' ),
|
||||||
'przeczytane' => array( 'label' => 'Przeczytane', 'color' => '#f59e0b' ),
|
'przeczytane' => array( 'color' => '#f59e0b' ),
|
||||||
'zrealizowane' => array( 'label' => 'Zrealizowane', 'color' => '#22c55e' ),
|
'zrealizowane' => array( 'color' => '#22c55e' ),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private static function get_status_label( $key ) {
|
||||||
|
switch ( $key ) {
|
||||||
|
case 'nowe':
|
||||||
|
return __( 'Nowe', 'carei-reservation' );
|
||||||
|
case 'przeczytane':
|
||||||
|
return __( 'Przeczytane', 'carei-reservation' );
|
||||||
|
case 'zrealizowane':
|
||||||
|
return __( 'Zrealizowane', 'carei-reservation' );
|
||||||
|
}
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
add_action( 'init', array( $this, 'register_post_type' ) );
|
add_action( 'init', array( $this, 'register_post_type' ) );
|
||||||
add_filter( 'manage_' . self::POST_TYPE . '_posts_columns', array( $this, 'admin_columns' ) );
|
add_filter( 'manage_' . self::POST_TYPE . '_posts_columns', array( $this, 'admin_columns' ) );
|
||||||
@@ -28,6 +42,135 @@ class Carei_Admin_Panel {
|
|||||||
add_action( 'admin_head', array( $this, 'admin_styles' ) );
|
add_action( 'admin_head', array( $this, 'admin_styles' ) );
|
||||||
add_action( 'admin_menu', array( $this, 'register_protection_packages_page' ) );
|
add_action( 'admin_menu', array( $this, 'register_protection_packages_page' ) );
|
||||||
add_action( 'admin_post_carei_save_protection_packages', array( $this, 'handle_protection_packages_save' ) );
|
add_action( 'admin_post_carei_save_protection_packages', array( $this, 'handle_protection_packages_save' ) );
|
||||||
|
add_action( 'admin_menu', array( $this, 'register_extras_translations_page' ) );
|
||||||
|
add_action( 'admin_post_carei_save_extras_translations', array( $this, 'handle_extras_translations_save' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Extras Translations (dynamic Softra pricelist item names) ────
|
||||||
|
|
||||||
|
public static function get_extras_seen() {
|
||||||
|
$seen = get_option( self::EXTRAS_SEEN_OPTION, array() );
|
||||||
|
if ( ! is_array( $seen ) ) return array();
|
||||||
|
$seen = array_values( array_unique( array_filter( array_map( 'strval', $seen ), 'strlen' ) ) );
|
||||||
|
sort( $seen, SORT_NATURAL | SORT_FLAG_CASE );
|
||||||
|
return $seen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_extras_translations() {
|
||||||
|
$map = get_option( self::EXTRAS_TRANSLATIONS_OPTION, array() );
|
||||||
|
return is_array( $map ) ? $map : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function remember_extra_name( $pl_name ) {
|
||||||
|
$pl_name = trim( (string) $pl_name );
|
||||||
|
if ( $pl_name === '' ) return;
|
||||||
|
$seen = get_option( self::EXTRAS_SEEN_OPTION, array() );
|
||||||
|
if ( ! is_array( $seen ) ) $seen = array();
|
||||||
|
if ( ! in_array( $pl_name, $seen, true ) ) {
|
||||||
|
$seen[] = $pl_name;
|
||||||
|
update_option( self::EXTRAS_SEEN_OPTION, $seen, false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function translate_extra_name( $pl_name, $locale = null ) {
|
||||||
|
if ( $locale === null ) {
|
||||||
|
$wp_locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
|
||||||
|
$locale = ( 0 === strpos( (string) $wp_locale, 'en' ) ) ? 'en' : 'pl';
|
||||||
|
}
|
||||||
|
if ( $locale !== 'en' ) return $pl_name;
|
||||||
|
$map = self::get_extras_translations();
|
||||||
|
if ( isset( $map[ $pl_name ] ) && $map[ $pl_name ] !== '' ) {
|
||||||
|
return $map[ $pl_name ];
|
||||||
|
}
|
||||||
|
return $pl_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register_extras_translations_page() {
|
||||||
|
add_submenu_page(
|
||||||
|
'edit.php?post_type=' . self::POST_TYPE,
|
||||||
|
__( 'Tłumaczenia extras', 'carei-reservation' ),
|
||||||
|
__( 'Tłumaczenia extras', 'carei-reservation' ),
|
||||||
|
'manage_options',
|
||||||
|
'carei-extras-translations',
|
||||||
|
array( $this, 'render_extras_translations_page' )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_extras_translations_page() {
|
||||||
|
if ( ! current_user_can( 'manage_options' ) ) {
|
||||||
|
wp_die( esc_html__( 'Brak uprawnień.', 'carei-reservation' ) );
|
||||||
|
}
|
||||||
|
$seen = self::get_extras_seen();
|
||||||
|
$trans = self::get_extras_translations();
|
||||||
|
$saved = isset( $_GET['carei_saved'] ) && $_GET['carei_saved'] === '1';
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php esc_html_e( 'Tłumaczenia extras', 'carei-reservation' ); ?></h1>
|
||||||
|
<p><?php esc_html_e( 'Tłumaczenia nazw dodatkowych opcji (extras) zwracanych z API Softra. Puste pole = wersja polska jest używana także w wersji angielskiej (fallback).', 'carei-reservation' ); ?></p>
|
||||||
|
<?php if ( $saved ) : ?>
|
||||||
|
<div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Zapisano.', 'carei-reservation' ); ?></p></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ( empty( $seen ) ) : ?>
|
||||||
|
<div class="notice notice-info"><p><?php esc_html_e( 'Brak zebranych pozycji. Otwórz formularz rezerwacji i wybierz daty/oddział/klasę aby załadować pricelist — pozycje pojawią się tutaj automatycznie.', 'carei-reservation' ); ?></p></div>
|
||||||
|
<?php else : ?>
|
||||||
|
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="carei-extras-form">
|
||||||
|
<input type="hidden" name="action" value="carei_save_extras_translations">
|
||||||
|
<?php wp_nonce_field( 'carei_extras_translations', 'carei_extras_nonce' ); ?>
|
||||||
|
<table class="form-table striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 40%;"><?php esc_html_e( 'Nazwa PL (z API Softra)', 'carei-reservation' ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Tłumaczenie EN', 'carei-reservation' ); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ( $seen as $pl_name ) :
|
||||||
|
$en = isset( $trans[ $pl_name ] ) ? (string) $trans[ $pl_name ] : '';
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td><strong><?php echo esc_html( $pl_name ); ?></strong></td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="translations[<?php echo esc_attr( $pl_name ); ?>]" value="<?php echo esc_attr( $en ); ?>" class="regular-text" placeholder="<?php echo esc_attr__( 'EN translation...', 'carei-reservation' ); ?>">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php submit_button( __( 'Zapisz tłumaczenia', 'carei-reservation' ) ); ?>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle_extras_translations_save() {
|
||||||
|
if ( ! current_user_can( 'manage_options' ) ) {
|
||||||
|
wp_die( esc_html__( 'Brak uprawnień.', 'carei-reservation' ) );
|
||||||
|
}
|
||||||
|
if ( ! isset( $_POST['carei_extras_nonce'] ) || ! wp_verify_nonce( $_POST['carei_extras_nonce'], 'carei_extras_translations' ) ) {
|
||||||
|
wp_die( esc_html__( 'Nieprawidłowy token.', 'carei-reservation' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$input = isset( $_POST['translations'] ) && is_array( $_POST['translations'] ) ? $_POST['translations'] : array();
|
||||||
|
$clean = array();
|
||||||
|
foreach ( $input as $pl_name => $en_value ) {
|
||||||
|
$pl = trim( (string) wp_unslash( $pl_name ) );
|
||||||
|
$en = sanitize_text_field( wp_unslash( (string) $en_value ) );
|
||||||
|
if ( $pl === '' ) continue;
|
||||||
|
$clean[ $pl ] = $en;
|
||||||
|
}
|
||||||
|
update_option( self::EXTRAS_TRANSLATIONS_OPTION, $clean, false );
|
||||||
|
|
||||||
|
$redirect = add_query_arg(
|
||||||
|
array(
|
||||||
|
'post_type' => self::POST_TYPE,
|
||||||
|
'page' => 'carei-extras-translations',
|
||||||
|
'carei_saved' => '1',
|
||||||
|
),
|
||||||
|
admin_url( 'edit.php' )
|
||||||
|
);
|
||||||
|
wp_safe_redirect( $redirect );
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Protection Packages (SOFT / PREMIUM) ────────────────────
|
// ─── Protection Packages (SOFT / PREMIUM) ────────────────────
|
||||||
@@ -35,16 +178,20 @@ class Carei_Admin_Panel {
|
|||||||
public static function get_protection_packages_defaults() {
|
public static function get_protection_packages_defaults() {
|
||||||
return array(
|
return array(
|
||||||
'soft' => array(
|
'soft' => array(
|
||||||
'name' => 'Ubezpieczenie SOFT',
|
'name' => __( 'Ubezpieczenie SOFT', 'carei-reservation' ),
|
||||||
'pricePerDay' => 0,
|
'name_en' => 'SOFT Protection',
|
||||||
'active' => true,
|
'pricePerDay' => 0,
|
||||||
'description' => '',
|
'active' => true,
|
||||||
|
'description' => '',
|
||||||
|
'description_en' => '',
|
||||||
),
|
),
|
||||||
'premium' => array(
|
'premium' => array(
|
||||||
'name' => 'Ubezpieczenie PREMIUM',
|
'name' => __( 'Ubezpieczenie PREMIUM', 'carei-reservation' ),
|
||||||
'pricePerDay' => 0,
|
'name_en' => 'PREMIUM Protection',
|
||||||
'active' => true,
|
'pricePerDay' => 0,
|
||||||
'description' => '',
|
'active' => true,
|
||||||
|
'description' => '',
|
||||||
|
'description_en' => '',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -59,10 +206,12 @@ class Carei_Admin_Panel {
|
|||||||
foreach ( $defaults as $key => $def ) {
|
foreach ( $defaults as $key => $def ) {
|
||||||
$item = isset( $stored[ $key ] ) && is_array( $stored[ $key ] ) ? $stored[ $key ] : array();
|
$item = isset( $stored[ $key ] ) && is_array( $stored[ $key ] ) ? $stored[ $key ] : array();
|
||||||
$out[ $key ] = array(
|
$out[ $key ] = array(
|
||||||
'name' => isset( $item['name'] ) && $item['name'] !== '' ? (string) $item['name'] : $def['name'],
|
'name' => isset( $item['name'] ) && $item['name'] !== '' ? (string) $item['name'] : $def['name'],
|
||||||
'pricePerDay' => isset( $item['pricePerDay'] ) ? (float) $item['pricePerDay'] : (float) $def['pricePerDay'],
|
'name_en' => isset( $item['name_en'] ) ? (string) $item['name_en'] : '',
|
||||||
'active' => isset( $item['active'] ) ? (bool) $item['active'] : (bool) $def['active'],
|
'pricePerDay' => isset( $item['pricePerDay'] ) ? (float) $item['pricePerDay'] : (float) $def['pricePerDay'],
|
||||||
'description' => isset( $item['description'] ) ? (string) $item['description'] : $def['description'],
|
'active' => isset( $item['active'] ) ? (bool) $item['active'] : (bool) $def['active'],
|
||||||
|
'description' => isset( $item['description'] ) ? (string) $item['description'] : $def['description'],
|
||||||
|
'description_en' => isset( $item['description_en'] ) ? (string) $item['description_en'] : '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return $out;
|
return $out;
|
||||||
@@ -71,8 +220,8 @@ class Carei_Admin_Panel {
|
|||||||
public function register_protection_packages_page() {
|
public function register_protection_packages_page() {
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'edit.php?post_type=' . self::POST_TYPE,
|
'edit.php?post_type=' . self::POST_TYPE,
|
||||||
'Pakiety ochronne',
|
__( 'Pakiety ochronne', 'carei-reservation' ),
|
||||||
'Pakiety ochronne',
|
__( 'Pakiety ochronne', 'carei-reservation' ),
|
||||||
'manage_options',
|
'manage_options',
|
||||||
'carei-protection-packages',
|
'carei-protection-packages',
|
||||||
array( $this, 'render_protection_packages_page' )
|
array( $this, 'render_protection_packages_page' )
|
||||||
@@ -81,16 +230,16 @@ class Carei_Admin_Panel {
|
|||||||
|
|
||||||
public function render_protection_packages_page() {
|
public function render_protection_packages_page() {
|
||||||
if ( ! current_user_can( 'manage_options' ) ) {
|
if ( ! current_user_can( 'manage_options' ) ) {
|
||||||
wp_die( 'Brak uprawnień.' );
|
wp_die( esc_html__( 'Brak uprawnień.', 'carei-reservation' ) );
|
||||||
}
|
}
|
||||||
$data = self::get_protection_packages();
|
$data = self::get_protection_packages();
|
||||||
$saved = isset( $_GET['carei_saved'] ) && $_GET['carei_saved'] === '1';
|
$saved = isset( $_GET['carei_saved'] ) && $_GET['carei_saved'] === '1';
|
||||||
?>
|
?>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<h1>Pakiety ochronne</h1>
|
<h1><?php esc_html_e( 'Pakiety ochronne', 'carei-reservation' ); ?></h1>
|
||||||
<p>Konfiguracja pakietów wyświetlanych w sekcji <strong>Pakiety ochronne</strong> formularza rezerwacji. Cena podawana jest za dobę — total = cena × liczba dób rezerwacji.</p>
|
<p><?php echo wp_kses( __( 'Konfiguracja pakietów wyświetlanych w sekcji <strong>Pakiety ochronne</strong> formularza rezerwacji. Cena podawana jest za dobę — total = cena × liczba dób rezerwacji.', 'carei-reservation' ), array( 'strong' => array() ) ); ?></p>
|
||||||
<?php if ( $saved ) : ?>
|
<?php if ( $saved ) : ?>
|
||||||
<div class="notice notice-success is-dismissible"><p>Zapisano.</p></div>
|
<div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Zapisano.', 'carei-reservation' ); ?></p></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="carei-protection-form">
|
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="carei-protection-form">
|
||||||
<input type="hidden" name="action" value="carei_save_protection_packages">
|
<input type="hidden" name="action" value="carei_save_protection_packages">
|
||||||
@@ -100,29 +249,43 @@ class Carei_Admin_Panel {
|
|||||||
$pkg = $data[ $key ];
|
$pkg = $data[ $key ];
|
||||||
?>
|
?>
|
||||||
<div class="carei-protection-card">
|
<div class="carei-protection-card">
|
||||||
<h2>Pakiet <?php echo esc_html( $label ); ?></h2>
|
<h2><?php echo esc_html( sprintf( __( 'Pakiet %s', 'carei-reservation' ), $label ) ); ?></h2>
|
||||||
<table class="form-table">
|
<table class="form-table">
|
||||||
<tr>
|
<tr>
|
||||||
<th><label for="carei_<?php echo esc_attr( $key ); ?>_name">Nazwa wyświetlana</label></th>
|
<th><label for="carei_<?php echo esc_attr( $key ); ?>_name"><?php esc_html_e( 'Nazwa wyświetlana (PL)', 'carei-reservation' ); ?></label></th>
|
||||||
<td><input type="text" id="carei_<?php echo esc_attr( $key ); ?>_name" name="packages[<?php echo esc_attr( $key ); ?>][name]" value="<?php echo esc_attr( $pkg['name'] ); ?>" class="regular-text" required></td>
|
<td><input type="text" id="carei_<?php echo esc_attr( $key ); ?>_name" name="packages[<?php echo esc_attr( $key ); ?>][name]" value="<?php echo esc_attr( $pkg['name'] ); ?>" class="regular-text" required></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th><label for="carei_<?php echo esc_attr( $key ); ?>_price">Cena za dobę (zł)</label></th>
|
<th><label for="carei_<?php echo esc_attr( $key ); ?>_name_en"><?php esc_html_e( 'Nazwa wyświetlana (EN)', 'carei-reservation' ); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<input type="text" id="carei_<?php echo esc_attr( $key ); ?>_name_en" name="packages[<?php echo esc_attr( $key ); ?>][name_en]" value="<?php echo esc_attr( $pkg['name_en'] ); ?>" class="regular-text" placeholder="<?php echo esc_attr__( 'Np. SOFT Protection', 'carei-reservation' ); ?>">
|
||||||
|
<p class="description"><?php esc_html_e( 'Puste = fallback do wersji polskiej.', 'carei-reservation' ); ?></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><label for="carei_<?php echo esc_attr( $key ); ?>_price"><?php esc_html_e( 'Cena za dobę (zł)', 'carei-reservation' ); ?></label></th>
|
||||||
<td><input type="number" id="carei_<?php echo esc_attr( $key ); ?>_price" name="packages[<?php echo esc_attr( $key ); ?>][pricePerDay]" value="<?php echo esc_attr( $pkg['pricePerDay'] ); ?>" min="0" step="0.01" class="small-text" required></td>
|
<td><input type="number" id="carei_<?php echo esc_attr( $key ); ?>_price" name="packages[<?php echo esc_attr( $key ); ?>][pricePerDay]" value="<?php echo esc_attr( $pkg['pricePerDay'] ); ?>" min="0" step="0.01" class="small-text" required></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Status</th>
|
<th><?php esc_html_e( 'Status', 'carei-reservation' ); ?></th>
|
||||||
<td><label><input type="checkbox" name="packages[<?php echo esc_attr( $key ); ?>][active]" value="1" <?php checked( $pkg['active'] ); ?>> Aktywny (widoczny w modalu)</label></td>
|
<td><label><input type="checkbox" name="packages[<?php echo esc_attr( $key ); ?>][active]" value="1" <?php checked( $pkg['active'] ); ?>> <?php esc_html_e( 'Aktywny (widoczny w modalu)', 'carei-reservation' ); ?></label></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th><label for="carei_<?php echo esc_attr( $key ); ?>_desc">Opis / zakres usług</label></th>
|
<th><label for="carei_<?php echo esc_attr( $key ); ?>_desc"><?php esc_html_e( 'Opis / zakres usług (PL)', 'carei-reservation' ); ?></label></th>
|
||||||
<td><textarea id="carei_<?php echo esc_attr( $key ); ?>_desc" name="packages[<?php echo esc_attr( $key ); ?>][description]" rows="3" cols="60" class="large-text"><?php echo esc_textarea( $pkg['description'] ); ?></textarea></td>
|
<td><textarea id="carei_<?php echo esc_attr( $key ); ?>_desc" name="packages[<?php echo esc_attr( $key ); ?>][description]" rows="3" cols="60" class="large-text"><?php echo esc_textarea( $pkg['description'] ); ?></textarea></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><label for="carei_<?php echo esc_attr( $key ); ?>_desc_en"><?php esc_html_e( 'Opis / zakres usług (EN)', 'carei-reservation' ); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<textarea id="carei_<?php echo esc_attr( $key ); ?>_desc_en" name="packages[<?php echo esc_attr( $key ); ?>][description_en]" rows="3" cols="60" class="large-text" placeholder="<?php echo esc_attr__( 'Basic protection package...', 'carei-reservation' ); ?>"><?php echo esc_textarea( $pkg['description_en'] ); ?></textarea>
|
||||||
|
<p class="description"><?php esc_html_e( 'Puste = fallback do wersji polskiej.', 'carei-reservation' ); ?></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
||||||
<?php submit_button( 'Zapisz pakiety' ); ?>
|
<?php submit_button( __( 'Zapisz pakiety', 'carei-reservation' ) ); ?>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<style>
|
<style>
|
||||||
@@ -134,10 +297,10 @@ class Carei_Admin_Panel {
|
|||||||
|
|
||||||
public function handle_protection_packages_save() {
|
public function handle_protection_packages_save() {
|
||||||
if ( ! current_user_can( 'manage_options' ) ) {
|
if ( ! current_user_can( 'manage_options' ) ) {
|
||||||
wp_die( 'Brak uprawnień.' );
|
wp_die( esc_html__( 'Brak uprawnień.', 'carei-reservation' ) );
|
||||||
}
|
}
|
||||||
if ( ! isset( $_POST['carei_protection_nonce'] ) || ! wp_verify_nonce( $_POST['carei_protection_nonce'], 'carei_protection_packages' ) ) {
|
if ( ! isset( $_POST['carei_protection_nonce'] ) || ! wp_verify_nonce( $_POST['carei_protection_nonce'], 'carei_protection_packages' ) ) {
|
||||||
wp_die( 'Nieprawidłowy token.' );
|
wp_die( esc_html__( 'Nieprawidłowy token.', 'carei-reservation' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
$input = isset( $_POST['packages'] ) && is_array( $_POST['packages'] ) ? $_POST['packages'] : array();
|
$input = isset( $_POST['packages'] ) && is_array( $_POST['packages'] ) ? $_POST['packages'] : array();
|
||||||
@@ -145,16 +308,20 @@ class Carei_Admin_Panel {
|
|||||||
$clean = array();
|
$clean = array();
|
||||||
foreach ( $defaults as $key => $def ) {
|
foreach ( $defaults as $key => $def ) {
|
||||||
$raw = isset( $input[ $key ] ) && is_array( $input[ $key ] ) ? $input[ $key ] : array();
|
$raw = isset( $input[ $key ] ) && is_array( $input[ $key ] ) ? $input[ $key ] : array();
|
||||||
$name = isset( $raw['name'] ) ? sanitize_text_field( wp_unslash( $raw['name'] ) ) : $def['name'];
|
$name = isset( $raw['name'] ) ? sanitize_text_field( wp_unslash( $raw['name'] ) ) : $def['name'];
|
||||||
$price = isset( $raw['pricePerDay'] ) ? (float) $raw['pricePerDay'] : 0;
|
$name_en = isset( $raw['name_en'] ) ? sanitize_text_field( wp_unslash( $raw['name_en'] ) ) : '';
|
||||||
|
$price = isset( $raw['pricePerDay'] ) ? (float) $raw['pricePerDay'] : 0;
|
||||||
if ( $price < 0 ) { $price = 0; }
|
if ( $price < 0 ) { $price = 0; }
|
||||||
$active = ! empty( $raw['active'] );
|
$active = ! empty( $raw['active'] );
|
||||||
$desc = isset( $raw['description'] ) ? sanitize_textarea_field( wp_unslash( $raw['description'] ) ) : '';
|
$desc = isset( $raw['description'] ) ? sanitize_textarea_field( wp_unslash( $raw['description'] ) ) : '';
|
||||||
|
$desc_en = isset( $raw['description_en'] ) ? sanitize_textarea_field( wp_unslash( $raw['description_en'] ) ) : '';
|
||||||
$clean[ $key ] = array(
|
$clean[ $key ] = array(
|
||||||
'name' => $name !== '' ? $name : $def['name'],
|
'name' => $name !== '' ? $name : $def['name'],
|
||||||
'pricePerDay' => $price,
|
'name_en' => $name_en,
|
||||||
'active' => $active,
|
'pricePerDay' => $price,
|
||||||
'description' => $desc,
|
'active' => $active,
|
||||||
|
'description' => $desc,
|
||||||
|
'description_en' => $desc_en,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
update_option( self::PROTECTION_OPTION, $clean );
|
update_option( self::PROTECTION_OPTION, $clean );
|
||||||
@@ -174,15 +341,15 @@ class Carei_Admin_Panel {
|
|||||||
public function register_post_type() {
|
public function register_post_type() {
|
||||||
register_post_type( self::POST_TYPE, array(
|
register_post_type( self::POST_TYPE, array(
|
||||||
'labels' => array(
|
'labels' => array(
|
||||||
'name' => 'Rezerwacje',
|
'name' => __( 'Rezerwacje', 'carei-reservation' ),
|
||||||
'singular_name' => 'Rezerwacja',
|
'singular_name' => __( 'Rezerwacja', 'carei-reservation' ),
|
||||||
'menu_name' => 'Rezerwacje',
|
'menu_name' => __( 'Rezerwacje', 'carei-reservation' ),
|
||||||
'all_items' => 'Wszystkie rezerwacje',
|
'all_items' => __( 'Wszystkie rezerwacje', 'carei-reservation' ),
|
||||||
'view_item' => 'Zobacz rezerwację',
|
'view_item' => __( 'Zobacz rezerwację', 'carei-reservation' ),
|
||||||
'edit_item' => 'Szczegóły rezerwacji',
|
'edit_item' => __( 'Szczegóły rezerwacji', 'carei-reservation' ),
|
||||||
'search_items' => 'Szukaj rezerwacji',
|
'search_items' => __( 'Szukaj rezerwacji', 'carei-reservation' ),
|
||||||
'not_found' => 'Nie znaleziono rezerwacji',
|
'not_found' => __( 'Nie znaleziono rezerwacji', 'carei-reservation' ),
|
||||||
'not_found_in_trash' => 'Brak rezerwacji w koszu',
|
'not_found_in_trash' => __( 'Brak rezerwacji w koszu', 'carei-reservation' ),
|
||||||
),
|
),
|
||||||
'public' => false,
|
'public' => false,
|
||||||
'show_ui' => true,
|
'show_ui' => true,
|
||||||
@@ -202,13 +369,13 @@ class Carei_Admin_Panel {
|
|||||||
public function admin_columns( $columns ) {
|
public function admin_columns( $columns ) {
|
||||||
return array(
|
return array(
|
||||||
'cb' => '<input type="checkbox" />',
|
'cb' => '<input type="checkbox" />',
|
||||||
'reservation_no' => 'Nr rezerwacji',
|
'reservation_no' => __( 'Nr rezerwacji', 'carei-reservation' ),
|
||||||
'client' => 'Klient',
|
'client' => __( 'Klient', 'carei-reservation' ),
|
||||||
'segment' => 'Segment',
|
'segment' => __( 'Segment', 'carei-reservation' ),
|
||||||
'dates' => 'Daty',
|
'dates' => __( 'Daty', 'carei-reservation' ),
|
||||||
'branch' => 'Oddział',
|
'branch' => __( 'Oddział', 'carei-reservation' ),
|
||||||
'carei_status' => 'Status',
|
'carei_status' => __( 'Status', 'carei-reservation' ),
|
||||||
'date' => 'Data',
|
'date' => __( 'Data', 'carei-reservation' ),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,7 +417,7 @@ class Carei_Admin_Panel {
|
|||||||
printf(
|
printf(
|
||||||
'<span class="carei-status-badge" style="background:%s;">%s</span>',
|
'<span class="carei-status-badge" style="background:%s;">%s</span>',
|
||||||
esc_attr( $info['color'] ),
|
esc_attr( $info['color'] ),
|
||||||
esc_html( $info['label'] )
|
esc_html( self::get_status_label( $status ) )
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -264,13 +431,13 @@ class Carei_Admin_Panel {
|
|||||||
}
|
}
|
||||||
$current = isset( $_GET['carei_status'] ) ? sanitize_text_field( $_GET['carei_status'] ) : '';
|
$current = isset( $_GET['carei_status'] ) ? sanitize_text_field( $_GET['carei_status'] ) : '';
|
||||||
echo '<select name="carei_status">';
|
echo '<select name="carei_status">';
|
||||||
echo '<option value="">Wszystkie statusy</option>';
|
echo '<option value="">' . esc_html__( 'Wszystkie statusy', 'carei-reservation' ) . '</option>';
|
||||||
foreach ( self::$statuses as $key => $info ) {
|
foreach ( self::$statuses as $key => $info ) {
|
||||||
printf(
|
printf(
|
||||||
'<option value="%s"%s>%s</option>',
|
'<option value="%s"%s>%s</option>',
|
||||||
esc_attr( $key ),
|
esc_attr( $key ),
|
||||||
selected( $current, $key, false ),
|
selected( $current, $key, false ),
|
||||||
esc_html( $info['label'] )
|
esc_html( self::get_status_label( $key ) )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
echo '</select>';
|
echo '</select>';
|
||||||
@@ -306,7 +473,7 @@ class Carei_Admin_Panel {
|
|||||||
public function add_meta_boxes() {
|
public function add_meta_boxes() {
|
||||||
add_meta_box(
|
add_meta_box(
|
||||||
'carei_reservation_details',
|
'carei_reservation_details',
|
||||||
'Szczegóły rezerwacji',
|
__( 'Szczegóły rezerwacji', 'carei-reservation' ),
|
||||||
array( $this, 'render_meta_box' ),
|
array( $this, 'render_meta_box' ),
|
||||||
self::POST_TYPE,
|
self::POST_TYPE,
|
||||||
'normal',
|
'normal',
|
||||||
@@ -345,7 +512,14 @@ class Carei_Admin_Panel {
|
|||||||
$price = isset( $protection['pricePerDay'] ) ? (float) $protection['pricePerDay'] : 0;
|
$price = isset( $protection['pricePerDay'] ) ? (float) $protection['pricePerDay'] : 0;
|
||||||
$days = isset( $protection['days'] ) ? (int) $protection['days'] : 0;
|
$days = isset( $protection['days'] ) ? (int) $protection['days'] : 0;
|
||||||
$total = isset( $protection['total'] ) ? (float) $protection['total'] : ( $price * $days );
|
$total = isset( $protection['total'] ) ? (float) $protection['total'] : ( $price * $days );
|
||||||
$protection_str = sprintf( '%s — %s zł/doba × %d = %s zł', $name, number_format( $price, 2, ',', ' ' ), $days, number_format( $total, 2, ',', ' ' ) );
|
$protection_str = sprintf(
|
||||||
|
/* translators: 1: package name, 2: price per day, 3: number of days, 4: total */
|
||||||
|
__( '%1$s — %2$s zł/doba × %3$d = %4$s zł', 'carei-reservation' ),
|
||||||
|
$name,
|
||||||
|
number_format( $price, 2, ',', ' ' ),
|
||||||
|
$days,
|
||||||
|
number_format( $total, 2, ',', ' ' )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$address = $meta['address'] ? json_decode( $meta['address'], true ) : null;
|
$address = $meta['address'] ? json_decode( $meta['address'], true ) : null;
|
||||||
@@ -373,34 +547,34 @@ class Carei_Admin_Panel {
|
|||||||
|
|
||||||
?>
|
?>
|
||||||
<table class="carei-meta-table">
|
<table class="carei-meta-table">
|
||||||
<tr><th>Nr rezerwacji</th><td><?php echo esc_html( $meta['reservation_no'] ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Nr rezerwacji', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['reservation_no'] ?: '—' ); ?></td></tr>
|
||||||
<tr><th>ID rezerwacji (Softra)</th><td><?php echo esc_html( $meta['reservation_id'] ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'ID rezerwacji (Softra)', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['reservation_id'] ?: '—' ); ?></td></tr>
|
||||||
<tr><th>ID klienta (Softra)</th><td><?php echo esc_html( $meta['customer_id'] ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'ID klienta (Softra)', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['customer_id'] ?: '—' ); ?></td></tr>
|
||||||
<tr class="carei-meta-divider"><td colspan="2"><hr></td></tr>
|
<tr class="carei-meta-divider"><td colspan="2"><hr></td></tr>
|
||||||
<tr><th>Segment</th><td><?php echo esc_html( $meta['segment'] ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Segment', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['segment'] ?: '—' ); ?></td></tr>
|
||||||
<tr><th>Data od</th><td><?php echo esc_html( $from_fmt ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Data od', 'carei-reservation' ); ?></th><td><?php echo esc_html( $from_fmt ); ?></td></tr>
|
||||||
<tr><th>Data do</th><td><?php echo esc_html( $to_fmt ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Data do', 'carei-reservation' ); ?></th><td><?php echo esc_html( $to_fmt ); ?></td></tr>
|
||||||
<tr><th>Oddział odbioru</th><td><?php echo esc_html( $meta['pickup_branch'] ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Oddział odbioru', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['pickup_branch'] ?: '—' ); ?></td></tr>
|
||||||
<tr><th>Oddział zwrotu</th><td><?php echo esc_html( $meta['return_branch'] ?: $meta['pickup_branch'] ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Oddział zwrotu', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['return_branch'] ?: $meta['pickup_branch'] ?: '—' ); ?></td></tr>
|
||||||
<tr class="carei-meta-divider"><td colspan="2"><hr></td></tr>
|
<tr class="carei-meta-divider"><td colspan="2"><hr></td></tr>
|
||||||
<tr><th>Imię</th><td><?php echo esc_html( $meta['first_name'] ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Imię', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['first_name'] ?: '—' ); ?></td></tr>
|
||||||
<tr><th>Nazwisko</th><td><?php echo esc_html( $meta['last_name'] ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Nazwisko', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['last_name'] ?: '—' ); ?></td></tr>
|
||||||
<tr><th>Email</th><td><?php echo esc_html( $meta['email'] ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Email', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['email'] ?: '—' ); ?></td></tr>
|
||||||
<tr><th>Telefon</th><td><?php echo esc_html( $meta['phone'] ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Telefon', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['phone'] ?: '—' ); ?></td></tr>
|
||||||
<tr><th>PESEL</th><td><?php echo esc_html( $meta['pesel'] ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'PESEL', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['pesel'] ?: '—' ); ?></td></tr>
|
||||||
<tr><th>Adres</th><td><?php echo esc_html( $address_str ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Adres', 'carei-reservation' ); ?></th><td><?php echo esc_html( $address_str ?: '—' ); ?></td></tr>
|
||||||
<tr class="carei-meta-divider"><td colspan="2"><hr></td></tr>
|
<tr class="carei-meta-divider"><td colspan="2"><hr></td></tr>
|
||||||
<tr><th>Opcje dodatkowe</th><td><?php echo esc_html( $extras_str ?: 'Brak' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Opcje dodatkowe', 'carei-reservation' ); ?></th><td><?php echo esc_html( $extras_str ?: __( 'Brak', 'carei-reservation' ) ); ?></td></tr>
|
||||||
<tr><th>Pakiet ochronny</th><td><?php echo esc_html( $protection_str ?: 'Brak' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Pakiet ochronny', 'carei-reservation' ); ?></th><td><?php echo esc_html( $protection_str ?: __( 'Brak', 'carei-reservation' ) ); ?></td></tr>
|
||||||
<tr><th>Wiadomość</th><td><?php echo esc_html( $meta['comments'] ?: '—' ); ?></td></tr>
|
<tr><th><?php esc_html_e( 'Wiadomość', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['comments'] ?: '—' ); ?></td></tr>
|
||||||
<tr class="carei-meta-divider"><td colspan="2"><hr></td></tr>
|
<tr class="carei-meta-divider"><td colspan="2"><hr></td></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Status</th>
|
<th><?php esc_html_e( 'Status', 'carei-reservation' ); ?></th>
|
||||||
<td>
|
<td>
|
||||||
<select name="carei_status">
|
<select name="carei_status">
|
||||||
<?php foreach ( self::$statuses as $key => $info ) : ?>
|
<?php foreach ( self::$statuses as $key => $info ) : ?>
|
||||||
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $meta['status'], $key ); ?>>
|
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $meta['status'], $key ); ?>>
|
||||||
<?php echo esc_html( $info['label'] ); ?>
|
<?php echo esc_html( self::get_status_label( $key ) ); ?>
|
||||||
</option>
|
</option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
@@ -513,7 +687,13 @@ class Carei_Admin_Panel {
|
|||||||
$first_name = isset( $driver['firstName'] ) ? $driver['firstName'] : '';
|
$first_name = isset( $driver['firstName'] ) ? $driver['firstName'] : '';
|
||||||
$last_name = isset( $driver['lastName'] ) ? $driver['lastName'] : '';
|
$last_name = isset( $driver['lastName'] ) ? $driver['lastName'] : '';
|
||||||
|
|
||||||
$title = sprintf( 'Rezerwacja #%s — %s %s', $reservation_no ?: $reservation_id, $first_name, $last_name );
|
$title = sprintf(
|
||||||
|
/* translators: 1: reservation number or ID, 2: first name, 3: last name */
|
||||||
|
__( 'Rezerwacja #%1$s — %2$s %3$s', 'carei-reservation' ),
|
||||||
|
$reservation_no ?: $reservation_id,
|
||||||
|
$first_name,
|
||||||
|
$last_name
|
||||||
|
);
|
||||||
|
|
||||||
$post_id = wp_insert_post( array(
|
$post_id = wp_insert_post( array(
|
||||||
'post_type' => self::POST_TYPE,
|
'post_type' => self::POST_TYPE,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class Carei_Branches_Widget extends \Elementor\Widget_Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function get_title() {
|
public function get_title() {
|
||||||
return 'Carei Branches';
|
return esc_html__( 'Carei Branches', 'carei-reservation' );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_icon() {
|
public function get_icon() {
|
||||||
@@ -92,11 +92,11 @@ class Carei_Branches_Widget extends \Elementor\Widget_Base {
|
|||||||
if ( $street ) {
|
if ( $street ) {
|
||||||
$street_lower = mb_strtolower( $street, 'UTF-8' );
|
$street_lower = mb_strtolower( $street, 'UTF-8' );
|
||||||
$has_prefix = preg_match( '/^(ul\.|al\.|pl\.|os\.)/u', $street_lower );
|
$has_prefix = preg_match( '/^(ul\.|al\.|pl\.|os\.)/u', $street_lower );
|
||||||
$street = $has_prefix ? $street : 'ul. ' . $street;
|
$street = $has_prefix ? $street : sprintf( /* translators: %s: street name */ __( 'ul. %s', 'carei-reservation' ), $street );
|
||||||
}
|
}
|
||||||
|
|
||||||
$result[] = array(
|
$result[] = array(
|
||||||
'name' => 'Oddział ' . $display_city,
|
'name' => sprintf( /* translators: %s: city name */ __( 'Oddział %s', 'carei-reservation' ), $display_city ),
|
||||||
'street' => $street,
|
'street' => $street,
|
||||||
'zipCity' => trim( $zip . ' ' . $api_city_tc ),
|
'zipCity' => trim( $zip . ' ' . $api_city_tc ),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class Carei_Cities_Widget extends \Elementor\Widget_Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function get_title() {
|
public function get_title() {
|
||||||
return 'Carei Cities';
|
return esc_html__( 'Carei Cities', 'carei-reservation' );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_icon() {
|
public function get_icon() {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function get_title() {
|
public function get_title() {
|
||||||
return 'Carei Reservation';
|
return esc_html__( 'Carei Reservation', 'carei-reservation' );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_icon() {
|
public function get_icon() {
|
||||||
@@ -34,14 +34,14 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
|
|
||||||
protected function register_controls() {
|
protected function register_controls() {
|
||||||
$this->start_controls_section( 'content_section', array(
|
$this->start_controls_section( 'content_section', array(
|
||||||
'label' => 'Przycisk rezerwacji',
|
'label' => esc_html__( 'Przycisk rezerwacji', 'carei-reservation' ),
|
||||||
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
|
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
|
||||||
) );
|
) );
|
||||||
|
|
||||||
$this->add_control( 'button_text', array(
|
$this->add_control( 'button_text', array(
|
||||||
'label' => 'Tekst przycisku',
|
'label' => esc_html__( 'Tekst przycisku', 'carei-reservation' ),
|
||||||
'type' => \Elementor\Controls_Manager::TEXT,
|
'type' => \Elementor\Controls_Manager::TEXT,
|
||||||
'default' => 'Złóż zapytanie o rezerwację',
|
'default' => esc_html__( 'Złóż zapytanie o rezerwację', 'carei-reservation' ),
|
||||||
) );
|
) );
|
||||||
|
|
||||||
$this->end_controls_section();
|
$this->end_controls_section();
|
||||||
@@ -59,12 +59,12 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
<div class="carei-modal-overlay" data-carei-modal role="dialog" aria-modal="true" aria-labelledby="carei-modal-title">
|
<div class="carei-modal-overlay" data-carei-modal role="dialog" aria-modal="true" aria-labelledby="carei-modal-title">
|
||||||
<div class="carei-modal">
|
<div class="carei-modal">
|
||||||
<div class="carei-modal__scroll">
|
<div class="carei-modal__scroll">
|
||||||
<span role="button" tabindex="0" class="carei-modal-close" data-carei-close-modal aria-label="Zamknij formularz">
|
<span role="button" tabindex="0" class="carei-modal-close" data-carei-close-modal aria-label="<?php echo esc_attr__( 'Zamknij formularz', 'carei-reservation' ); ?>">
|
||||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M15 5L5 15M5 5l10 10" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
<path d="M15 5L5 15M5 5l10 10" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<h2 class="carei-modal-title" id="carei-modal-title">Wypełnij formularz rezerwacji<span>.</span></h2>
|
<h2 class="carei-modal-title" id="carei-modal-title"><?php esc_html_e( 'Wypełnij formularz rezerwacji', 'carei-reservation' ); ?><span>.</span></h2>
|
||||||
|
|
||||||
<form class="carei-form" id="carei-reservation-form" novalidate>
|
<form class="carei-form" id="carei-reservation-form" novalidate>
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
<div class="carei-form__field">
|
<div class="carei-form__field">
|
||||||
<div class="carei-form__select-wrap">
|
<div class="carei-form__select-wrap">
|
||||||
<select id="carei-segment" name="segment" required>
|
<select id="carei-segment" name="segment" required>
|
||||||
<option value="" disabled selected>Wybierz segment pojazdu</option>
|
<option value="" disabled selected><?php esc_html_e( 'Wybierz segment pojazdu', 'carei-reservation' ); ?></option>
|
||||||
</select>
|
</select>
|
||||||
<svg class="carei-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
<svg class="carei-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,27 +82,27 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
<div class="carei-form__field carei-form__field--date">
|
<div class="carei-form__field carei-form__field--date">
|
||||||
<div class="carei-form__date-wrap">
|
<div class="carei-form__date-wrap">
|
||||||
<svg class="carei-form__date-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3" width="11" height="10" rx="0.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path d="M11 1.5v2M5 1.5v2M2.5 5.5h11" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg class="carei-form__date-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3" width="11" height="10" rx="0.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path d="M11 1.5v2M5 1.5v2M2.5 5.5h11" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
<label class="carei-form__date-label" for="carei-date-from">Od kiedy?</label>
|
<label class="carei-form__date-label" for="carei-date-from"><?php esc_html_e( 'Od kiedy?', 'carei-reservation' ); ?></label>
|
||||||
<input type="datetime-local" id="carei-date-from" name="dateFrom" class="carei-form__input carei-form__input--date" required>
|
<input type="datetime-local" id="carei-date-from" name="dateFrom" class="carei-form__input carei-form__input--date" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="carei-form__field carei-form__field--date">
|
<div class="carei-form__field carei-form__field--date">
|
||||||
<div class="carei-form__date-wrap">
|
<div class="carei-form__date-wrap">
|
||||||
<svg class="carei-form__date-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3" width="11" height="10" rx="0.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path d="M11 1.5v2M5 1.5v2M2.5 5.5h11" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg class="carei-form__date-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3" width="11" height="10" rx="0.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path d="M11 1.5v2M5 1.5v2M2.5 5.5h11" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
<label class="carei-form__date-label" for="carei-date-to">Do kiedy?</label>
|
<label class="carei-form__date-label" for="carei-date-to"><?php esc_html_e( 'Do kiedy?', 'carei-reservation' ); ?></label>
|
||||||
<input type="datetime-local" id="carei-date-to" name="dateTo" class="carei-form__input carei-form__input--date" required>
|
<input type="datetime-local" id="carei-date-to" name="dateTo" class="carei-form__input carei-form__input--date" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="carei-form__days-count" id="carei-days-count">Wybrano: <strong>0 dni</strong></div>
|
<div class="carei-form__days-count" id="carei-days-count"><?php esc_html_e( 'Wybrano:', 'carei-reservation' ); ?> <strong><?php esc_html_e( '0 dni', 'carei-reservation' ); ?></strong></div>
|
||||||
|
|
||||||
<div class="carei-form__row carei-form__row--pickup">
|
<div class="carei-form__row carei-form__row--pickup">
|
||||||
<div class="carei-form__field">
|
<div class="carei-form__field">
|
||||||
<div class="carei-form__select-wrap carei-form__select-wrap--icon">
|
<div class="carei-form__select-wrap carei-form__select-wrap--icon">
|
||||||
<svg class="carei-form__icon-pin" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 1C5.24 1 3 3.24 3 6c0 3.75 5 9 5 9s5-5.25 5-9c0-2.76-2.24-5-5-5zm0 7a2 2 0 110-4 2 2 0 010 4z" fill="currentColor"/></svg>
|
<svg class="carei-form__icon-pin" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 1C5.24 1 3 3.24 3 6c0 3.75 5 9 5 9s5-5.25 5-9c0-2.76-2.24-5-5-5zm0 7a2 2 0 110-4 2 2 0 010 4z" fill="currentColor"/></svg>
|
||||||
<select id="carei-pickup-branch" name="pickupBranch" required>
|
<select id="carei-pickup-branch" name="pickupBranch" required>
|
||||||
<option value="" disabled selected>Miejsce odbioru</option>
|
<option value="" disabled selected><?php esc_html_e( 'Miejsce odbioru', 'carei-reservation' ); ?></option>
|
||||||
</select>
|
</select>
|
||||||
<svg class="carei-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
<svg class="carei-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,7 +112,7 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
<span class="carei-form__checkbox-box">
|
<span class="carei-form__checkbox-box">
|
||||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7l3.5 3.5L12 4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7l3.5 3.5L12 4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
</span>
|
</span>
|
||||||
<span class="carei-form__checkbox-text">Zwrot w tej samej lokalizacji</span>
|
<span class="carei-form__checkbox-text"><?php esc_html_e( 'Zwrot w tej samej lokalizacji', 'carei-reservation' ); ?></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
<div class="carei-form__select-wrap carei-form__select-wrap--icon">
|
<div class="carei-form__select-wrap carei-form__select-wrap--icon">
|
||||||
<svg class="carei-form__icon-pin" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 1C5.24 1 3 3.24 3 6c0 3.75 5 9 5 9s5-5.25 5-9c0-2.76-2.24-5-5-5zm0 7a2 2 0 110-4 2 2 0 010 4z" fill="currentColor"/></svg>
|
<svg class="carei-form__icon-pin" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 1C5.24 1 3 3.24 3 6c0 3.75 5 9 5 9s5-5.25 5-9c0-2.76-2.24-5-5-5zm0 7a2 2 0 110-4 2 2 0 010 4z" fill="currentColor"/></svg>
|
||||||
<select id="carei-return-branch" name="returnBranch">
|
<select id="carei-return-branch" name="returnBranch">
|
||||||
<option value="" disabled selected>Miejsce zwrotu</option>
|
<option value="" disabled selected><?php esc_html_e( 'Miejsce zwrotu', 'carei-reservation' ); ?></option>
|
||||||
</select>
|
</select>
|
||||||
<svg class="carei-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
<svg class="carei-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,18 +130,18 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
<!-- Ubezpieczenie + Opcje dodatkowe (ukryte do wybrania segmentu i miejsca odbioru) -->
|
<!-- Ubezpieczenie + Opcje dodatkowe (ukryte do wybrania segmentu i miejsca odbioru) -->
|
||||||
<div id="carei-extras-wrapper" class="carei-form__extras-wrapper" style="display:none;">
|
<div id="carei-extras-wrapper" class="carei-form__extras-wrapper" style="display:none;">
|
||||||
|
|
||||||
<div class="carei-form__divider"><span>Wyjazd zagraniczny</span></div>
|
<div class="carei-form__divider"><span><?php esc_html_e( 'Wyjazd zagraniczny', 'carei-reservation' ); ?></span></div>
|
||||||
<div class="carei-form__section" id="carei-abroad-section" style="display:none;">
|
<div class="carei-form__section" id="carei-abroad-section" style="display:none;">
|
||||||
<label class="carei-form__checkbox-label carei-form__checkbox-label--abroad">
|
<label class="carei-form__checkbox-label carei-form__checkbox-label--abroad">
|
||||||
<input type="checkbox" id="carei-abroad-toggle" name="abroadToggle">
|
<input type="checkbox" id="carei-abroad-toggle" name="abroadToggle">
|
||||||
<span class="carei-form__checkbox-box"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7l3.5 3.5L12 4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
|
<span class="carei-form__checkbox-box"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7l3.5 3.5L12 4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
|
||||||
<span>Wyjazd poza granicę Polski do:</span>
|
<span><?php esc_html_e( 'Wyjazd poza granicę Polski do:', 'carei-reservation' ); ?></span>
|
||||||
</label>
|
</label>
|
||||||
<div id="carei-abroad-search" class="carei-abroad" style="display:none;">
|
<div id="carei-abroad-search" class="carei-abroad" style="display:none;">
|
||||||
<div class="carei-abroad__input-wrap">
|
<div class="carei-abroad__input-wrap">
|
||||||
<svg class="carei-abroad__plus-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2v12M2 8h12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
|
<svg class="carei-abroad__plus-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2v12M2 8h12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
|
||||||
<input type="text" id="carei-abroad-input" class="carei-abroad__input" placeholder="Wyszukaj i dodaj kraj na trasie">
|
<input type="text" id="carei-abroad-input" class="carei-abroad__input" placeholder="<?php echo esc_attr__( 'Wyszukaj i dodaj kraj na trasie', 'carei-reservation' ); ?>">
|
||||||
<span role="button" tabindex="0" class="carei-abroad__clear" id="carei-abroad-clear" title="Wyczyść">
|
<span role="button" tabindex="0" class="carei-abroad__clear" id="carei-abroad-clear" title="<?php echo esc_attr__( 'Wyczyść', 'carei-reservation' ); ?>">
|
||||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -150,18 +150,14 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="carei-form__divider"><span>Pakiety ochronne</span></div>
|
<div class="carei-form__divider"><span><?php esc_html_e( 'Pakiety ochronne', 'carei-reservation' ); ?></span></div>
|
||||||
<div class="carei-form__section">
|
<div class="carei-form__section">
|
||||||
<div class="carei-form__row carei-form__row--protection-packages" id="carei-protection-packages-container">
|
<div class="carei-form__row carei-form__row--protection-packages" id="carei-protection-packages-container">
|
||||||
<!-- Dynamicznie z panelu WP (SOFT, PREMIUM) -->
|
<!-- Dynamicznie z panelu WP (SOFT, PREMIUM) -->
|
||||||
</div>
|
</div>
|
||||||
<div class="carei-form__protection-divider" aria-hidden="true"></div>
|
|
||||||
<div class="carei-form__row" id="carei-insurance-container">
|
|
||||||
<!-- Dynamicznie z API pricelist -->
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="carei-form__divider"><span>Opcje dodatkowe</span></div>
|
<div class="carei-form__divider"><span><?php esc_html_e( 'Opcje dodatkowe', 'carei-reservation' ); ?></span></div>
|
||||||
<div class="carei-form__section">
|
<div class="carei-form__section">
|
||||||
<div class="carei-form__row" id="carei-extras-container">
|
<div class="carei-form__row" id="carei-extras-container">
|
||||||
<!-- Dynamicznie z API pricelist -->
|
<!-- Dynamicznie z API pricelist -->
|
||||||
@@ -171,20 +167,20 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dane najemcy -->
|
<!-- Dane najemcy -->
|
||||||
<div class="carei-form__divider"><span>Dane najemcy</span></div>
|
<div class="carei-form__divider"><span><?php esc_html_e( 'Dane najemcy', 'carei-reservation' ); ?></span></div>
|
||||||
|
|
||||||
<div class="carei-form__section">
|
<div class="carei-form__section">
|
||||||
<div class="carei-form__row">
|
<div class="carei-form__row">
|
||||||
<div class="carei-form__field">
|
<div class="carei-form__field">
|
||||||
<div class="carei-form__float-wrap">
|
<div class="carei-form__float-wrap">
|
||||||
<input type="text" id="carei-firstname" name="firstName" class="carei-form__input carei-form__input--float" placeholder=" " required>
|
<input type="text" id="carei-firstname" name="firstName" class="carei-form__input carei-form__input--float" placeholder=" " required>
|
||||||
<label class="carei-form__float-label" for="carei-firstname">Imię</label>
|
<label class="carei-form__float-label" for="carei-firstname"><?php esc_html_e( 'Imię', 'carei-reservation' ); ?></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="carei-form__field">
|
<div class="carei-form__field">
|
||||||
<div class="carei-form__float-wrap">
|
<div class="carei-form__float-wrap">
|
||||||
<input type="text" id="carei-lastname" name="lastName" class="carei-form__input carei-form__input--float" placeholder=" " required>
|
<input type="text" id="carei-lastname" name="lastName" class="carei-form__input carei-form__input--float" placeholder=" " required>
|
||||||
<label class="carei-form__float-label" for="carei-lastname">Nazwisko</label>
|
<label class="carei-form__float-label" for="carei-lastname"><?php esc_html_e( 'Nazwisko', 'carei-reservation' ); ?></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -193,12 +189,12 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
<div class="carei-form__field">
|
<div class="carei-form__field">
|
||||||
<div class="carei-form__float-wrap">
|
<div class="carei-form__float-wrap">
|
||||||
<input type="email" id="carei-email" name="email" class="carei-form__input carei-form__input--float" placeholder=" " required>
|
<input type="email" id="carei-email" name="email" class="carei-form__input carei-form__input--float" placeholder=" " required>
|
||||||
<label class="carei-form__float-label" for="carei-email">Adres e-mail</label>
|
<label class="carei-form__float-label" for="carei-email"><?php esc_html_e( 'Adres e-mail', 'carei-reservation' ); ?></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="carei-form__field">
|
<div class="carei-form__field">
|
||||||
<div class="carei-form__phone-wrap">
|
<div class="carei-form__phone-wrap">
|
||||||
<label class="carei-form__phone-label" for="carei-phone">Nr telefonu</label>
|
<label class="carei-form__phone-label" for="carei-phone"><?php esc_html_e( 'Nr telefonu', 'carei-reservation' ); ?></label>
|
||||||
<div class="carei-form__phone-row">
|
<div class="carei-form__phone-row">
|
||||||
<div class="carei-form__phone-prefix">
|
<div class="carei-form__phone-prefix">
|
||||||
<span class="carei-form__phone-flag">🇵🇱</span>
|
<span class="carei-form__phone-flag">🇵🇱</span>
|
||||||
@@ -212,7 +208,7 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
|
|
||||||
|
|
||||||
<div class="carei-form__field carei-form__field--full">
|
<div class="carei-form__field carei-form__field--full">
|
||||||
<textarea id="carei-message" name="message" class="carei-form__textarea" placeholder="Twoja wiadomość dotycząca rezerwacji" rows="4"></textarea>
|
<textarea id="carei-message" name="message" class="carei-form__textarea" placeholder="<?php echo esc_attr__( 'Twoja wiadomość dotycząca rezerwacji', 'carei-reservation' ); ?>" rows="4"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -223,23 +219,23 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
<span class="carei-form__checkbox-box">
|
<span class="carei-form__checkbox-box">
|
||||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7l3.5 3.5L12 4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7l3.5 3.5L12 4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
</span>
|
</span>
|
||||||
<span class="carei-form__checkbox-text">Zgadzam się na <a href="/polityka-prywatnosci/" target="_blank">Politykę Prywatności</a></span>
|
<span class="carei-form__checkbox-text"><?php echo wp_kses( __( 'Zgadzam się na <a href="/polityka-prywatnosci/" target="_blank">Politykę Prywatności</a>', 'carei-reservation' ), array( 'a' => array( 'href' => array(), 'target' => array() ) ) ); ?></span>
|
||||||
</label>
|
</label>
|
||||||
<button type="submit" class="carei-form__submit" aria-busy="false">
|
<button type="submit" class="carei-form__submit" aria-busy="false">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
||||||
Wyślij
|
<?php esc_html_e( 'Wyślij', 'carei-reservation' ); ?>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="carei-form__error-summary" id="carei-error-summary" style="display:none;">
|
<div class="carei-form__error-summary" id="carei-error-summary" style="display:none;">
|
||||||
Uzupełnij wymagane pola zaznaczone na czerwono.
|
<?php esc_html_e( 'Uzupełnij wymagane pola zaznaczone na czerwono.', 'carei-reservation' ); ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- Podsumowanie kosztów (po submit) -->
|
<!-- Podsumowanie kosztów (po submit) -->
|
||||||
<div id="carei-summary-overlay" class="carei-summary" style="display:none;">
|
<div id="carei-summary-overlay" class="carei-summary" style="display:none;">
|
||||||
<h3 class="carei-summary__title" tabindex="-1">Podsumowanie rezerwacji<span>.</span></h3>
|
<h3 class="carei-summary__title" tabindex="-1"><?php esc_html_e( 'Podsumowanie rezerwacji', 'carei-reservation' ); ?><span>.</span></h3>
|
||||||
<div class="carei-summary__details" id="carei-summary-details"></div>
|
<div class="carei-summary__details" id="carei-summary-details"></div>
|
||||||
<div class="carei-summary__table" id="carei-summary-table"></div>
|
<div class="carei-summary__table" id="carei-summary-table"></div>
|
||||||
<div class="carei-summary__total" id="carei-summary-total"></div>
|
<div class="carei-summary__total" id="carei-summary-total"></div>
|
||||||
@@ -247,11 +243,11 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
<div class="carei-summary__actions">
|
<div class="carei-summary__actions">
|
||||||
<button type="button" class="carei-summary__btn carei-summary__btn--back" id="carei-summary-back">
|
<button type="button" class="carei-summary__btn carei-summary__btn--back" id="carei-summary-back">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M19 12H5M12 19l-7-7 7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M19 12H5M12 19l-7-7 7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
Wróć do formularza
|
<?php esc_html_e( 'Wróć do formularza', 'carei-reservation' ); ?>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="carei-summary__btn carei-summary__btn--confirm" id="carei-summary-confirm" aria-busy="false">
|
<button type="button" class="carei-summary__btn carei-summary__btn--confirm" id="carei-summary-confirm" aria-busy="false">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
||||||
Potwierdź rezerwację
|
<?php esc_html_e( 'Potwierdź rezerwację', 'carei-reservation' ); ?>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -261,10 +257,10 @@ class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
|||||||
<div class="carei-success__icon">
|
<div class="carei-success__icon">
|
||||||
<svg width="40" height="40" viewBox="0 0 24 24" fill="none"><path d="M20 6L9 17l-5-5" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg width="40" height="40" viewBox="0 0 24 24" fill="none"><path d="M20 6L9 17l-5-5" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="carei-success__title" tabindex="-1">Zamówienie złożone!</h3>
|
<h3 class="carei-success__title" tabindex="-1"><?php esc_html_e( 'Zamówienie złożone!', 'carei-reservation' ); ?></h3>
|
||||||
<p class="carei-success__number" id="carei-success-number"></p>
|
<p class="carei-success__number" id="carei-success-number"></p>
|
||||||
<p class="carei-success__message">Oczekuj na kontakt z wypożyczalnią</p>
|
<p class="carei-success__message"><?php esc_html_e( 'Oczekuj na kontakt z wypożyczalnią', 'carei-reservation' ); ?></p>
|
||||||
<button type="button" class="carei-success__close" id="carei-success-close">Zamknij</button>
|
<button type="button" class="carei-success__close" id="carei-success-close"><?php esc_html_e( 'Zamknij', 'carei-reservation' ); ?></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div><!-- .carei-modal__scroll -->
|
</div><!-- .carei-modal__scroll -->
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class Carei_Map_Widget extends \Elementor\Widget_Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function get_title() {
|
public function get_title() {
|
||||||
return 'Carei Map';
|
return esc_html__( 'Carei Map', 'carei-reservation' );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_icon() {
|
public function get_icon() {
|
||||||
@@ -169,11 +169,11 @@ class Carei_Map_Widget extends \Elementor\Widget_Base {
|
|||||||
$api_city = isset( $b['city'] ) ? trim( $b['city'] ) : '';
|
$api_city = isset( $b['city'] ) ? trim( $b['city'] ) : '';
|
||||||
$api_city_title = mb_convert_case( $api_city, MB_CASE_TITLE, 'UTF-8' );
|
$api_city_title = mb_convert_case( $api_city, MB_CASE_TITLE, 'UTF-8' );
|
||||||
|
|
||||||
$address_lines = array( 'Oddział ' . $display_city );
|
$address_lines = array( sprintf( /* translators: %s: city name */ __( 'Oddział %s', 'carei-reservation' ), $display_city ) );
|
||||||
if ( $street ) {
|
if ( $street ) {
|
||||||
$street_lower = mb_strtolower( $street, 'UTF-8' );
|
$street_lower = mb_strtolower( $street, 'UTF-8' );
|
||||||
$has_prefix = preg_match( '/^(ul\.|al\.|pl\.|os\.)/u', $street_lower );
|
$has_prefix = preg_match( '/^(ul\.|al\.|pl\.|os\.)/u', $street_lower );
|
||||||
$address_lines[] = $has_prefix ? $street : 'ul. ' . $street;
|
$address_lines[] = $has_prefix ? $street : sprintf( /* translators: %s: street name */ __( 'ul. %s', 'carei-reservation' ), $street );
|
||||||
}
|
}
|
||||||
$zip_city = trim( $zip . ' ' . $api_city_title );
|
$zip_city = trim( $zip . ' ' . $api_city_title );
|
||||||
if ( $zip_city ) {
|
if ( $zip_city ) {
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ class Carei_REST_Proxy {
|
|||||||
public function check_nonce( WP_REST_Request $request ) {
|
public function check_nonce( WP_REST_Request $request ) {
|
||||||
$nonce = $request->get_header( 'X-WP-Nonce' );
|
$nonce = $request->get_header( 'X-WP-Nonce' );
|
||||||
if ( ! $nonce || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
|
if ( ! $nonce || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
|
||||||
return new WP_Error( 'rest_forbidden', 'Invalid nonce.', array( 'status' => 403 ) );
|
return new WP_Error( 'rest_forbidden', __( 'Invalid nonce.', 'carei-reservation' ), array( 'status' => 403 ) );
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,7 @@ class Carei_REST_Proxy {
|
|||||||
private function api() {
|
private function api() {
|
||||||
$api = Carei_Softra_API::get_instance();
|
$api = Carei_Softra_API::get_instance();
|
||||||
if ( null === $api ) {
|
if ( null === $api ) {
|
||||||
return new WP_Error( 'carei_not_configured', 'Softra API not configured.', array( 'status' => 500 ) );
|
return new WP_Error( 'carei_not_configured', __( 'Softra API not configured.', 'carei-reservation' ), array( 'status' => 500 ) );
|
||||||
}
|
}
|
||||||
return $api;
|
return $api;
|
||||||
}
|
}
|
||||||
@@ -227,12 +227,34 @@ class Carei_REST_Proxy {
|
|||||||
if ( is_wp_error( $api ) ) {
|
if ( is_wp_error( $api ) ) {
|
||||||
return $api;
|
return $api;
|
||||||
}
|
}
|
||||||
return $this->respond( $api->get_pricelist(
|
$pricelists = $api->get_pricelist(
|
||||||
$request->get_param( 'category' ),
|
$request->get_param( 'category' ),
|
||||||
$request->get_param( 'dateFrom' ),
|
$request->get_param( 'dateFrom' ),
|
||||||
$request->get_param( 'dateTo' ),
|
$request->get_param( 'dateTo' ),
|
||||||
$request->get_param( 'pickUpLocation' )
|
$request->get_param( 'pickUpLocation' )
|
||||||
) );
|
);
|
||||||
|
|
||||||
|
// Auto-collect PL extra names + per-locale translate (Phase 19).
|
||||||
|
if ( is_array( $pricelists ) ) {
|
||||||
|
$locale = $this->resolve_locale( $request );
|
||||||
|
$translations = Carei_Admin_Panel::get_extras_translations();
|
||||||
|
foreach ( $pricelists as &$pricelist ) {
|
||||||
|
if ( ! is_array( $pricelist ) || empty( $pricelist['additionalItems'] ) || ! is_array( $pricelist['additionalItems'] ) ) continue;
|
||||||
|
foreach ( $pricelist['additionalItems'] as &$item ) {
|
||||||
|
if ( ! is_array( $item ) || ! isset( $item['name'] ) || ! is_string( $item['name'] ) ) continue;
|
||||||
|
$pl_name = trim( $item['name'] );
|
||||||
|
if ( $pl_name === '' ) continue;
|
||||||
|
Carei_Admin_Panel::remember_extra_name( $pl_name );
|
||||||
|
if ( $locale === 'en' && isset( $translations[ $pl_name ] ) && $translations[ $pl_name ] !== '' ) {
|
||||||
|
$item['name'] = $translations[ $pl_name ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset( $item );
|
||||||
|
}
|
||||||
|
unset( $pricelist );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respond( $pricelists );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_pricing_summary( WP_REST_Request $request ) {
|
public function get_pricing_summary( WP_REST_Request $request ) {
|
||||||
@@ -307,18 +329,36 @@ class Carei_REST_Proxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function get_protection_packages( WP_REST_Request $request ) {
|
public function get_protection_packages( WP_REST_Request $request ) {
|
||||||
$all = Carei_Admin_Panel::get_protection_packages();
|
$all = Carei_Admin_Panel::get_protection_packages();
|
||||||
$out = array( 'soft' => null, 'premium' => null );
|
$locale = $this->resolve_locale( $request );
|
||||||
|
$out = array( 'soft' => null, 'premium' => null );
|
||||||
foreach ( array( 'soft', 'premium' ) as $key ) {
|
foreach ( array( 'soft', 'premium' ) as $key ) {
|
||||||
if ( isset( $all[ $key ] ) && ! empty( $all[ $key ]['active'] ) ) {
|
if ( isset( $all[ $key ] ) && ! empty( $all[ $key ]['active'] ) ) {
|
||||||
|
$pkg = $all[ $key ];
|
||||||
|
if ( 'en' === $locale ) {
|
||||||
|
$name = ! empty( $pkg['name_en'] ) ? $pkg['name_en'] : $pkg['name'];
|
||||||
|
$desc = ! empty( $pkg['description_en'] ) ? $pkg['description_en'] : $pkg['description'];
|
||||||
|
} else {
|
||||||
|
$name = $pkg['name'];
|
||||||
|
$desc = $pkg['description'];
|
||||||
|
}
|
||||||
$out[ $key ] = array(
|
$out[ $key ] = array(
|
||||||
'key' => $key,
|
'key' => $key,
|
||||||
'name' => $all[ $key ]['name'],
|
'name' => $name,
|
||||||
'pricePerDay' => (float) $all[ $key ]['pricePerDay'],
|
'pricePerDay' => (float) $pkg['pricePerDay'],
|
||||||
'description' => $all[ $key ]['description'],
|
'description' => $desc,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rest_ensure_response( $out );
|
return rest_ensure_response( $out );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function resolve_locale( WP_REST_Request $request ) {
|
||||||
|
$lang = $request->get_param( 'lang' );
|
||||||
|
if ( $lang && in_array( strtolower( $lang ), array( 'pl', 'en' ), true ) ) {
|
||||||
|
return strtolower( $lang );
|
||||||
|
}
|
||||||
|
$locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
|
||||||
|
return ( 0 === strpos( (string) $locale, 'en' ) ) ? 'en' : 'pl';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class Carei_Search_Widget extends \Elementor\Widget_Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function get_title() {
|
public function get_title() {
|
||||||
return 'Carei Search Form';
|
return esc_html__( 'Carei Search Form', 'carei-reservation' );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_icon() {
|
public function get_icon() {
|
||||||
@@ -37,14 +37,14 @@ class Carei_Search_Widget extends \Elementor\Widget_Base {
|
|||||||
protected function render() {
|
protected function render() {
|
||||||
?>
|
?>
|
||||||
<div class="carei-search-form">
|
<div class="carei-search-form">
|
||||||
<h2 class="carei-search-form__title">Wypełnij formularz rezerwacji<span>.</span></h2>
|
<h2 class="carei-search-form__title"><?php esc_html_e( 'Wypełnij formularz rezerwacji', 'carei-reservation' ); ?><span>.</span></h2>
|
||||||
|
|
||||||
<div class="carei-search-form__fields">
|
<div class="carei-search-form__fields">
|
||||||
<!-- Segment -->
|
<!-- Segment -->
|
||||||
<div class="carei-search-form__field carei-search-form__field--full">
|
<div class="carei-search-form__field carei-search-form__field--full">
|
||||||
<div class="carei-search-form__select-wrap">
|
<div class="carei-search-form__select-wrap">
|
||||||
<select id="carei-search-segment">
|
<select id="carei-search-segment">
|
||||||
<option value="" disabled selected>Wybierz segment</option>
|
<option value="" disabled selected><?php esc_html_e( 'Wybierz segment', 'carei-reservation' ); ?></option>
|
||||||
</select>
|
</select>
|
||||||
<svg class="carei-search-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
<svg class="carei-search-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,14 +55,14 @@ class Carei_Search_Widget extends \Elementor\Widget_Base {
|
|||||||
<div class="carei-search-form__field">
|
<div class="carei-search-form__field">
|
||||||
<div class="carei-search-form__date-wrap">
|
<div class="carei-search-form__date-wrap">
|
||||||
<svg class="carei-search-form__date-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3" width="11" height="10" rx="0.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path d="M11 1.5v2M5 1.5v2M2.5 5.5h11" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg class="carei-search-form__date-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3" width="11" height="10" rx="0.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path d="M11 1.5v2M5 1.5v2M2.5 5.5h11" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
<label class="carei-search-form__date-label" for="carei-search-date-from">Od kiedy?</label>
|
<label class="carei-search-form__date-label" for="carei-search-date-from"><?php esc_html_e( 'Od kiedy?', 'carei-reservation' ); ?></label>
|
||||||
<input type="datetime-local" id="carei-search-date-from" class="carei-search-form__input carei-search-form__input--date">
|
<input type="datetime-local" id="carei-search-date-from" class="carei-search-form__input carei-search-form__input--date">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="carei-search-form__field">
|
<div class="carei-search-form__field">
|
||||||
<div class="carei-search-form__date-wrap">
|
<div class="carei-search-form__date-wrap">
|
||||||
<svg class="carei-search-form__date-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3" width="11" height="10" rx="0.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path d="M11 1.5v2M5 1.5v2M2.5 5.5h11" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg class="carei-search-form__date-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3" width="11" height="10" rx="0.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path d="M11 1.5v2M5 1.5v2M2.5 5.5h11" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
<label class="carei-search-form__date-label" for="carei-search-date-to">Do kiedy?</label>
|
<label class="carei-search-form__date-label" for="carei-search-date-to"><?php esc_html_e( 'Do kiedy?', 'carei-reservation' ); ?></label>
|
||||||
<input type="datetime-local" id="carei-search-date-to" class="carei-search-form__input carei-search-form__input--date">
|
<input type="datetime-local" id="carei-search-date-to" class="carei-search-form__input carei-search-form__input--date">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -73,7 +73,7 @@ class Carei_Search_Widget extends \Elementor\Widget_Base {
|
|||||||
<div class="carei-search-form__select-wrap carei-search-form__select-wrap--icon">
|
<div class="carei-search-form__select-wrap carei-search-form__select-wrap--icon">
|
||||||
<svg class="carei-search-form__pin-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 1C5.24 1 3 3.24 3 6c0 3.75 5 9 5 9s5-5.25 5-9c0-2.76-2.24-5-5-5zm0 7a2 2 0 110-4 2 2 0 010 4z" fill="currentColor"/></svg>
|
<svg class="carei-search-form__pin-icon" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 1C5.24 1 3 3.24 3 6c0 3.75 5 9 5 9s5-5.25 5-9c0-2.76-2.24-5-5-5zm0 7a2 2 0 110-4 2 2 0 010 4z" fill="currentColor"/></svg>
|
||||||
<select id="carei-search-pickup">
|
<select id="carei-search-pickup">
|
||||||
<option value="" disabled selected>Miejsce odbioru</option>
|
<option value="" disabled selected><?php esc_html_e( 'Miejsce odbioru', 'carei-reservation' ); ?></option>
|
||||||
</select>
|
</select>
|
||||||
<svg class="carei-search-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
<svg class="carei-search-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -86,7 +86,7 @@ class Carei_Search_Widget extends \Elementor\Widget_Base {
|
|||||||
<span class="carei-search-form__checkbox-box">
|
<span class="carei-search-form__checkbox-box">
|
||||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7l3.5 3.5L12 4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7l3.5 3.5L12 4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
</span>
|
</span>
|
||||||
<span class="carei-search-form__checkbox-text">Zwrot w tej samej lokalizacji</span>
|
<span class="carei-search-form__checkbox-text"><?php esc_html_e( 'Zwrot w tej samej lokalizacji', 'carei-reservation' ); ?></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -94,7 +94,7 @@ class Carei_Search_Widget extends \Elementor\Widget_Base {
|
|||||||
<!-- Przycisk -->
|
<!-- Przycisk -->
|
||||||
<button type="button" class="carei-search-form__submit" id="carei-search-submit">
|
<button type="button" class="carei-search-form__submit" id="carei-search-submit">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
||||||
Złóż zapytanie o rezerwację
|
<?php esc_html_e( 'Złóż zapytanie o rezerwację', 'carei-reservation' ); ?>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
|
|||||||
@@ -137,9 +137,12 @@ class Carei_Softra_API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( $res['status'] < 200 || $res['status'] >= 300 ) {
|
if ( $res['status'] < 200 || $res['status'] >= 300 ) {
|
||||||
|
$raw_msg = self::extract_softra_message( $res['body'] );
|
||||||
|
$mapped = self::map_error_message( $raw_msg );
|
||||||
|
$message = $mapped !== '' ? $mapped : sprintf( 'Softra API error: HTTP %d', $res['status'] );
|
||||||
return new WP_Error(
|
return new WP_Error(
|
||||||
'carei_api_error',
|
'carei_api_error',
|
||||||
'Softra API error: HTTP ' . $res['status'],
|
$message,
|
||||||
array( 'status' => $res['status'], 'body' => $res['body'] )
|
array( 'status' => $res['status'], 'body' => $res['body'] )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -147,6 +150,69 @@ class Carei_Softra_API {
|
|||||||
return $res['body'];
|
return $res['body'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract human-readable message from Softra response body.
|
||||||
|
* Softra returns either JSON with `message` / `error` / `details` field, or raw string.
|
||||||
|
*/
|
||||||
|
public static function extract_softra_message( $body ) {
|
||||||
|
if ( is_array( $body ) ) {
|
||||||
|
foreach ( array( 'message', 'error', 'details', 'description' ) as $field ) {
|
||||||
|
if ( ! empty( $body[ $field ] ) && is_string( $body[ $field ] ) ) {
|
||||||
|
return trim( $body[ $field ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if ( is_string( $body ) && $body !== '' ) {
|
||||||
|
$decoded = json_decode( $body, true );
|
||||||
|
if ( is_array( $decoded ) ) {
|
||||||
|
return self::extract_softra_message( $decoded );
|
||||||
|
}
|
||||||
|
return trim( $body );
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map typical Softra PL error messages to localized strings.
|
||||||
|
* Exact match first, then fuzzy prefix match. Unknown messages passthrough.
|
||||||
|
*/
|
||||||
|
public static function map_error_message( $original_message ) {
|
||||||
|
if ( ! is_string( $original_message ) || '' === trim( $original_message ) ) {
|
||||||
|
return $original_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dict = array(
|
||||||
|
'Brak dostępnego pojazdu w wybranym terminie' => __( 'Brak dostępnego pojazdu w wybranym terminie. Zmień daty lub segment.', 'carei-reservation' ),
|
||||||
|
'Nieprawidłowy zakres dat' => __( 'Nieprawidłowy zakres dat', 'carei-reservation' ),
|
||||||
|
'Nie znaleziono oddziału' => __( 'Nie znaleziono oddziału', 'carei-reservation' ),
|
||||||
|
'Klient o tych danych już istnieje' => __( 'Klient o tych danych już istnieje w systemie', 'carei-reservation' ),
|
||||||
|
'Nieprawidłowy numer PESEL' => __( 'Nieprawidłowy numer PESEL', 'carei-reservation' ),
|
||||||
|
'Cennik wygasł' => __( 'Cennik wygasł. Odśwież formularz i spróbuj ponownie.', 'carei-reservation' ),
|
||||||
|
'Token wygasł' => __( 'Sesja wygasła. Odśwież stronę.', 'carei-reservation' ),
|
||||||
|
'Nieprawidłowe dane logowania' => __( 'Błąd autoryzacji API. Skontaktuj się z administratorem.', 'carei-reservation' ),
|
||||||
|
'Brak uprawnień' => __( 'Brak uprawnień do wykonania operacji.', 'carei-reservation' ),
|
||||||
|
'Błąd serwera' => __( 'Błąd serwera. Spróbuj ponownie za chwilę.', 'carei-reservation' ),
|
||||||
|
'Przekroczono limit rezerwacji' => __( 'Przekroczono limit rezerwacji dla tego klienta.', 'carei-reservation' ),
|
||||||
|
'Nieprawidłowy numer telefonu' => __( 'Podaj poprawny numer telefonu (min. 9 cyfr).', 'carei-reservation' ),
|
||||||
|
'Wymagane pole' => __( 'To pole jest wymagane.', 'carei-reservation' ),
|
||||||
|
);
|
||||||
|
|
||||||
|
$trimmed = trim( $original_message );
|
||||||
|
|
||||||
|
if ( isset( $dict[ $trimmed ] ) ) {
|
||||||
|
return $dict[ $trimmed ];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( $dict as $pl_key => $translated ) {
|
||||||
|
if ( 0 === stripos( $trimmed, $pl_key ) ) {
|
||||||
|
return $translated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $original_message;
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Public API Methods ───────────────────────────────────────
|
// ─── Public API Methods ───────────────────────────────────────
|
||||||
|
|
||||||
public function get_branches() {
|
public function get_branches() {
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,659 @@
|
|||||||
|
# Copyright (C) 2026 Carei
|
||||||
|
# This file is distributed under the same license as the Carei Reservation plugin.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Carei Reservation 1.0.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2026-04-22 12:00+0000\n"
|
||||||
|
"PO-Revision-Date: 2026-04-22 12:00+0000\n"
|
||||||
|
"Last-Translator: Carei\n"
|
||||||
|
"Language-Team: English\n"
|
||||||
|
"Language: en_GB\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Domain: carei-reservation\n"
|
||||||
|
|
||||||
|
#: carei-reservation.php:133
|
||||||
|
msgid "Błąd API: HTTP %status%"
|
||||||
|
msgstr "API error: HTTP %status%"
|
||||||
|
|
||||||
|
#: carei-reservation.php:134
|
||||||
|
msgid "Przekroczono czas oczekiwania. Spróbuj ponownie."
|
||||||
|
msgstr "Request timed out. Please try again."
|
||||||
|
|
||||||
|
#: carei-reservation.php:135
|
||||||
|
msgid "Brak połączenia z serwerem. Sprawdź internet i spróbuj ponownie."
|
||||||
|
msgstr "No server connection. Check your internet and try again."
|
||||||
|
|
||||||
|
#: carei-reservation.php:136
|
||||||
|
msgid "Błąd ładowania"
|
||||||
|
msgstr "Loading error"
|
||||||
|
|
||||||
|
#: carei-reservation.php:139
|
||||||
|
msgid "Wybierz..."
|
||||||
|
msgstr "Select..."
|
||||||
|
|
||||||
|
#: carei-reservation.php:140 includes/class-elementor-widget.php:77
|
||||||
|
msgid "Wybierz segment pojazdu"
|
||||||
|
msgstr "Select a vehicle segment"
|
||||||
|
|
||||||
|
#: carei-reservation.php:141 includes/class-search-widget.php:47
|
||||||
|
msgid "Wybierz segment"
|
||||||
|
msgstr "Select segment"
|
||||||
|
|
||||||
|
#: carei-reservation.php:142
|
||||||
|
msgid "Wybierz miejsce odbioru"
|
||||||
|
msgstr "Select pickup location"
|
||||||
|
|
||||||
|
#: carei-reservation.php:143
|
||||||
|
msgid "Wybierz miejsce zwrotu"
|
||||||
|
msgstr "Select return location"
|
||||||
|
|
||||||
|
#: carei-reservation.php:144
|
||||||
|
msgid "Segment %name%"
|
||||||
|
msgstr "Segment %name%"
|
||||||
|
|
||||||
|
#: carei-reservation.php:145
|
||||||
|
msgid "Brak segmentów"
|
||||||
|
msgstr "No segments"
|
||||||
|
|
||||||
|
#: carei-reservation.php:146
|
||||||
|
msgid "Najpierw wybierz segment"
|
||||||
|
msgstr "Select segment first"
|
||||||
|
|
||||||
|
#: carei-reservation.php:147 carei-reservation.php:209
|
||||||
|
#: includes/class-elementor-widget.php:105
|
||||||
|
#: includes/class-search-widget.php:76
|
||||||
|
msgid "Miejsce odbioru"
|
||||||
|
msgstr "Pickup location"
|
||||||
|
|
||||||
|
#: carei-reservation.php:148 carei-reservation.php:210
|
||||||
|
#: includes/class-elementor-widget.php:123
|
||||||
|
msgid "Miejsce zwrotu"
|
||||||
|
msgstr "Return location"
|
||||||
|
|
||||||
|
#: carei-reservation.php:149
|
||||||
|
msgid "Brak lokalizacji dla tego segmentu"
|
||||||
|
msgstr "No locations for this segment"
|
||||||
|
|
||||||
|
#: carei-reservation.php:150
|
||||||
|
msgid "Brak lokalizacji"
|
||||||
|
msgstr "No locations"
|
||||||
|
|
||||||
|
#: carei-reservation.php:153
|
||||||
|
msgid "Rozpoczęcie"
|
||||||
|
msgstr "Start"
|
||||||
|
|
||||||
|
#: carei-reservation.php:154
|
||||||
|
msgid "Zakończenie"
|
||||||
|
msgstr "End"
|
||||||
|
|
||||||
|
#: carei-reservation.php:155
|
||||||
|
msgid "%label% — data lub godzina już minęły"
|
||||||
|
msgstr "%label% — date or time has already passed"
|
||||||
|
|
||||||
|
#: carei-reservation.php:156
|
||||||
|
msgid "Data lub godzina rozpoczęcia już minęły"
|
||||||
|
msgstr "Start date or time has already passed"
|
||||||
|
|
||||||
|
#: carei-reservation.php:157
|
||||||
|
msgid "Data lub godzina zakończenia już minęły"
|
||||||
|
msgstr "End date or time has already passed"
|
||||||
|
|
||||||
|
#: carei-reservation.php:158
|
||||||
|
msgid "Data zakończenia musi być po dacie rozpoczęcia"
|
||||||
|
msgstr "End date must be after start date"
|
||||||
|
|
||||||
|
#: carei-reservation.php:159
|
||||||
|
msgid "Podaj datę rozpoczęcia"
|
||||||
|
msgstr "Enter start date"
|
||||||
|
|
||||||
|
#: carei-reservation.php:160
|
||||||
|
msgid "Podaj datę zakończenia"
|
||||||
|
msgstr "Enter end date"
|
||||||
|
|
||||||
|
#: carei-reservation.php:163
|
||||||
|
msgid "Wybrano: <strong>%count% %unit%</strong>"
|
||||||
|
msgstr "Selected: <strong>%count% %unit%</strong>"
|
||||||
|
|
||||||
|
#: carei-reservation.php:164
|
||||||
|
msgid "dzień"
|
||||||
|
msgstr "day"
|
||||||
|
|
||||||
|
#: carei-reservation.php:165
|
||||||
|
msgid "dni"
|
||||||
|
msgstr "days"
|
||||||
|
|
||||||
|
#: carei-reservation.php:166
|
||||||
|
msgid "doba"
|
||||||
|
msgstr "day"
|
||||||
|
|
||||||
|
#: carei-reservation.php:167
|
||||||
|
msgid "doby"
|
||||||
|
msgstr "days"
|
||||||
|
|
||||||
|
#: carei-reservation.php:168
|
||||||
|
msgid "dób"
|
||||||
|
msgstr "days"
|
||||||
|
|
||||||
|
#: carei-reservation.php:171
|
||||||
|
msgid "%price% zł/doba"
|
||||||
|
msgstr "%price% PLN/day"
|
||||||
|
|
||||||
|
#: carei-reservation.php:172 carei-reservation.php:173
|
||||||
|
msgid "%price% zł"
|
||||||
|
msgstr "%price% PLN"
|
||||||
|
|
||||||
|
#: carei-reservation.php:174
|
||||||
|
msgid "od %min% do %max% zł"
|
||||||
|
msgstr "from %min% to %max% PLN"
|
||||||
|
|
||||||
|
#: carei-reservation.php:175
|
||||||
|
msgid "%perDay% zł/doba × %days% = %total% zł"
|
||||||
|
msgstr "%perDay% PLN/day × %days% = %total% PLN"
|
||||||
|
|
||||||
|
#: carei-reservation.php:176
|
||||||
|
msgid "Gratis"
|
||||||
|
msgstr "Free"
|
||||||
|
|
||||||
|
#: carei-reservation.php:179
|
||||||
|
msgid "Usuń"
|
||||||
|
msgstr "Remove"
|
||||||
|
|
||||||
|
#: carei-reservation.php:180
|
||||||
|
msgid "Dodaj"
|
||||||
|
msgstr "Add"
|
||||||
|
|
||||||
|
#: carei-reservation.php:183
|
||||||
|
msgid "Podaj imię"
|
||||||
|
msgstr "Enter first name"
|
||||||
|
|
||||||
|
#: carei-reservation.php:184
|
||||||
|
msgid "Podaj nazwisko"
|
||||||
|
msgstr "Enter last name"
|
||||||
|
|
||||||
|
#: carei-reservation.php:185
|
||||||
|
msgid "Podaj poprawny adres e-mail"
|
||||||
|
msgstr "Enter a valid email address"
|
||||||
|
|
||||||
|
#: carei-reservation.php:186
|
||||||
|
msgid "Podaj numer telefonu (min. 9 cyfr)"
|
||||||
|
msgstr "Enter phone number (min. 9 digits)"
|
||||||
|
|
||||||
|
#: carei-reservation.php:187
|
||||||
|
msgid "Wymagana zgoda na Politykę Prywatności"
|
||||||
|
msgstr "Privacy policy consent is required"
|
||||||
|
|
||||||
|
#: carei-reservation.php:190 includes/class-elementor-widget.php:226
|
||||||
|
msgid "Wyślij"
|
||||||
|
msgstr "Send"
|
||||||
|
|
||||||
|
#: carei-reservation.php:191
|
||||||
|
msgid "Przetwarzanie..."
|
||||||
|
msgstr "Processing..."
|
||||||
|
|
||||||
|
#: carei-reservation.php:192
|
||||||
|
msgid "Rezerwuję..."
|
||||||
|
msgstr "Reserving..."
|
||||||
|
|
||||||
|
#: carei-reservation.php:193 includes/class-elementor-widget.php:250
|
||||||
|
msgid "Potwierdź rezerwację"
|
||||||
|
msgstr "Confirm reservation"
|
||||||
|
|
||||||
|
#: carei-reservation.php:196
|
||||||
|
msgid "Ładowanie podsumowania..."
|
||||||
|
msgstr "Loading summary..."
|
||||||
|
|
||||||
|
#: carei-reservation.php:197
|
||||||
|
msgid "Nie udało się utworzyć klienta"
|
||||||
|
msgstr "Failed to create customer"
|
||||||
|
|
||||||
|
#: carei-reservation.php:198
|
||||||
|
msgid "Błąd tworzenia klienta: %msg%"
|
||||||
|
msgstr "Customer creation error: %msg%"
|
||||||
|
|
||||||
|
#: carei-reservation.php:199
|
||||||
|
msgid "Błąd pobierania podsumowania: %msg%"
|
||||||
|
msgstr "Error loading summary: %msg%"
|
||||||
|
|
||||||
|
#: carei-reservation.php:200
|
||||||
|
msgid "Rezerwacja nie powiodła się"
|
||||||
|
msgstr "Reservation failed"
|
||||||
|
|
||||||
|
#: carei-reservation.php:203
|
||||||
|
msgid "Pakiet ochronny: %name% — %perDay% zł/doba × %days% = %total% zł (do doliczenia poza systemem)"
|
||||||
|
msgstr "Protection package: %name% — %perDay% PLN/day × %days% = %total% PLN (to be paid separately)"
|
||||||
|
|
||||||
|
#: carei-reservation.php:206 includes/class-admin-panel.php:219
|
||||||
|
#: includes/class-admin-panel.php:399
|
||||||
|
msgid "Segment"
|
||||||
|
msgstr "Segment"
|
||||||
|
|
||||||
|
#: carei-reservation.php:207
|
||||||
|
msgid "Od"
|
||||||
|
msgstr "From"
|
||||||
|
|
||||||
|
#: carei-reservation.php:208
|
||||||
|
msgid "Do"
|
||||||
|
msgstr "To"
|
||||||
|
|
||||||
|
#: carei-reservation.php:211
|
||||||
|
msgid "Najemca"
|
||||||
|
msgstr "Renter"
|
||||||
|
|
||||||
|
#: carei-reservation.php:212 includes/class-admin-panel.php:407
|
||||||
|
msgid "Email"
|
||||||
|
msgstr "Email"
|
||||||
|
|
||||||
|
#: carei-reservation.php:213 includes/class-admin-panel.php:408
|
||||||
|
msgid "Telefon"
|
||||||
|
msgstr "Phone"
|
||||||
|
|
||||||
|
#: carei-reservation.php:214
|
||||||
|
msgid "Wybrane opcje"
|
||||||
|
msgstr "Selected options"
|
||||||
|
|
||||||
|
#: carei-reservation.php:215 includes/class-admin-panel.php:414
|
||||||
|
msgid "Wiadomość"
|
||||||
|
msgstr "Message"
|
||||||
|
|
||||||
|
#: carei-reservation.php:216
|
||||||
|
msgid "auto"
|
||||||
|
msgstr "car"
|
||||||
|
|
||||||
|
#: carei-reservation.php:217
|
||||||
|
msgid "do doliczenia"
|
||||||
|
msgstr "to be added"
|
||||||
|
|
||||||
|
#: carei-reservation.php:218
|
||||||
|
msgid "VAT"
|
||||||
|
msgstr "VAT"
|
||||||
|
|
||||||
|
#: carei-reservation.php:219 includes/class-admin-panel.php:413
|
||||||
|
msgid "Pakiet ochronny"
|
||||||
|
msgstr "Protection package"
|
||||||
|
|
||||||
|
#: carei-reservation.php:220
|
||||||
|
msgid "Do zapłaty"
|
||||||
|
msgstr "Total due"
|
||||||
|
|
||||||
|
#: carei-reservation.php:223
|
||||||
|
msgid "Nazwa"
|
||||||
|
msgstr "Name"
|
||||||
|
|
||||||
|
#: carei-reservation.php:224
|
||||||
|
msgid "Ilość"
|
||||||
|
msgstr "Quantity"
|
||||||
|
|
||||||
|
#: carei-reservation.php:225
|
||||||
|
msgid "Netto"
|
||||||
|
msgstr "Net"
|
||||||
|
|
||||||
|
#: carei-reservation.php:226
|
||||||
|
msgid "Brutto"
|
||||||
|
msgstr "Gross"
|
||||||
|
|
||||||
|
#: carei-reservation.php:229
|
||||||
|
msgid "Brak dostępnego pojazdu w wybranym terminie. Zmień daty lub segment."
|
||||||
|
msgstr "No vehicle available for the selected dates. Change dates or segment."
|
||||||
|
|
||||||
|
#: carei-reservation.php:230
|
||||||
|
msgid "Nieprawidłowy zakres dat"
|
||||||
|
msgstr "Invalid date range"
|
||||||
|
|
||||||
|
#: carei-reservation.php:231
|
||||||
|
msgid "Nie znaleziono oddziału"
|
||||||
|
msgstr "Location not found"
|
||||||
|
|
||||||
|
#: carei-reservation.php:232
|
||||||
|
msgid "Klient o tych danych już istnieje w systemie"
|
||||||
|
msgstr "A customer with these details already exists in the system"
|
||||||
|
|
||||||
|
#: carei-reservation.php:233
|
||||||
|
msgid "Nieprawidłowy numer PESEL"
|
||||||
|
msgstr "Invalid PESEL number"
|
||||||
|
|
||||||
|
#: carei-reservation.php:234
|
||||||
|
msgid "Cennik wygasł. Odśwież formularz i spróbuj ponownie."
|
||||||
|
msgstr "Price list expired. Refresh the form and try again."
|
||||||
|
|
||||||
|
#: carei-reservation.php:237
|
||||||
|
msgid "Nr zamówienia: %no%"
|
||||||
|
msgstr "Order number: %no%"
|
||||||
|
|
||||||
|
#: carei-reservation.php:238
|
||||||
|
msgid "Rezerwacja potwierdzona"
|
||||||
|
msgstr "Reservation confirmed"
|
||||||
|
|
||||||
|
#: carei-reservation.php:239 includes/class-elementor-widget.php:238
|
||||||
|
msgid "Podsumowanie rezerwacji"
|
||||||
|
msgstr "Reservation summary"
|
||||||
|
|
||||||
|
#: carei-reservation.php:74
|
||||||
|
msgid "Brak konfiguracji API w pliku .env (url, username, password)."
|
||||||
|
msgstr "Missing API configuration in .env file (url, username, password)."
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:103
|
||||||
|
msgid "Konfiguracja pakietów wyświetlanych w sekcji <strong>Pakiety ochronne</strong> formularza rezerwacji. Cena podawana jest za dobę — total = cena × liczba dób rezerwacji."
|
||||||
|
msgstr "Configuration of packages displayed in the <strong>Protection packages</strong> section of the reservation form. Price is per day — total = price × number of rental days."
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:105
|
||||||
|
msgid "Zapisano."
|
||||||
|
msgstr "Saved."
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:115
|
||||||
|
msgid "Pakiet %s"
|
||||||
|
msgstr "%s Package"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:118
|
||||||
|
msgid "Nazwa wyświetlana"
|
||||||
|
msgstr "Display name"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:122
|
||||||
|
msgid "Cena za dobę (zł)"
|
||||||
|
msgstr "Price per day (PLN)"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:126 includes/class-admin-panel.php:222
|
||||||
|
#: includes/class-admin-panel.php:417
|
||||||
|
msgid "Status"
|
||||||
|
msgstr "Status"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:127
|
||||||
|
msgid "Aktywny (widoczny w modalu)"
|
||||||
|
msgstr "Active (visible in modal)"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:130
|
||||||
|
msgid "Opis / zakres usług"
|
||||||
|
msgstr "Description / service scope"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:137
|
||||||
|
msgid "Zapisz pakiety"
|
||||||
|
msgstr "Save packages"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:152
|
||||||
|
msgid "Nieprawidłowy token."
|
||||||
|
msgstr "Invalid token."
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:189 includes/class-admin-panel.php:191
|
||||||
|
msgid "Rezerwacje"
|
||||||
|
msgstr "Reservations"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:190
|
||||||
|
msgid "Rezerwacja"
|
||||||
|
msgstr "Reservation"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:192
|
||||||
|
msgid "Wszystkie rezerwacje"
|
||||||
|
msgstr "All reservations"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:193
|
||||||
|
msgid "Zobacz rezerwację"
|
||||||
|
msgstr "View reservation"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:194 includes/class-admin-panel.php:321
|
||||||
|
msgid "Szczegóły rezerwacji"
|
||||||
|
msgstr "Reservation details"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:195
|
||||||
|
msgid "Szukaj rezerwacji"
|
||||||
|
msgstr "Search reservations"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:196
|
||||||
|
msgid "Nie znaleziono rezerwacji"
|
||||||
|
msgstr "No reservations found"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:197
|
||||||
|
msgid "Brak rezerwacji w koszu"
|
||||||
|
msgstr "No reservations in trash"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:217 includes/class-admin-panel.php:395
|
||||||
|
msgid "Nr rezerwacji"
|
||||||
|
msgstr "Reservation No."
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:218
|
||||||
|
msgid "Klient"
|
||||||
|
msgstr "Customer"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:22
|
||||||
|
msgid "Nowe"
|
||||||
|
msgstr "New"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:220
|
||||||
|
msgid "Daty"
|
||||||
|
msgstr "Dates"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:221
|
||||||
|
msgid "Oddział"
|
||||||
|
msgstr "Location"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:223
|
||||||
|
msgid "Data"
|
||||||
|
msgstr "Date"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:24
|
||||||
|
msgid "Przeczytane"
|
||||||
|
msgstr "Read"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:26
|
||||||
|
msgid "Zrealizowane"
|
||||||
|
msgstr "Completed"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:279
|
||||||
|
msgid "Wszystkie statusy"
|
||||||
|
msgstr "All statuses"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:362
|
||||||
|
msgid "%1$s — %2$s zł/doba × %3$d = %4$s zł"
|
||||||
|
msgstr "%1$s — %2$s PLN/day × %3$d = %4$s PLN"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:396
|
||||||
|
msgid "ID rezerwacji (Softra)"
|
||||||
|
msgstr "Reservation ID (Softra)"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:397
|
||||||
|
msgid "ID klienta (Softra)"
|
||||||
|
msgstr "Customer ID (Softra)"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:400
|
||||||
|
msgid "Data od"
|
||||||
|
msgstr "Date from"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:401
|
||||||
|
msgid "Data do"
|
||||||
|
msgstr "Date to"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:402
|
||||||
|
msgid "Oddział odbioru"
|
||||||
|
msgstr "Pickup location"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:403
|
||||||
|
msgid "Oddział zwrotu"
|
||||||
|
msgstr "Return location"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:409
|
||||||
|
msgid "PESEL"
|
||||||
|
msgstr "PESEL"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:410
|
||||||
|
msgid "Adres"
|
||||||
|
msgstr "Address"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:412 includes/class-admin-panel.php:413
|
||||||
|
msgid "Brak"
|
||||||
|
msgstr "None"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:50
|
||||||
|
msgid "Ubezpieczenie SOFT"
|
||||||
|
msgstr "SOFT Protection"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:537
|
||||||
|
msgid "Rezerwacja #%1$s — %2$s %3$s"
|
||||||
|
msgstr "Reservation #%1$s — %2$s %3$s"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:56
|
||||||
|
msgid "Ubezpieczenie PREMIUM"
|
||||||
|
msgstr "PREMIUM Protection"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:149 includes/class-admin-panel.php:96
|
||||||
|
msgid "Brak uprawnień."
|
||||||
|
msgstr "No permissions."
|
||||||
|
|
||||||
|
#: includes/class-branches-widget.php:23
|
||||||
|
msgid "Carei Branches"
|
||||||
|
msgstr "Carei Branches"
|
||||||
|
|
||||||
|
#: includes/class-cities-widget.php:16
|
||||||
|
msgid "Carei Cities"
|
||||||
|
msgstr "Carei Cities"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:115
|
||||||
|
#: includes/class-search-widget.php:89
|
||||||
|
msgid "Zwrot w tej samej lokalizacji"
|
||||||
|
msgstr "Return at the same location"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:133
|
||||||
|
msgid "Wyjazd zagraniczny"
|
||||||
|
msgstr "International travel"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:138
|
||||||
|
msgid "Wyjazd poza granicę Polski do:"
|
||||||
|
msgstr "Travel outside Poland to:"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:143
|
||||||
|
msgid "Wyszukaj i dodaj kraj na trasie"
|
||||||
|
msgstr "Search and add a country on the route"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:144
|
||||||
|
msgid "Wyczyść"
|
||||||
|
msgstr "Clear"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:102 includes/class-admin-panel.php:86
|
||||||
|
#: includes/class-admin-panel.php:87
|
||||||
|
#: includes/class-elementor-widget.php:153
|
||||||
|
msgid "Pakiety ochronne"
|
||||||
|
msgstr "Protection packages"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:16
|
||||||
|
msgid "Carei Reservation"
|
||||||
|
msgstr "Carei Reservation"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:412
|
||||||
|
#: includes/class-elementor-widget.php:160
|
||||||
|
msgid "Opcje dodatkowe"
|
||||||
|
msgstr "Additional options"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:170
|
||||||
|
msgid "Dane najemcy"
|
||||||
|
msgstr "Renter details"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:405
|
||||||
|
#: includes/class-elementor-widget.php:177
|
||||||
|
msgid "Imię"
|
||||||
|
msgstr "First name"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:406
|
||||||
|
#: includes/class-elementor-widget.php:183
|
||||||
|
msgid "Nazwisko"
|
||||||
|
msgstr "Last name"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:192
|
||||||
|
msgid "Adres e-mail"
|
||||||
|
msgstr "Email address"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:197
|
||||||
|
msgid "Nr telefonu"
|
||||||
|
msgstr "Phone number"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:211
|
||||||
|
msgid "Twoja wiadomość dotycząca rezerwacji"
|
||||||
|
msgstr "Your message regarding the reservation"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:222
|
||||||
|
msgid "Zgadzam się na <a href=\"/polityka-prywatnosci/\" target=\"_blank\">Politykę Prywatności</a>"
|
||||||
|
msgstr "I agree to the <a href=\"/polityka-prywatnosci/\" target=\"_blank\">Privacy Policy</a>"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:231
|
||||||
|
msgid "Uzupełnij wymagane pola zaznaczone na czerwono."
|
||||||
|
msgstr "Please complete the required fields marked in red."
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:246
|
||||||
|
msgid "Wróć do formularza"
|
||||||
|
msgstr "Back to form"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:260
|
||||||
|
msgid "Zamówienie złożone!"
|
||||||
|
msgstr "Order submitted!"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:262
|
||||||
|
msgid "Oczekuj na kontakt z wypożyczalnią"
|
||||||
|
msgstr "Please wait for the rental company to contact you"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:263
|
||||||
|
msgid "Zamknij"
|
||||||
|
msgstr "Close"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:37
|
||||||
|
msgid "Przycisk rezerwacji"
|
||||||
|
msgstr "Reservation button"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:42
|
||||||
|
msgid "Tekst przycisku"
|
||||||
|
msgstr "Button text"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:44
|
||||||
|
#: includes/class-search-widget.php:97
|
||||||
|
msgid "Złóż zapytanie o rezerwację"
|
||||||
|
msgstr "Request a reservation"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:62
|
||||||
|
msgid "Zamknij formularz"
|
||||||
|
msgstr "Close form"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:67
|
||||||
|
#: includes/class-search-widget.php:40
|
||||||
|
msgid "Wypełnij formularz rezerwacji"
|
||||||
|
msgstr "Fill out the reservation form"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:85
|
||||||
|
#: includes/class-search-widget.php:58
|
||||||
|
msgid "Od kiedy?"
|
||||||
|
msgstr "From?"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:92
|
||||||
|
#: includes/class-search-widget.php:65
|
||||||
|
msgid "Do kiedy?"
|
||||||
|
msgstr "Until?"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:98
|
||||||
|
msgid "Wybrano:"
|
||||||
|
msgstr "Selected:"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:98
|
||||||
|
msgid "0 dni"
|
||||||
|
msgstr "0 days"
|
||||||
|
|
||||||
|
#: includes/class-branches-widget.php:99 includes/class-map-widget.php:172
|
||||||
|
msgid "Oddział %s"
|
||||||
|
msgstr "Location %s"
|
||||||
|
|
||||||
|
#: includes/class-branches-widget.php:95 includes/class-map-widget.php:176
|
||||||
|
msgid "ul. %s"
|
||||||
|
msgstr "%s St."
|
||||||
|
|
||||||
|
#: includes/class-map-widget.php:66
|
||||||
|
msgid "Carei Map"
|
||||||
|
msgstr "Carei Map"
|
||||||
|
|
||||||
|
#: includes/class-rest-proxy.php:148
|
||||||
|
msgid "Invalid nonce."
|
||||||
|
msgstr "Invalid nonce."
|
||||||
|
|
||||||
|
#: includes/class-rest-proxy.php:159
|
||||||
|
msgid "Softra API not configured."
|
||||||
|
msgstr "Softra API not configured."
|
||||||
|
|
||||||
|
#: includes/class-search-widget.php:16
|
||||||
|
msgid "Carei Search Form"
|
||||||
|
msgstr "Carei Search Form"
|
||||||
Binary file not shown.
@@ -0,0 +1,659 @@
|
|||||||
|
# Copyright (C) 2026 Carei
|
||||||
|
# This file is distributed under the same license as the Carei Reservation plugin.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Carei Reservation 1.0.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2026-04-22 12:00+0000\n"
|
||||||
|
"PO-Revision-Date: 2026-04-22 12:00+0000\n"
|
||||||
|
"Last-Translator: Carei\n"
|
||||||
|
"Language-Team: English\n"
|
||||||
|
"Language: en_US\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Domain: carei-reservation\n"
|
||||||
|
|
||||||
|
#: carei-reservation.php:133
|
||||||
|
msgid "Błąd API: HTTP %status%"
|
||||||
|
msgstr "API error: HTTP %status%"
|
||||||
|
|
||||||
|
#: carei-reservation.php:134
|
||||||
|
msgid "Przekroczono czas oczekiwania. Spróbuj ponownie."
|
||||||
|
msgstr "Request timed out. Please try again."
|
||||||
|
|
||||||
|
#: carei-reservation.php:135
|
||||||
|
msgid "Brak połączenia z serwerem. Sprawdź internet i spróbuj ponownie."
|
||||||
|
msgstr "No server connection. Check your internet and try again."
|
||||||
|
|
||||||
|
#: carei-reservation.php:136
|
||||||
|
msgid "Błąd ładowania"
|
||||||
|
msgstr "Loading error"
|
||||||
|
|
||||||
|
#: carei-reservation.php:139
|
||||||
|
msgid "Wybierz..."
|
||||||
|
msgstr "Select..."
|
||||||
|
|
||||||
|
#: carei-reservation.php:140 includes/class-elementor-widget.php:77
|
||||||
|
msgid "Wybierz segment pojazdu"
|
||||||
|
msgstr "Select a vehicle segment"
|
||||||
|
|
||||||
|
#: carei-reservation.php:141 includes/class-search-widget.php:47
|
||||||
|
msgid "Wybierz segment"
|
||||||
|
msgstr "Select segment"
|
||||||
|
|
||||||
|
#: carei-reservation.php:142
|
||||||
|
msgid "Wybierz miejsce odbioru"
|
||||||
|
msgstr "Select pickup location"
|
||||||
|
|
||||||
|
#: carei-reservation.php:143
|
||||||
|
msgid "Wybierz miejsce zwrotu"
|
||||||
|
msgstr "Select return location"
|
||||||
|
|
||||||
|
#: carei-reservation.php:144
|
||||||
|
msgid "Segment %name%"
|
||||||
|
msgstr "Segment %name%"
|
||||||
|
|
||||||
|
#: carei-reservation.php:145
|
||||||
|
msgid "Brak segmentów"
|
||||||
|
msgstr "No segments"
|
||||||
|
|
||||||
|
#: carei-reservation.php:146
|
||||||
|
msgid "Najpierw wybierz segment"
|
||||||
|
msgstr "Select segment first"
|
||||||
|
|
||||||
|
#: carei-reservation.php:147 carei-reservation.php:209
|
||||||
|
#: includes/class-elementor-widget.php:105
|
||||||
|
#: includes/class-search-widget.php:76
|
||||||
|
msgid "Miejsce odbioru"
|
||||||
|
msgstr "Pickup location"
|
||||||
|
|
||||||
|
#: carei-reservation.php:148 carei-reservation.php:210
|
||||||
|
#: includes/class-elementor-widget.php:123
|
||||||
|
msgid "Miejsce zwrotu"
|
||||||
|
msgstr "Return location"
|
||||||
|
|
||||||
|
#: carei-reservation.php:149
|
||||||
|
msgid "Brak lokalizacji dla tego segmentu"
|
||||||
|
msgstr "No locations for this segment"
|
||||||
|
|
||||||
|
#: carei-reservation.php:150
|
||||||
|
msgid "Brak lokalizacji"
|
||||||
|
msgstr "No locations"
|
||||||
|
|
||||||
|
#: carei-reservation.php:153
|
||||||
|
msgid "Rozpoczęcie"
|
||||||
|
msgstr "Start"
|
||||||
|
|
||||||
|
#: carei-reservation.php:154
|
||||||
|
msgid "Zakończenie"
|
||||||
|
msgstr "End"
|
||||||
|
|
||||||
|
#: carei-reservation.php:155
|
||||||
|
msgid "%label% — data lub godzina już minęły"
|
||||||
|
msgstr "%label% — date or time has already passed"
|
||||||
|
|
||||||
|
#: carei-reservation.php:156
|
||||||
|
msgid "Data lub godzina rozpoczęcia już minęły"
|
||||||
|
msgstr "Start date or time has already passed"
|
||||||
|
|
||||||
|
#: carei-reservation.php:157
|
||||||
|
msgid "Data lub godzina zakończenia już minęły"
|
||||||
|
msgstr "End date or time has already passed"
|
||||||
|
|
||||||
|
#: carei-reservation.php:158
|
||||||
|
msgid "Data zakończenia musi być po dacie rozpoczęcia"
|
||||||
|
msgstr "End date must be after start date"
|
||||||
|
|
||||||
|
#: carei-reservation.php:159
|
||||||
|
msgid "Podaj datę rozpoczęcia"
|
||||||
|
msgstr "Enter start date"
|
||||||
|
|
||||||
|
#: carei-reservation.php:160
|
||||||
|
msgid "Podaj datę zakończenia"
|
||||||
|
msgstr "Enter end date"
|
||||||
|
|
||||||
|
#: carei-reservation.php:163
|
||||||
|
msgid "Wybrano: <strong>%count% %unit%</strong>"
|
||||||
|
msgstr "Selected: <strong>%count% %unit%</strong>"
|
||||||
|
|
||||||
|
#: carei-reservation.php:164
|
||||||
|
msgid "dzień"
|
||||||
|
msgstr "day"
|
||||||
|
|
||||||
|
#: carei-reservation.php:165
|
||||||
|
msgid "dni"
|
||||||
|
msgstr "days"
|
||||||
|
|
||||||
|
#: carei-reservation.php:166
|
||||||
|
msgid "doba"
|
||||||
|
msgstr "day"
|
||||||
|
|
||||||
|
#: carei-reservation.php:167
|
||||||
|
msgid "doby"
|
||||||
|
msgstr "days"
|
||||||
|
|
||||||
|
#: carei-reservation.php:168
|
||||||
|
msgid "dób"
|
||||||
|
msgstr "days"
|
||||||
|
|
||||||
|
#: carei-reservation.php:171
|
||||||
|
msgid "%price% zł/doba"
|
||||||
|
msgstr "%price% PLN/day"
|
||||||
|
|
||||||
|
#: carei-reservation.php:172 carei-reservation.php:173
|
||||||
|
msgid "%price% zł"
|
||||||
|
msgstr "%price% PLN"
|
||||||
|
|
||||||
|
#: carei-reservation.php:174
|
||||||
|
msgid "od %min% do %max% zł"
|
||||||
|
msgstr "from %min% to %max% PLN"
|
||||||
|
|
||||||
|
#: carei-reservation.php:175
|
||||||
|
msgid "%perDay% zł/doba × %days% = %total% zł"
|
||||||
|
msgstr "%perDay% PLN/day × %days% = %total% PLN"
|
||||||
|
|
||||||
|
#: carei-reservation.php:176
|
||||||
|
msgid "Gratis"
|
||||||
|
msgstr "Free"
|
||||||
|
|
||||||
|
#: carei-reservation.php:179
|
||||||
|
msgid "Usuń"
|
||||||
|
msgstr "Remove"
|
||||||
|
|
||||||
|
#: carei-reservation.php:180
|
||||||
|
msgid "Dodaj"
|
||||||
|
msgstr "Add"
|
||||||
|
|
||||||
|
#: carei-reservation.php:183
|
||||||
|
msgid "Podaj imię"
|
||||||
|
msgstr "Enter first name"
|
||||||
|
|
||||||
|
#: carei-reservation.php:184
|
||||||
|
msgid "Podaj nazwisko"
|
||||||
|
msgstr "Enter last name"
|
||||||
|
|
||||||
|
#: carei-reservation.php:185
|
||||||
|
msgid "Podaj poprawny adres e-mail"
|
||||||
|
msgstr "Enter a valid email address"
|
||||||
|
|
||||||
|
#: carei-reservation.php:186
|
||||||
|
msgid "Podaj numer telefonu (min. 9 cyfr)"
|
||||||
|
msgstr "Enter phone number (min. 9 digits)"
|
||||||
|
|
||||||
|
#: carei-reservation.php:187
|
||||||
|
msgid "Wymagana zgoda na Politykę Prywatności"
|
||||||
|
msgstr "Privacy policy consent is required"
|
||||||
|
|
||||||
|
#: carei-reservation.php:190 includes/class-elementor-widget.php:226
|
||||||
|
msgid "Wyślij"
|
||||||
|
msgstr "Send"
|
||||||
|
|
||||||
|
#: carei-reservation.php:191
|
||||||
|
msgid "Przetwarzanie..."
|
||||||
|
msgstr "Processing..."
|
||||||
|
|
||||||
|
#: carei-reservation.php:192
|
||||||
|
msgid "Rezerwuję..."
|
||||||
|
msgstr "Reserving..."
|
||||||
|
|
||||||
|
#: carei-reservation.php:193 includes/class-elementor-widget.php:250
|
||||||
|
msgid "Potwierdź rezerwację"
|
||||||
|
msgstr "Confirm reservation"
|
||||||
|
|
||||||
|
#: carei-reservation.php:196
|
||||||
|
msgid "Ładowanie podsumowania..."
|
||||||
|
msgstr "Loading summary..."
|
||||||
|
|
||||||
|
#: carei-reservation.php:197
|
||||||
|
msgid "Nie udało się utworzyć klienta"
|
||||||
|
msgstr "Failed to create customer"
|
||||||
|
|
||||||
|
#: carei-reservation.php:198
|
||||||
|
msgid "Błąd tworzenia klienta: %msg%"
|
||||||
|
msgstr "Customer creation error: %msg%"
|
||||||
|
|
||||||
|
#: carei-reservation.php:199
|
||||||
|
msgid "Błąd pobierania podsumowania: %msg%"
|
||||||
|
msgstr "Error loading summary: %msg%"
|
||||||
|
|
||||||
|
#: carei-reservation.php:200
|
||||||
|
msgid "Rezerwacja nie powiodła się"
|
||||||
|
msgstr "Reservation failed"
|
||||||
|
|
||||||
|
#: carei-reservation.php:203
|
||||||
|
msgid "Pakiet ochronny: %name% — %perDay% zł/doba × %days% = %total% zł (do doliczenia poza systemem)"
|
||||||
|
msgstr "Protection package: %name% — %perDay% PLN/day × %days% = %total% PLN (to be paid separately)"
|
||||||
|
|
||||||
|
#: carei-reservation.php:206 includes/class-admin-panel.php:219
|
||||||
|
#: includes/class-admin-panel.php:399
|
||||||
|
msgid "Segment"
|
||||||
|
msgstr "Segment"
|
||||||
|
|
||||||
|
#: carei-reservation.php:207
|
||||||
|
msgid "Od"
|
||||||
|
msgstr "From"
|
||||||
|
|
||||||
|
#: carei-reservation.php:208
|
||||||
|
msgid "Do"
|
||||||
|
msgstr "To"
|
||||||
|
|
||||||
|
#: carei-reservation.php:211
|
||||||
|
msgid "Najemca"
|
||||||
|
msgstr "Renter"
|
||||||
|
|
||||||
|
#: carei-reservation.php:212 includes/class-admin-panel.php:407
|
||||||
|
msgid "Email"
|
||||||
|
msgstr "Email"
|
||||||
|
|
||||||
|
#: carei-reservation.php:213 includes/class-admin-panel.php:408
|
||||||
|
msgid "Telefon"
|
||||||
|
msgstr "Phone"
|
||||||
|
|
||||||
|
#: carei-reservation.php:214
|
||||||
|
msgid "Wybrane opcje"
|
||||||
|
msgstr "Selected options"
|
||||||
|
|
||||||
|
#: carei-reservation.php:215 includes/class-admin-panel.php:414
|
||||||
|
msgid "Wiadomość"
|
||||||
|
msgstr "Message"
|
||||||
|
|
||||||
|
#: carei-reservation.php:216
|
||||||
|
msgid "auto"
|
||||||
|
msgstr "car"
|
||||||
|
|
||||||
|
#: carei-reservation.php:217
|
||||||
|
msgid "do doliczenia"
|
||||||
|
msgstr "to be added"
|
||||||
|
|
||||||
|
#: carei-reservation.php:218
|
||||||
|
msgid "VAT"
|
||||||
|
msgstr "VAT"
|
||||||
|
|
||||||
|
#: carei-reservation.php:219 includes/class-admin-panel.php:413
|
||||||
|
msgid "Pakiet ochronny"
|
||||||
|
msgstr "Protection package"
|
||||||
|
|
||||||
|
#: carei-reservation.php:220
|
||||||
|
msgid "Do zapłaty"
|
||||||
|
msgstr "Total due"
|
||||||
|
|
||||||
|
#: carei-reservation.php:223
|
||||||
|
msgid "Nazwa"
|
||||||
|
msgstr "Name"
|
||||||
|
|
||||||
|
#: carei-reservation.php:224
|
||||||
|
msgid "Ilość"
|
||||||
|
msgstr "Quantity"
|
||||||
|
|
||||||
|
#: carei-reservation.php:225
|
||||||
|
msgid "Netto"
|
||||||
|
msgstr "Net"
|
||||||
|
|
||||||
|
#: carei-reservation.php:226
|
||||||
|
msgid "Brutto"
|
||||||
|
msgstr "Gross"
|
||||||
|
|
||||||
|
#: carei-reservation.php:229
|
||||||
|
msgid "Brak dostępnego pojazdu w wybranym terminie. Zmień daty lub segment."
|
||||||
|
msgstr "No vehicle available for the selected dates. Change dates or segment."
|
||||||
|
|
||||||
|
#: carei-reservation.php:230
|
||||||
|
msgid "Nieprawidłowy zakres dat"
|
||||||
|
msgstr "Invalid date range"
|
||||||
|
|
||||||
|
#: carei-reservation.php:231
|
||||||
|
msgid "Nie znaleziono oddziału"
|
||||||
|
msgstr "Location not found"
|
||||||
|
|
||||||
|
#: carei-reservation.php:232
|
||||||
|
msgid "Klient o tych danych już istnieje w systemie"
|
||||||
|
msgstr "A customer with these details already exists in the system"
|
||||||
|
|
||||||
|
#: carei-reservation.php:233
|
||||||
|
msgid "Nieprawidłowy numer PESEL"
|
||||||
|
msgstr "Invalid PESEL number"
|
||||||
|
|
||||||
|
#: carei-reservation.php:234
|
||||||
|
msgid "Cennik wygasł. Odśwież formularz i spróbuj ponownie."
|
||||||
|
msgstr "Price list expired. Refresh the form and try again."
|
||||||
|
|
||||||
|
#: carei-reservation.php:237
|
||||||
|
msgid "Nr zamówienia: %no%"
|
||||||
|
msgstr "Order number: %no%"
|
||||||
|
|
||||||
|
#: carei-reservation.php:238
|
||||||
|
msgid "Rezerwacja potwierdzona"
|
||||||
|
msgstr "Reservation confirmed"
|
||||||
|
|
||||||
|
#: carei-reservation.php:239 includes/class-elementor-widget.php:238
|
||||||
|
msgid "Podsumowanie rezerwacji"
|
||||||
|
msgstr "Reservation summary"
|
||||||
|
|
||||||
|
#: carei-reservation.php:74
|
||||||
|
msgid "Brak konfiguracji API w pliku .env (url, username, password)."
|
||||||
|
msgstr "Missing API configuration in .env file (url, username, password)."
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:103
|
||||||
|
msgid "Konfiguracja pakietów wyświetlanych w sekcji <strong>Pakiety ochronne</strong> formularza rezerwacji. Cena podawana jest za dobę — total = cena × liczba dób rezerwacji."
|
||||||
|
msgstr "Configuration of packages displayed in the <strong>Protection packages</strong> section of the reservation form. Price is per day — total = price × number of rental days."
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:105
|
||||||
|
msgid "Zapisano."
|
||||||
|
msgstr "Saved."
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:115
|
||||||
|
msgid "Pakiet %s"
|
||||||
|
msgstr "%s Package"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:118
|
||||||
|
msgid "Nazwa wyświetlana"
|
||||||
|
msgstr "Display name"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:122
|
||||||
|
msgid "Cena za dobę (zł)"
|
||||||
|
msgstr "Price per day (PLN)"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:126 includes/class-admin-panel.php:222
|
||||||
|
#: includes/class-admin-panel.php:417
|
||||||
|
msgid "Status"
|
||||||
|
msgstr "Status"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:127
|
||||||
|
msgid "Aktywny (widoczny w modalu)"
|
||||||
|
msgstr "Active (visible in modal)"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:130
|
||||||
|
msgid "Opis / zakres usług"
|
||||||
|
msgstr "Description / service scope"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:137
|
||||||
|
msgid "Zapisz pakiety"
|
||||||
|
msgstr "Save packages"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:152
|
||||||
|
msgid "Nieprawidłowy token."
|
||||||
|
msgstr "Invalid token."
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:189 includes/class-admin-panel.php:191
|
||||||
|
msgid "Rezerwacje"
|
||||||
|
msgstr "Reservations"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:190
|
||||||
|
msgid "Rezerwacja"
|
||||||
|
msgstr "Reservation"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:192
|
||||||
|
msgid "Wszystkie rezerwacje"
|
||||||
|
msgstr "All reservations"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:193
|
||||||
|
msgid "Zobacz rezerwację"
|
||||||
|
msgstr "View reservation"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:194 includes/class-admin-panel.php:321
|
||||||
|
msgid "Szczegóły rezerwacji"
|
||||||
|
msgstr "Reservation details"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:195
|
||||||
|
msgid "Szukaj rezerwacji"
|
||||||
|
msgstr "Search reservations"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:196
|
||||||
|
msgid "Nie znaleziono rezerwacji"
|
||||||
|
msgstr "No reservations found"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:197
|
||||||
|
msgid "Brak rezerwacji w koszu"
|
||||||
|
msgstr "No reservations in trash"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:217 includes/class-admin-panel.php:395
|
||||||
|
msgid "Nr rezerwacji"
|
||||||
|
msgstr "Reservation No."
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:218
|
||||||
|
msgid "Klient"
|
||||||
|
msgstr "Customer"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:22
|
||||||
|
msgid "Nowe"
|
||||||
|
msgstr "New"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:220
|
||||||
|
msgid "Daty"
|
||||||
|
msgstr "Dates"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:221
|
||||||
|
msgid "Oddział"
|
||||||
|
msgstr "Location"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:223
|
||||||
|
msgid "Data"
|
||||||
|
msgstr "Date"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:24
|
||||||
|
msgid "Przeczytane"
|
||||||
|
msgstr "Read"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:26
|
||||||
|
msgid "Zrealizowane"
|
||||||
|
msgstr "Completed"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:279
|
||||||
|
msgid "Wszystkie statusy"
|
||||||
|
msgstr "All statuses"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:362
|
||||||
|
msgid "%1$s — %2$s zł/doba × %3$d = %4$s zł"
|
||||||
|
msgstr "%1$s — %2$s PLN/day × %3$d = %4$s PLN"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:396
|
||||||
|
msgid "ID rezerwacji (Softra)"
|
||||||
|
msgstr "Reservation ID (Softra)"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:397
|
||||||
|
msgid "ID klienta (Softra)"
|
||||||
|
msgstr "Customer ID (Softra)"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:400
|
||||||
|
msgid "Data od"
|
||||||
|
msgstr "Date from"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:401
|
||||||
|
msgid "Data do"
|
||||||
|
msgstr "Date to"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:402
|
||||||
|
msgid "Oddział odbioru"
|
||||||
|
msgstr "Pickup location"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:403
|
||||||
|
msgid "Oddział zwrotu"
|
||||||
|
msgstr "Return location"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:409
|
||||||
|
msgid "PESEL"
|
||||||
|
msgstr "PESEL"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:410
|
||||||
|
msgid "Adres"
|
||||||
|
msgstr "Address"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:412 includes/class-admin-panel.php:413
|
||||||
|
msgid "Brak"
|
||||||
|
msgstr "None"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:50
|
||||||
|
msgid "Ubezpieczenie SOFT"
|
||||||
|
msgstr "SOFT Protection"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:537
|
||||||
|
msgid "Rezerwacja #%1$s — %2$s %3$s"
|
||||||
|
msgstr "Reservation #%1$s — %2$s %3$s"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:56
|
||||||
|
msgid "Ubezpieczenie PREMIUM"
|
||||||
|
msgstr "PREMIUM Protection"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:149 includes/class-admin-panel.php:96
|
||||||
|
msgid "Brak uprawnień."
|
||||||
|
msgstr "No permissions."
|
||||||
|
|
||||||
|
#: includes/class-branches-widget.php:23
|
||||||
|
msgid "Carei Branches"
|
||||||
|
msgstr "Carei Branches"
|
||||||
|
|
||||||
|
#: includes/class-cities-widget.php:16
|
||||||
|
msgid "Carei Cities"
|
||||||
|
msgstr "Carei Cities"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:115
|
||||||
|
#: includes/class-search-widget.php:89
|
||||||
|
msgid "Zwrot w tej samej lokalizacji"
|
||||||
|
msgstr "Return at the same location"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:133
|
||||||
|
msgid "Wyjazd zagraniczny"
|
||||||
|
msgstr "International travel"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:138
|
||||||
|
msgid "Wyjazd poza granicę Polski do:"
|
||||||
|
msgstr "Travel outside Poland to:"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:143
|
||||||
|
msgid "Wyszukaj i dodaj kraj na trasie"
|
||||||
|
msgstr "Search and add a country on the route"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:144
|
||||||
|
msgid "Wyczyść"
|
||||||
|
msgstr "Clear"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:102 includes/class-admin-panel.php:86
|
||||||
|
#: includes/class-admin-panel.php:87
|
||||||
|
#: includes/class-elementor-widget.php:153
|
||||||
|
msgid "Pakiety ochronne"
|
||||||
|
msgstr "Protection packages"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:16
|
||||||
|
msgid "Carei Reservation"
|
||||||
|
msgstr "Carei Reservation"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:412
|
||||||
|
#: includes/class-elementor-widget.php:160
|
||||||
|
msgid "Opcje dodatkowe"
|
||||||
|
msgstr "Additional options"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:170
|
||||||
|
msgid "Dane najemcy"
|
||||||
|
msgstr "Renter details"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:405
|
||||||
|
#: includes/class-elementor-widget.php:177
|
||||||
|
msgid "Imię"
|
||||||
|
msgstr "First name"
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:406
|
||||||
|
#: includes/class-elementor-widget.php:183
|
||||||
|
msgid "Nazwisko"
|
||||||
|
msgstr "Last name"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:192
|
||||||
|
msgid "Adres e-mail"
|
||||||
|
msgstr "Email address"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:197
|
||||||
|
msgid "Nr telefonu"
|
||||||
|
msgstr "Phone number"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:211
|
||||||
|
msgid "Twoja wiadomość dotycząca rezerwacji"
|
||||||
|
msgstr "Your message regarding the reservation"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:222
|
||||||
|
msgid "Zgadzam się na <a href=\"/polityka-prywatnosci/\" target=\"_blank\">Politykę Prywatności</a>"
|
||||||
|
msgstr "I agree to the <a href=\"/polityka-prywatnosci/\" target=\"_blank\">Privacy Policy</a>"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:231
|
||||||
|
msgid "Uzupełnij wymagane pola zaznaczone na czerwono."
|
||||||
|
msgstr "Please complete the required fields marked in red."
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:246
|
||||||
|
msgid "Wróć do formularza"
|
||||||
|
msgstr "Back to form"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:260
|
||||||
|
msgid "Zamówienie złożone!"
|
||||||
|
msgstr "Order submitted!"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:262
|
||||||
|
msgid "Oczekuj na kontakt z wypożyczalnią"
|
||||||
|
msgstr "Please wait for the rental company to contact you"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:263
|
||||||
|
msgid "Zamknij"
|
||||||
|
msgstr "Close"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:37
|
||||||
|
msgid "Przycisk rezerwacji"
|
||||||
|
msgstr "Reservation button"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:42
|
||||||
|
msgid "Tekst przycisku"
|
||||||
|
msgstr "Button text"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:44
|
||||||
|
#: includes/class-search-widget.php:97
|
||||||
|
msgid "Złóż zapytanie o rezerwację"
|
||||||
|
msgstr "Request a reservation"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:62
|
||||||
|
msgid "Zamknij formularz"
|
||||||
|
msgstr "Close form"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:67
|
||||||
|
#: includes/class-search-widget.php:40
|
||||||
|
msgid "Wypełnij formularz rezerwacji"
|
||||||
|
msgstr "Fill out the reservation form"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:85
|
||||||
|
#: includes/class-search-widget.php:58
|
||||||
|
msgid "Od kiedy?"
|
||||||
|
msgstr "From?"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:92
|
||||||
|
#: includes/class-search-widget.php:65
|
||||||
|
msgid "Do kiedy?"
|
||||||
|
msgstr "Until?"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:98
|
||||||
|
msgid "Wybrano:"
|
||||||
|
msgstr "Selected:"
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:98
|
||||||
|
msgid "0 dni"
|
||||||
|
msgstr "0 days"
|
||||||
|
|
||||||
|
#: includes/class-branches-widget.php:99 includes/class-map-widget.php:172
|
||||||
|
msgid "Oddział %s"
|
||||||
|
msgstr "Location %s"
|
||||||
|
|
||||||
|
#: includes/class-branches-widget.php:95 includes/class-map-widget.php:176
|
||||||
|
msgid "ul. %s"
|
||||||
|
msgstr "%s St."
|
||||||
|
|
||||||
|
#: includes/class-map-widget.php:66
|
||||||
|
msgid "Carei Map"
|
||||||
|
msgstr "Carei Map"
|
||||||
|
|
||||||
|
#: includes/class-rest-proxy.php:148
|
||||||
|
msgid "Invalid nonce."
|
||||||
|
msgstr "Invalid nonce."
|
||||||
|
|
||||||
|
#: includes/class-rest-proxy.php:159
|
||||||
|
msgid "Softra API not configured."
|
||||||
|
msgstr "Softra API not configured."
|
||||||
|
|
||||||
|
#: includes/class-search-widget.php:16
|
||||||
|
msgid "Carei Search Form"
|
||||||
|
msgstr "Carei Search Form"
|
||||||
@@ -0,0 +1,659 @@
|
|||||||
|
# Copyright (C) 2026 Carei
|
||||||
|
# This file is distributed under the same license as the Carei Reservation plugin.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Carei Reservation 1.0.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2026-04-22 12:00+0000\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Domain: carei-reservation\n"
|
||||||
|
|
||||||
|
#: carei-reservation.php:133
|
||||||
|
msgid "Błąd API: HTTP %status%"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:134
|
||||||
|
msgid "Przekroczono czas oczekiwania. Spróbuj ponownie."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:135
|
||||||
|
msgid "Brak połączenia z serwerem. Sprawdź internet i spróbuj ponownie."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:136
|
||||||
|
msgid "Błąd ładowania"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:139
|
||||||
|
msgid "Wybierz..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:140 includes/class-elementor-widget.php:77
|
||||||
|
msgid "Wybierz segment pojazdu"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:141 includes/class-search-widget.php:47
|
||||||
|
msgid "Wybierz segment"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:142
|
||||||
|
msgid "Wybierz miejsce odbioru"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:143
|
||||||
|
msgid "Wybierz miejsce zwrotu"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:144
|
||||||
|
msgid "Segment %name%"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:145
|
||||||
|
msgid "Brak segmentów"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:146
|
||||||
|
msgid "Najpierw wybierz segment"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:147 carei-reservation.php:209
|
||||||
|
#: includes/class-elementor-widget.php:105
|
||||||
|
#: includes/class-search-widget.php:76
|
||||||
|
msgid "Miejsce odbioru"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:148 carei-reservation.php:210
|
||||||
|
#: includes/class-elementor-widget.php:123
|
||||||
|
msgid "Miejsce zwrotu"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:149
|
||||||
|
msgid "Brak lokalizacji dla tego segmentu"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:150
|
||||||
|
msgid "Brak lokalizacji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:153
|
||||||
|
msgid "Rozpoczęcie"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:154
|
||||||
|
msgid "Zakończenie"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:155
|
||||||
|
msgid "%label% — data lub godzina już minęły"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:156
|
||||||
|
msgid "Data lub godzina rozpoczęcia już minęły"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:157
|
||||||
|
msgid "Data lub godzina zakończenia już minęły"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:158
|
||||||
|
msgid "Data zakończenia musi być po dacie rozpoczęcia"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:159
|
||||||
|
msgid "Podaj datę rozpoczęcia"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:160
|
||||||
|
msgid "Podaj datę zakończenia"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:163
|
||||||
|
msgid "Wybrano: <strong>%count% %unit%</strong>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:164
|
||||||
|
msgid "dzień"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:165
|
||||||
|
msgid "dni"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:166
|
||||||
|
msgid "doba"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:167
|
||||||
|
msgid "doby"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:168
|
||||||
|
msgid "dób"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:171
|
||||||
|
msgid "%price% zł/doba"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:172 carei-reservation.php:173
|
||||||
|
msgid "%price% zł"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:174
|
||||||
|
msgid "od %min% do %max% zł"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:175
|
||||||
|
msgid "%perDay% zł/doba × %days% = %total% zł"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:176
|
||||||
|
msgid "Gratis"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:179
|
||||||
|
msgid "Usuń"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:180
|
||||||
|
msgid "Dodaj"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:183
|
||||||
|
msgid "Podaj imię"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:184
|
||||||
|
msgid "Podaj nazwisko"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:185
|
||||||
|
msgid "Podaj poprawny adres e-mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:186
|
||||||
|
msgid "Podaj numer telefonu (min. 9 cyfr)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:187
|
||||||
|
msgid "Wymagana zgoda na Politykę Prywatności"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:190 includes/class-elementor-widget.php:226
|
||||||
|
msgid "Wyślij"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:191
|
||||||
|
msgid "Przetwarzanie..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:192
|
||||||
|
msgid "Rezerwuję..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:193 includes/class-elementor-widget.php:250
|
||||||
|
msgid "Potwierdź rezerwację"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:196
|
||||||
|
msgid "Ładowanie podsumowania..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:197
|
||||||
|
msgid "Nie udało się utworzyć klienta"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:198
|
||||||
|
msgid "Błąd tworzenia klienta: %msg%"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:199
|
||||||
|
msgid "Błąd pobierania podsumowania: %msg%"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:200
|
||||||
|
msgid "Rezerwacja nie powiodła się"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:203
|
||||||
|
msgid "Pakiet ochronny: %name% — %perDay% zł/doba × %days% = %total% zł (do doliczenia poza systemem)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:206 includes/class-admin-panel.php:219
|
||||||
|
#: includes/class-admin-panel.php:399
|
||||||
|
msgid "Segment"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:207
|
||||||
|
msgid "Od"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:208
|
||||||
|
msgid "Do"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:211
|
||||||
|
msgid "Najemca"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:212 includes/class-admin-panel.php:407
|
||||||
|
msgid "Email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:213 includes/class-admin-panel.php:408
|
||||||
|
msgid "Telefon"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:214
|
||||||
|
msgid "Wybrane opcje"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:215 includes/class-admin-panel.php:414
|
||||||
|
msgid "Wiadomość"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:216
|
||||||
|
msgid "auto"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:217
|
||||||
|
msgid "do doliczenia"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:218
|
||||||
|
msgid "VAT"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:219 includes/class-admin-panel.php:413
|
||||||
|
msgid "Pakiet ochronny"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:220
|
||||||
|
msgid "Do zapłaty"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:223
|
||||||
|
msgid "Nazwa"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:224
|
||||||
|
msgid "Ilość"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:225
|
||||||
|
msgid "Netto"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:226
|
||||||
|
msgid "Brutto"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:229
|
||||||
|
msgid "Brak dostępnego pojazdu w wybranym terminie. Zmień daty lub segment."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:230
|
||||||
|
msgid "Nieprawidłowy zakres dat"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:231
|
||||||
|
msgid "Nie znaleziono oddziału"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:232
|
||||||
|
msgid "Klient o tych danych już istnieje w systemie"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:233
|
||||||
|
msgid "Nieprawidłowy numer PESEL"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:234
|
||||||
|
msgid "Cennik wygasł. Odśwież formularz i spróbuj ponownie."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:237
|
||||||
|
msgid "Nr zamówienia: %no%"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:238
|
||||||
|
msgid "Rezerwacja potwierdzona"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:239 includes/class-elementor-widget.php:238
|
||||||
|
msgid "Podsumowanie rezerwacji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: carei-reservation.php:74
|
||||||
|
msgid "Brak konfiguracji API w pliku .env (url, username, password)."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:103
|
||||||
|
msgid "Konfiguracja pakietów wyświetlanych w sekcji <strong>Pakiety ochronne</strong> formularza rezerwacji. Cena podawana jest za dobę — total = cena × liczba dób rezerwacji."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:105
|
||||||
|
msgid "Zapisano."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:115
|
||||||
|
msgid "Pakiet %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:118
|
||||||
|
msgid "Nazwa wyświetlana"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:122
|
||||||
|
msgid "Cena za dobę (zł)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:126 includes/class-admin-panel.php:222
|
||||||
|
#: includes/class-admin-panel.php:417
|
||||||
|
msgid "Status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:127
|
||||||
|
msgid "Aktywny (widoczny w modalu)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:130
|
||||||
|
msgid "Opis / zakres usług"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:137
|
||||||
|
msgid "Zapisz pakiety"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:152
|
||||||
|
msgid "Nieprawidłowy token."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:189 includes/class-admin-panel.php:191
|
||||||
|
msgid "Rezerwacje"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:190
|
||||||
|
msgid "Rezerwacja"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:192
|
||||||
|
msgid "Wszystkie rezerwacje"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:193
|
||||||
|
msgid "Zobacz rezerwację"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:194 includes/class-admin-panel.php:321
|
||||||
|
msgid "Szczegóły rezerwacji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:195
|
||||||
|
msgid "Szukaj rezerwacji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:196
|
||||||
|
msgid "Nie znaleziono rezerwacji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:197
|
||||||
|
msgid "Brak rezerwacji w koszu"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:217 includes/class-admin-panel.php:395
|
||||||
|
msgid "Nr rezerwacji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:218
|
||||||
|
msgid "Klient"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:22
|
||||||
|
msgid "Nowe"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:220
|
||||||
|
msgid "Daty"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:221
|
||||||
|
msgid "Oddział"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:223
|
||||||
|
msgid "Data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:24
|
||||||
|
msgid "Przeczytane"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:26
|
||||||
|
msgid "Zrealizowane"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:279
|
||||||
|
msgid "Wszystkie statusy"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:362
|
||||||
|
msgid "%1$s — %2$s zł/doba × %3$d = %4$s zł"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:396
|
||||||
|
msgid "ID rezerwacji (Softra)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:397
|
||||||
|
msgid "ID klienta (Softra)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:400
|
||||||
|
msgid "Data od"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:401
|
||||||
|
msgid "Data do"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:402
|
||||||
|
msgid "Oddział odbioru"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:403
|
||||||
|
msgid "Oddział zwrotu"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:409
|
||||||
|
msgid "PESEL"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:410
|
||||||
|
msgid "Adres"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:412 includes/class-admin-panel.php:413
|
||||||
|
msgid "Brak"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:50
|
||||||
|
msgid "Ubezpieczenie SOFT"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:537
|
||||||
|
msgid "Rezerwacja #%1$s — %2$s %3$s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:56
|
||||||
|
msgid "Ubezpieczenie PREMIUM"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:149 includes/class-admin-panel.php:96
|
||||||
|
msgid "Brak uprawnień."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-branches-widget.php:23
|
||||||
|
msgid "Carei Branches"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-cities-widget.php:16
|
||||||
|
msgid "Carei Cities"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:115
|
||||||
|
#: includes/class-search-widget.php:89
|
||||||
|
msgid "Zwrot w tej samej lokalizacji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:133
|
||||||
|
msgid "Wyjazd zagraniczny"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:138
|
||||||
|
msgid "Wyjazd poza granicę Polski do:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:143
|
||||||
|
msgid "Wyszukaj i dodaj kraj na trasie"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:144
|
||||||
|
msgid "Wyczyść"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:102 includes/class-admin-panel.php:86
|
||||||
|
#: includes/class-admin-panel.php:87
|
||||||
|
#: includes/class-elementor-widget.php:153
|
||||||
|
msgid "Pakiety ochronne"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:16
|
||||||
|
msgid "Carei Reservation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:412
|
||||||
|
#: includes/class-elementor-widget.php:160
|
||||||
|
msgid "Opcje dodatkowe"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:170
|
||||||
|
msgid "Dane najemcy"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:405
|
||||||
|
#: includes/class-elementor-widget.php:177
|
||||||
|
msgid "Imię"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-panel.php:406
|
||||||
|
#: includes/class-elementor-widget.php:183
|
||||||
|
msgid "Nazwisko"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:192
|
||||||
|
msgid "Adres e-mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:197
|
||||||
|
msgid "Nr telefonu"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:211
|
||||||
|
msgid "Twoja wiadomość dotycząca rezerwacji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:222
|
||||||
|
msgid "Zgadzam się na <a href=\"/polityka-prywatnosci/\" target=\"_blank\">Politykę Prywatności</a>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:231
|
||||||
|
msgid "Uzupełnij wymagane pola zaznaczone na czerwono."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:246
|
||||||
|
msgid "Wróć do formularza"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:260
|
||||||
|
msgid "Zamówienie złożone!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:262
|
||||||
|
msgid "Oczekuj na kontakt z wypożyczalnią"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:263
|
||||||
|
msgid "Zamknij"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:37
|
||||||
|
msgid "Przycisk rezerwacji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:42
|
||||||
|
msgid "Tekst przycisku"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:44
|
||||||
|
#: includes/class-search-widget.php:97
|
||||||
|
msgid "Złóż zapytanie o rezerwację"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:62
|
||||||
|
msgid "Zamknij formularz"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:67
|
||||||
|
#: includes/class-search-widget.php:40
|
||||||
|
msgid "Wypełnij formularz rezerwacji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:85
|
||||||
|
#: includes/class-search-widget.php:58
|
||||||
|
msgid "Od kiedy?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:92
|
||||||
|
#: includes/class-search-widget.php:65
|
||||||
|
msgid "Do kiedy?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:98
|
||||||
|
msgid "Wybrano:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-elementor-widget.php:98
|
||||||
|
msgid "0 dni"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-branches-widget.php:99 includes/class-map-widget.php:172
|
||||||
|
msgid "Oddział %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-branches-widget.php:95 includes/class-map-widget.php:176
|
||||||
|
msgid "ul. %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-map-widget.php:66
|
||||||
|
msgid "Carei Map"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-rest-proxy.php:148
|
||||||
|
msgid "Invalid nonce."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-rest-proxy.php:159
|
||||||
|
msgid "Softra API not configured."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-search-widget.php:16
|
||||||
|
msgid "Carei Search Form"
|
||||||
|
msgstr ""
|
||||||
BIN
wp-content/themes/.DS_Store
vendored
Normal file
BIN
wp-content/themes/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
wp-includes/.DS_Store
vendored
Normal file
BIN
wp-includes/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
wp-includes/Requests/.DS_Store
vendored
Normal file
BIN
wp-includes/Requests/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
wp-includes/SimplePie/.DS_Store
vendored
Normal file
BIN
wp-includes/SimplePie/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
wp-includes/blocks/.DS_Store
vendored
Normal file
BIN
wp-includes/blocks/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
wp-includes/images/.DS_Store
vendored
Normal file
BIN
wp-includes/images/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
wp-includes/js/.DS_Store
vendored
Normal file
BIN
wp-includes/js/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
wp-includes/rest-api/.DS_Store
vendored
Normal file
BIN
wp-includes/rest-api/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
wp-includes/sodium_compat/.DS_Store
vendored
Normal file
BIN
wp-includes/sodium_compat/.DS_Store
vendored
Normal file
Binary file not shown.
Reference in New Issue
Block a user