From 811069a25c9b8bca31a6a69cbb12205c685fdbe0 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Thu, 7 May 2026 14:57:59 +0200 Subject: [PATCH] update --- .paul/ROADMAP.md | 5 +- .paul/STATE.md | 27 +- .paul/changelog/2026-05-07.md | 29 ++ .paul/governance/governance_2026-05-07.jsonl | 60 ++++ .paul/phases/09-finalizacja/09-04-PLAN.md | 329 ++++++++++++++++++ .paul/phases/09-finalizacja/09-04-SUMMARY.md | 210 +++++++++++ .../console-2026-05-07T12-25-18-552Z.log | 1 + .../console-2026-05-07T12-27-19-429Z.log | 1 + .../console-2026-05-07T12-29-49-066Z.log | 1 + .../console-2026-05-07T12-30-39-475Z.log | 1 + .../console-2026-05-07T12-36-28-144Z.log | 1 + .../console-2026-05-07T12-46-41-718Z.log | 1 + .../page-2026-05-07T12-25-19-865Z.yml | 239 +++++++++++++ .../page-2026-05-07T12-27-20-436Z.yml | 239 +++++++++++++ .../page-2026-05-07T12-29-49-913Z.yml | 240 +++++++++++++ .../page-2026-05-07T12-30-40-455Z.yml | 239 +++++++++++++ .../page-2026-05-07T12-36-29-172Z.yml | 240 +++++++++++++ .../page-2026-05-07T12-46-42-080Z.yml | 1 + .../page-2026-05-07T12-46-54-602Z.yml | 1 + all-cal.png | Bin 0 -> 23813 bytes layout.png | Bin 0 -> 47833 bytes rezerwacja-full.png | Bin 0 -> 1041953 bytes single-cal.png | Bin 0 -> 18128 bytes single-wrapper.png | Bin 0 -> 93408 bytes .../admin/class-admin.php | 29 ++ .../api/class-rest-controller.php | 175 ++++++++++ .../frontend/assets/css/calendar-all.css | 113 ++++++ .../frontend/assets/js/calendar-all.js | 229 ++++++++++++ .../frontend/class-calendar-widget-all.php | 282 +++++++++++++++ .../frontend/class-shortcode.php | 33 ++ .../includes/class-installer.php | 1 + .../includes/class-settings.php | 14 + .../includes/class-yacht-booking.php | 76 +++- .../integrations/ical/class-ical-import.php | 189 +++++++++- .../yacht-booking-system.php | 4 +- 35 files changed, 2980 insertions(+), 30 deletions(-) create mode 100644 .paul/changelog/2026-05-07.md create mode 100644 .paul/governance/governance_2026-05-07.jsonl create mode 100644 .paul/phases/09-finalizacja/09-04-PLAN.md create mode 100644 .paul/phases/09-finalizacja/09-04-SUMMARY.md create mode 100644 .playwright-mcp/console-2026-05-07T12-25-18-552Z.log create mode 100644 .playwright-mcp/console-2026-05-07T12-27-19-429Z.log create mode 100644 .playwright-mcp/console-2026-05-07T12-29-49-066Z.log create mode 100644 .playwright-mcp/console-2026-05-07T12-30-39-475Z.log create mode 100644 .playwright-mcp/console-2026-05-07T12-36-28-144Z.log create mode 100644 .playwright-mcp/console-2026-05-07T12-46-41-718Z.log create mode 100644 .playwright-mcp/page-2026-05-07T12-25-19-865Z.yml create mode 100644 .playwright-mcp/page-2026-05-07T12-27-20-436Z.yml create mode 100644 .playwright-mcp/page-2026-05-07T12-29-49-913Z.yml create mode 100644 .playwright-mcp/page-2026-05-07T12-30-40-455Z.yml create mode 100644 .playwright-mcp/page-2026-05-07T12-36-29-172Z.yml create mode 100644 .playwright-mcp/page-2026-05-07T12-46-42-080Z.yml create mode 100644 .playwright-mcp/page-2026-05-07T12-46-54-602Z.yml create mode 100644 all-cal.png create mode 100644 layout.png create mode 100644 rezerwacja-full.png create mode 100644 single-cal.png create mode 100644 single-wrapper.png create mode 100644 wp-content/plugins/yacht-booking-system/frontend/assets/css/calendar-all.css create mode 100644 wp-content/plugins/yacht-booking-system/frontend/assets/js/calendar-all.js create mode 100644 wp-content/plugins/yacht-booking-system/frontend/class-calendar-widget-all.php diff --git a/.paul/ROADMAP.md b/.paul/ROADMAP.md index 5120d7e..4d5743e 100644 --- a/.paul/ROADMAP.md +++ b/.paul/ROADMAP.md @@ -47,8 +47,9 @@ Szczegóły w `wp-content/plugins/yacht-booking-system/PROJECT-STATUS.md`. - [ ] 09-01: UX/UI polish kalendarza — half-day na pierwszym/ostatnim dniu rezerwacji + paleta widgetu pasująca do strony - [x] 09-02: Globalna synchronizacja iCal — wspólny Google Calendar z podziałem na jachty po prefiksie nazwy w tytule eventu (export feed + import URL + alias jachtu) ✅ 2026-05-06 - [x] 09-03: Cleanup OAuth + per-yacht iCal — usunięcie martwego kodu (3 pliki GCal/OAuth, per-yacht feedy, pole "Google Calendar ID") + cleanup migration "po cichu" ✅ 2026-05-06 -- [ ] 09-04: Security audit i poprawki -- [ ] 09-05: Testy + tłumaczenia + dokumentacja +- [x] 09-04: Globalna sync iCal (tryb wspólny kalendarz, bez filtrowania) + nowy widget "wszystkie jachty" (kolory per-jacht, half-day, bez ukośników, formularz inquiry, privacy w REST) ✅ 2026-05-07 +- [ ] 09-05: Security audit i poprawki +- [ ] 09-06: Testy + tłumaczenia + dokumentacja --- *Roadmap created: 2026-05-05* diff --git a/.paul/STATE.md b/.paul/STATE.md index 0050f8f..dfad300 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -11,20 +11,20 @@ See: .paul/PROJECT.md (updated 2026-05-05) Milestone: v1.0 Production Release (v1.0.0) Phase: 9 of 9 (Finalizacja) — In progress -Plan: 09-03 — Complete (Cleanup OAuth + per-yacht iCal) -Status: Loop closed, ready for next plan (09-04) -Last activity: 2026-05-06 — Closed loop 09-03 (3 pliki OAuth usunięte, per-yacht iCal wycofany, migration po cichu) +Plan: 09-04 — Complete (Global iCal sync + widget "wszystkie jachty") +Status: Loop closed, ready for next plan (09-05 Security audit) +Last activity: 2026-05-07 — Closed loop 09-04 (tryb global iCal, widget zbiorczy z formularzem inquiry, privacy w REST title) Progress: -- Milestone: [█████████░] 92% -- Phase 9: [██████░░░░] 60% (3 of 5 plans complete) +- Milestone: [█████████░] 95% +- Phase 9: [██████░░░░] 67% (4 of 6 plans complete) ## Loop Position Current loop state: ``` PLAN ──▶ APPLY ──▶ UNIFY - ✓ ✓ ✓ [Loop 09-03 complete, ready for 09-04] + ✓ ✓ ✓ [Loop 09-04 complete, ready for 09-05] ``` ## Accumulated Context @@ -51,15 +51,16 @@ None. ## Session Continuity -Last session: 2026-05-06 (paused — end of productive day, 2 pełne pętle zamknięte) -Stopped at: Loop 09-03 zamknięty + handoff utworzony -Next action: Run /paul:plan to plan 09-04 (Security audit). Klient wykonuje deploy FTP zmienionych plików i aktywację pluginu (cleanup migration uruchomi się automatycznie). -Resume file: .paul/HANDOFF-2026-05-06.md +Last session: 2026-05-07 +Stopped at: Loop 09-04 zamknięty — tryb global iCal + widget zbiorczy w produkcji +Next action: Run /paul:plan to plan 09-05 (Security audit). Klient: usuń `test-overlap-bookings.php` z FTP. +Resume file: .paul/phases/09-finalizacja/09-04-SUMMARY.md Resume context: -- Faza 9: 3/5 planów ukończonych (60%), milestone v1.0 92% -- Plugin sprowadzony do jednego mechanizmu sync (globalny iCal) — kontekst w 09-02 i 09-03 SUMMARY +- Faza 9: 4/6 planów ukończonych (67%), milestone v1.0 95% +- Plugin produkcyjnie obsługuje 2 tryby sync iCal (per_yacht, global) + nowy widget `[yacht_calendar_all]` +- Privacy hardening REST: brak `customer_name` w title eventów (precedens dla security audit) +- Plugin w wersji 1.1.0 (bump z 1.0.0) - Brak open issues, brak deferred items, brak blockers -- Sesja zaczęła z 09-01 zamknięte, kończy z 09-03 zamknięte (2 pełne pętle PLAN→APPLY→UNIFY w jednej sesji) --- *STATE.md — Updated after every significant action* diff --git a/.paul/changelog/2026-05-07.md b/.paul/changelog/2026-05-07.md new file mode 100644 index 0000000..bffa7de --- /dev/null +++ b/.paul/changelog/2026-05-07.md @@ -0,0 +1,29 @@ +# 2026-05-07 + +## Co zrobiono + +- [Faza 9, Plan 04] Globalna sync iCal (tryb wspólny kalendarz) + nowy widget "wszystkie jachty" +- Settings: nowe pole "Tryb synchronizacji iCal" (per_yacht / global) z whitelistą i bezpiecznym defaultem +- ICal_Import: rozgałęzienie `run_per_yacht_mode()` / `run_global_calendar_mode()`, nowa stała `GLOBAL_CALENDAR_SOURCE`, niezależny cleanup obu trybów +- REST: nowy endpoint `GET /availability/all` (timed 12:00→12:00, kolory z palety per-jacht, privacy w title) +- Frontend: widget Elementor `Calendar_Widget_All` + shortcode `[yacht_calendar_all]` z layoutem 2-kolumnowym (kalendarz + formularz inquiry z wyborem jachtu) +- Privacy hardening: REST nie wystawia `customer_name` w title eventów (defense-in-depth: JS `eventContent` zwraca pusty html) +- UX iteracje po checkpoincie: ciemny styl kalendarza (emulacja `/rezerwacja-maja/`), select w spójnym stylu z innymi inputami, instrukcja, server-side legenda, kolor wspólnych eventów `#7fb3d5` (jasnoniebieski) +- Bump wersji pluginu 1.0.0 → 1.1.0 + +## Zmienione pliki + +- `wp-content/plugins/yacht-booking-system/yacht-booking-system.php` +- `wp-content/plugins/yacht-booking-system/includes/class-settings.php` +- `wp-content/plugins/yacht-booking-system/includes/class-installer.php` +- `wp-content/plugins/yacht-booking-system/includes/class-yacht-booking.php` +- `wp-content/plugins/yacht-booking-system/admin/class-admin.php` +- `wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-import.php` +- `wp-content/plugins/yacht-booking-system/api/class-rest-controller.php` +- `wp-content/plugins/yacht-booking-system/frontend/class-shortcode.php` +- `wp-content/plugins/yacht-booking-system/frontend/class-calendar-widget-all.php` (nowy) +- `wp-content/plugins/yacht-booking-system/frontend/assets/css/calendar-all.css` (nowy) +- `wp-content/plugins/yacht-booking-system/frontend/assets/js/calendar-all.js` (nowy) +- `test-overlap-bookings.php` (test helper, do skasowania z FTP po teście) +- `.paul/phases/09-finalizacja/09-04-PLAN.md` +- `.paul/phases/09-finalizacja/09-04-SUMMARY.md` diff --git a/.paul/governance/governance_2026-05-07.jsonl b/.paul/governance/governance_2026-05-07.jsonl new file mode 100644 index 0000000..bce287f --- /dev/null +++ b/.paul/governance/governance_2026-05-07.jsonl @@ -0,0 +1,60 @@ +{"ts":"2026-05-07T11:38:05Z","tool":"Bash","cmd":"mkdir -p \".paul/phases/09-finalizacja\" 2>&1; ls \".paul/phases/09-finalizacja/\" 2>&1\",\"description\":\"Ensure phase dir exists\"},\"tool_response\":{\"stdout\":\"09-01-PLAN.md\\n09-01-SUMMARY.md\","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:40:34Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\.paul\\\\phases\\\\09-finalizacja\\\\09-04-PLAN.md","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:40:49Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:41:01Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:41:17Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:43:11Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\includes\\\\class-settings.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:43:16Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\includes\\\\class-installer.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:43:27Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\admin\\\\class-admin.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:43:34Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\admin\\\\class-admin.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:43:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\admin\\\\class-admin.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:44:13Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\integrations\\\\ical\\\\class-ical-import.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:44:23Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\integrations\\\\ical\\\\class-ical-import.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:44:51Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\integrations\\\\ical\\\\class-ical-import.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:45:10Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\integrations\\\\ical\\\\class-ical-import.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:45:18Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\api\\\\class-rest-controller.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:45:28Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\api\\\\class-rest-controller.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:45:51Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\api\\\\class-rest-controller.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:46:21Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\class-calendar-widget-all.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:46:31Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\class-shortcode.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:46:46Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\css\\\\calendar-all.css","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:47:10Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\js\\\\calendar-all.js","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:47:39Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\js\\\\calendar-all.js","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:47:54Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\js\\\\calendar-all.js","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:48:32Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\js\\\\calendar-all.js","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:48:46Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\js\\\\calendar-all.js","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:48:59Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\css\\\\calendar-all.css","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:49:07Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\includes\\\\class-yacht-booking.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:49:25Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\includes\\\\class-yacht-booking.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:49:33Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\includes\\\\class-yacht-booking.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:49:52Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\yacht-booking-system.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T11:49:57Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\yacht-booking-system.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:14:15Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\class-calendar-widget-all.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:14:36Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\css\\\\calendar-all.css","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:14:44Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\css\\\\calendar-all.css","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:14:59Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\js\\\\calendar-all.js","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:15:13Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\js\\\\calendar-all.js","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:15:45Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\js\\\\calendar-all.js","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:19:10Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\includes\\\\class-yacht-booking.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:22:31Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\class-calendar-widget-all.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:22:40Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\js\\\\calendar-all.js","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:23:25Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\class-calendar-widget-all.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:23:42Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\css\\\\calendar-all.css","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:27:00Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\css\\\\calendar-all.css","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:31:45Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\css\\\\calendar-all.css","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:31:55Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\css\\\\calendar-all.css","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:32:16Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\api\\\\class-rest-controller.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:32:33Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\api\\\\class-rest-controller.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:32:45Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\class-calendar-widget-all.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:32:55Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\js\\\\calendar-all.js","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:37:44Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\css\\\\calendar-all.css","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:38:01Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\css\\\\calendar-all.css","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:39:24Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\api\\\\class-rest-controller.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:40:50Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\test-overlap-bookings.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:45:42Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\api\\\\class-rest-controller.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:48:49Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\.paul\\\\phases\\\\09-finalizacja\\\\09-04-SUMMARY.md","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:49:17Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\.paul\\\\changelog\\\\2026-05-07.md","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:49:31Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:49:49Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:50:00Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} +{"ts":"2026-05-07T12:56:14Z","tool":"Bash","cmd":"rm test-overlap-bookings.php && echo \"Usunięto test-overlap-bookings.php\"\",\"description\":\"Delete test helper file\"},\"tool_response\":{\"stdout\":\"Usunięto test-overlap-bookings.php\",\"stde","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"} diff --git a/.paul/phases/09-finalizacja/09-04-PLAN.md b/.paul/phases/09-finalizacja/09-04-PLAN.md new file mode 100644 index 0000000..689a8bd --- /dev/null +++ b/.paul/phases/09-finalizacja/09-04-PLAN.md @@ -0,0 +1,329 @@ +--- +phase: 09-finalizacja +plan: 04 +type: execute +wave: 1 +depends_on: [] +files_modified: + - wp-content/plugins/yacht-booking-system/includes/class-settings.php + - wp-content/plugins/yacht-booking-system/includes/class-installer.php + - wp-content/plugins/yacht-booking-system/admin/class-admin.php + - wp-content/plugins/yacht-booking-system/admin/views/settings-page.php + - wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-import.php + - wp-content/plugins/yacht-booking-system/api/class-rest-controller.php + - wp-content/plugins/yacht-booking-system/includes/class-yacht-booking.php + - wp-content/plugins/yacht-booking-system/frontend/class-calendar-widget-all.php (NEW) + - wp-content/plugins/yacht-booking-system/frontend/class-shortcode.php + - wp-content/plugins/yacht-booking-system/assets/js/calendar-all.js (NEW) + - wp-content/plugins/yacht-booking-system/assets/css/calendar-all.css (NEW) +autonomous: false +delegation: off +--- + + +## Goal +Wprowadzić tryb "globalnej" synchronizacji iCal (wszystkie eventy z jednego feedu trafiają do wspólnego storage bez per-yacht matchingu) oraz nowy publiczny widget kalendarza pokazujący zajętość WSZYSTKICH publikowanych jachtów na jednej siatce, z kolorami per-jacht i wizualnym efektem "half-day" na pierwszym/ostatnim dniu rezerwacji. + +## Purpose +- Klient prowadzi jeden wspólny Google Calendar do wszystkich jachtów. Obecny tryb wymaga prefiksu w SUMMARY ("Maja - Klient") i wyrzuca nierozpoznane eventy. Nowy tryb pozwala importować je wszystkie bez utraty (np. "Pierwszy dzień szkoły") jako wydarzenia informacyjne na wspólnym widoku. +- Frontend ma jedno miejsce ("kalendarz floty") gdzie potencjalny klient widzi wszystkie zajętości naraz, bez rozpraszającego efektu ukośników z trybu per-jacht. Half-day daje wizualny sygnał, że ktoś może wynająć od/do południa. + +## Output +- Settings: nowy select `ical_sync_mode` (`per_yacht` | `global`) +- iCal Import: gałąź `global` zapisuje WSZYSTKIE eventy do wspólnego storage (`_booking_source = 'ical_global_calendar'`, brak yacht_id), bez wpisów do `wp_yacht_availability` +- REST: `GET /availability/all` — zwraca eventy FullCalendar dla wszystkich jachtów + globalne wydarzenia, z kolorami auto-paleta i czasami timed (12:00 → 12:00) +- Nowy widget Elementor `Yacht_Calendar_All_Widget` + shortcode `[yacht_calendar_all]` renderujący FullCalendar timed-grid z legendą jachtów + + + + +- **Unmatched** — Co zrobić z eventami iCal bez prefiksu lub z nieznaną nazwą jachtu? + → Odpowiedź: Dodatkowa opcja w ustawieniach przełączająca tryb sync. Tryb `per_yacht` (obecny — match po prefiksie) lub `global` (jeden wspólny kalendarz, wszystkie eventy zapisywane bez przypisywania do jachtów; w danym dniu może być kilka wydarzeń). +- **Widget zakres** — Nowy widget pokazuje które jachty? + → Odpowiedź: Wszystkie publikowane jachty (auto, `post_status = publish`). +- **Rozróżnienie** — Jak odróżnić rezerwacje różnych jachtów? + → Odpowiedź: Kolor per jacht z auto palety (predefiniowana tablica kolorów indeksowana wg kolejności yacht_id, deterministycznie). +- **Half-day** — Jak pokazać start/koniec w połowie dnia? + → Odpowiedź: FullCalendar timed events. Dane all-day konwertowane na timed (start = data 12:00, end = data+1 12:00) na poziomie REST endpointu. + + +## Project Context +@.paul/PROJECT.md +@.paul/ROADMAP.md +@.paul/STATE.md +@.paul/codebase/architecture.md +@.paul/codebase/db_schema.md + +## Prior Work +@.paul/phases/09-finalizacja/09-02-SUMMARY.md +@.paul/phases/09-finalizacja/09-03-SUMMARY.md + +## Source Files +@wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-import.php +@wp-content/plugins/yacht-booking-system/includes/class-settings.php +@wp-content/plugins/yacht-booking-system/admin/class-admin.php +@wp-content/plugins/yacht-booking-system/admin/views/settings-page.php +@wp-content/plugins/yacht-booking-system/api/class-rest-controller.php +@wp-content/plugins/yacht-booking-system/frontend/class-calendar-widget.php +@wp-content/plugins/yacht-booking-system/frontend/class-shortcode.php +@wp-content/plugins/yacht-booking-system/includes/class-yacht-booking.php +@wp-content/plugins/yacht-booking-system/includes/class-installer.php + + + + +## AC-1: Settings — przełącznik trybu synchronizacji iCal +```gherkin +Given admin otwiera "Yacht Booking → Settings → Synchronizacja" +When zobaczy nowe pole "Tryb synchronizacji iCal" z opcjami "Per jacht" i "Wspólny kalendarz" +And wybiera "Wspólny kalendarz" i zapisuje +Then opcja `yacht_booking_ical_sync_mode` w wp_options = "global" +And formularz pokazuje wybraną wartość po reload +And opis pomocniczy wyjaśnia różnicę między trybami (per_yacht: matching po prefiksie / global: wszystkie eventy bez filtrowania) +``` + +## AC-2: Import iCal w trybie globalnym — wszystkie eventy bez filtrowania +```gherkin +Given `yacht_booking_ical_sync_mode = "global"` i feed URL ustawiony +And feed zawiera 32 eventy (z prefiksami "Kubuś -", "Maja -", bez prefiksu "Pierwszy dzień szkoły", bez SUMMARY itp.) +When uruchamia się hook `yacht_booking_ical_global_import` +Then powstaje booking dla KAŻDEGO eventu z UID i `start`/`end` (poza past events) +And `_booking_source = "ical_global_calendar"`, `_booking_yacht_id = 0`, `_booking_status = "confirmed"` +And eventy NIE są wpisywane do `wp_yacht_availability` (nie blokują dostępności żadnego jachtu) +And eventy bez SUMMARY otrzymują domyślny tytuł (np. "Wydarzenie kalendarza") +And ponowne uruchomienie cron-u (idempotencja) — bookingi z tymi samymi UID są aktualizowane, nie duplikowane +And eventy obecne w DB ale brakujące w nowym feedzie są usuwane (stale cleanup) +``` + +## AC-3: Import iCal w trybie per-jacht — bez regresji +```gherkin +Given `yacht_booking_ical_sync_mode = "per_yacht"` (domyślne dla istniejących instalacji) +When uruchamia się hook globalnego importu +Then zachowanie jest IDENTYCZNE jak przed tą zmianą (matching po prefiksie SUMMARY, wpisy do `wp_yacht_availability`, stale cleanup ograniczony do `_booking_source = "ical_import_global"`) +And istniejące bookingi importowane wcześniej w trybie per-jacht działają dalej i są nadal aktualizowane +``` + +## AC-4: REST endpoint `GET /availability/all` +```gherkin +Given istnieje min. 2 publikowane jachty (np. "Kubuś", "Maja") z rezerwacjami +And w trybie globalnym istnieją wydarzenia z `_booking_source = "ical_global_calendar"` +When klient niezalogowany wywołuje `GET /wp-json/yacht-booking/v1/availability/all?start=2026-05-01&end=2026-12-31` +Then odpowiedź to JSON tablica obiektów FullCalendar event: + { id, title, start (ISO datetime z "T12:00:00"), end (ISO datetime z "T12:00:00"), color, yacht_id } +And bookingi per-jacht mają `color` z deterministycznej palety (tablica 8 kolorów indeksowana wg kolejności yacht_id) +And globalne wydarzenia mają osobny kolor (np. szary `#7f8c8d`) i `yacht_id = 0` +And tylko bookingi `confirmed`/`pending` są zwracane (nie `cancelled`/`rejected`) +And `start <= end` zachowane +``` + +## AC-5: Nowy widget i shortcode +```gherkin +Given strona z `[yacht_calendar_all]` lub widgetem Elementor "Yacht Calendar (wszystkie jachty)" +When klient otwiera stronę +Then widoczny FullCalendar w widoku miesięcznym (dayGridMonth) +And eventy z `GET /availability/all` są renderowane jako timed events 12:00 → 12:00 (efekt half-day na pierwszym/ostatnim dniu rezerwacji) +And nad/pod kalendarzem jest legenda kolorów (kropka + nazwa jachtu) auto-generowana z palety +And widget jest read-only (brak formularza rezerwacji, brak klikalności dnia) +And brak ukośników (cell background pełny / pusty) +``` + +## AC-6: Auto paleta kolorów per jacht +```gherkin +Given lista jachtów posortowana po ID rosnąco +When system mapuje yacht_id → kolor +Then używana jest stała tablica min. 8 kolorów (np. #3498db, #e74c3c, #2ecc71, #f39c12, #9b59b6, #1abc9c, #34495e, #d35400) +And mapowanie jest deterministyczne (yacht_id → index modulo długość palety) — ten sam yacht zawsze ten sam kolor +And paleta jest wspólna dla REST endpointu i frontendowej legendy +``` + + + + + + + Task 1: Settings — przełącznik trybu sync iCal + zachowanie per_yacht jako default + + wp-content/plugins/yacht-booking-system/includes/class-settings.php, + wp-content/plugins/yacht-booking-system/includes/class-installer.php, + wp-content/plugins/yacht-booking-system/admin/class-admin.php, + wp-content/plugins/yacht-booking-system/admin/views/settings-page.php + + + 1. `Settings`: dodaj typowany getter `get_ical_sync_mode()` zwracający `'per_yacht'` lub `'global'` (default: `'per_yacht'`). Klucz opcji: `yacht_booking_ical_sync_mode`. Dodaj setter w obecnym wzorcu klasy. + 2. `Installer`: dopisz default opcji `yacht_booking_ical_sync_mode = 'per_yacht'` do tablicy defaultów (nie nadpisuj istniejącej wartości jeśli jest). + 3. `Admin::process_settings_save()`: obsłuż nowe pole formularza (sanitize_text_field + whitelista wartości; nieznane → fallback `per_yacht`). + 4. `settings-page.php` (sekcja "Synchronizacja"): dodaj `` dla wyboru jachtu, własny submit handler w JS +- **Verification:** Playwright snapshot — wszystko widoczne +- **Commit:** `class-calendar-widget-all.php`, `calendar-all.css`, `calendar-all.js` + +**4. Kalendarz nie renderował się (tylko nagłówki dni)** +- **Found during:** Po dodaniu layoutu 2-kolumnowego +- **Issue:** FullCalendar `height: '100%'` w gridzie się nie liczyło → komórki collapsed +- **Fix:** Wysokość przekazywana przez `data-height` na wrapperze, JS odczytuje i podaje FC jako liczbę pikseli (`heightPx`) +- **Verification:** Playwright eval — fcDayGridRect ma poprawną wysokość +- **Commit:** `calendar-all.js` + `class-calendar-widget-all.php` + +**5. Select formularza w jasnym natywnym stylu** +- **Found during:** Po dodaniu formularza inquiry +- **Issue:** calendar.css ma reguły dla `input[type=text|email|tel]` ale BRAK dla ` + + + +

+ +

+

+ +

+ + diff --git a/wp-content/plugins/yacht-booking-system/api/class-rest-controller.php b/wp-content/plugins/yacht-booking-system/api/class-rest-controller.php index 5a0bb62..931e5ce 100644 --- a/wp-content/plugins/yacht-booking-system/api/class-rest-controller.php +++ b/wp-content/plugins/yacht-booking-system/api/class-rest-controller.php @@ -24,6 +24,30 @@ class Rest_Controller extends \WP_REST_Controller { */ const NAMESPACE = 'yacht-booking/v1'; + /** + * Deterministyczna paleta kolorów dla widgetu zbiorczego (wszystkie jachty). + * + * Mapowanie: yacht_id (sortowane rosnąco) → palette[index % count]. + * Wspólna dla REST i frontendu (używana przez calendar-all.js do legendy). + */ + const YACHT_COLOR_PALETTE = array( + '#3498db', + '#e74c3c', + '#2ecc71', + '#f39c12', + '#9b59b6', + '#1abc9c', + '#34495e', + '#d35400', + ); + + /** + * Kolor dla wspólnych wydarzeń kalendarza (yacht_id=0, source=ical_global_calendar). + * Jasnoniebieski — dobry kontrast na ciemnogranatowym tle kalendarza, łagodniejszy + * dla oka niż akcent czerwony. + */ + const GLOBAL_EVENT_COLOR = '#7fb3d5'; + /** * Constructor */ @@ -97,6 +121,27 @@ class Rest_Controller extends \WP_REST_Controller { ) ); + // GET /yacht-booking/v1/availability/all + register_rest_route( + self::NAMESPACE, + '/availability/all', + array( + 'methods' => \WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_all_availability' ), + 'permission_callback' => '__return_true', + 'args' => array( + 'start' => array( + 'required' => false, + 'sanitize_callback' => 'sanitize_text_field', + ), + 'end' => array( + 'required' => false, + 'sanitize_callback' => 'sanitize_text_field', + ), + ), + ) + ); + // POST /yacht-booking/v1/bookings register_rest_route( self::NAMESPACE, @@ -297,6 +342,136 @@ class Rest_Controller extends \WP_REST_Controller { return rest_ensure_response( $events ); } + /** + * Get aggregated availability for all yachts + globalne wydarzenia kalendarza. + * + * Zwraca tablicę FullCalendar events (timed 12:00 → 12:00 dla efektu half-day). + * Eventy z yacht_id > 0: kolor z palety per-jacht. + * Eventy yacht_id = 0 (sync_mode=global): kolor `GLOBAL_EVENT_COLOR`. + * + * @param \WP_REST_Request $request Request object. + * @return \WP_REST_Response + */ + public function get_all_availability( $request ) { + $start = $request->get_param( 'start' ); + $end = $request->get_param( 'end' ); + + if ( ! $start || ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $start ) ) { + $start = gmdate( 'Y-m-01' ); + } + if ( ! $end || ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $end ) ) { + $end = gmdate( 'Y-m-d', strtotime( $start . ' +12 months' ) ); + } + + $is_global_mode = ( 'global' === Settings::get_ical_sync_mode() ); + + // Build yacht_id → color map (deterministic by ascending yacht_id). + $yacht_posts = get_posts( + array( + 'post_type' => 'yacht', + 'post_status' => 'publish', + 'posts_per_page' => -1, + 'orderby' => 'ID', + 'order' => 'ASC', + 'fields' => 'ids', + ) + ); + $color_map = self::get_yacht_color_palette( $yacht_posts ); + + // Query bookings overlapping [start, end] with status confirmed or pending. + $bookings = get_posts( + array( + 'post_type' => 'yacht_booking', + 'post_status' => 'publish', + 'posts_per_page' => -1, + 'meta_query' => array( + 'relation' => 'AND', + array( + 'key' => '_booking_status', + 'value' => array( 'confirmed', 'pending' ), + 'compare' => 'IN', + ), + array( + 'key' => '_booking_end_date', + 'value' => $start, + 'compare' => '>=', + 'type' => 'DATE', + ), + array( + 'key' => '_booking_start_date', + 'value' => $end, + 'compare' => '<=', + 'type' => 'DATE', + ), + ), + ) + ); + + $events = array(); + foreach ( $bookings as $booking ) { + $booking_id = $booking->ID; + $start_date = Booking::get_start_date( $booking_id ); + $end_date = Booking::get_end_date( $booking_id ); + + if ( ! $start_date || ! $end_date ) { + continue; + } + + $yacht_id = Booking::get_yacht_id( $booking_id ); + $source = (string) get_post_meta( $booking_id, '_booking_source', true ); + $is_global_event = ( 0 === $yacht_id || \YachtBooking\Integrations\ICal\ICal_Import::GLOBAL_CALENDAR_SOURCE === $source ); + + // W trybie global wszystko traktujemy jak wspólne wydarzenia: szary kolor, + // brak yacht_id, generyczny tytuł "Rezerwacja" — bez wycieku danych klientów. + if ( $is_global_mode || $is_global_event ) { + $color = self::GLOBAL_EVENT_COLOR; + $title = __( 'Rezerwacja', 'yacht-booking' ); + $y_id = 0; + } else { + $color = isset( $color_map[ $yacht_id ] ) ? $color_map[ $yacht_id ] : self::GLOBAL_EVENT_COLOR; + $yacht = get_post( $yacht_id ); + // Tryb per_yacht: pokazujemy tylko nazwę jachtu (bez nazwiska klienta — privacy). + $title = $yacht ? $yacht->post_title : __( 'Rezerwacja', 'yacht-booking' ); + $y_id = $yacht_id; + } + + $events[] = array( + 'id' => $booking_id, + 'title' => $title, + 'start' => $start_date . 'T12:00:00', + 'end' => $end_date . 'T12:00:00', + 'color' => $color, + 'yacht_id' => $y_id, + ); + } + + return rest_ensure_response( $events ); + } + + /** + * Buduje deterministyczną mapę yacht_id → kolor z palety. + * + * Sortuje yacht_ids rosnąco i indeksuje paletę modulo długość. Zapewnia stabilny + * kolor dla danego jachtu między requestami i wspólny przypisanie z frontendem. + * + * @param array $yacht_ids Lista ID jachtów. + * @return array yacht_id => hex color. + */ + public static function get_yacht_color_palette( $yacht_ids ) { + $ids = array_map( 'intval', (array) $yacht_ids ); + sort( $ids, SORT_NUMERIC ); + + $palette = self::YACHT_COLOR_PALETTE; + $count = count( $palette ); + $map = array(); + + foreach ( $ids as $i => $yacht_id ) { + $map[ $yacht_id ] = $palette[ $i % $count ]; + } + + return $map; + } + /** * Create new booking * diff --git a/wp-content/plugins/yacht-booking-system/frontend/assets/css/calendar-all.css b/wp-content/plugins/yacht-booking-system/frontend/assets/css/calendar-all.css new file mode 100644 index 0000000..9839670 --- /dev/null +++ b/wp-content/plugins/yacht-booking-system/frontend/assets/css/calendar-all.css @@ -0,0 +1,113 @@ +/* Yacht Calendar (All) — wspólny widok wszystkich jachtów. + Layout, instrukcja, legenda i formularz dziedziczą z calendar.css + (klasy .yacht-inquiry-layout, .yacht-calendar-instructions, .yacht-calendar-legend, + .yacht-inquiry-form-container, .yacht-inquiry-form). Tutaj tylko nadpisania + specyficzne dla widgetu zbiorczego. */ + +.yacht-calendar-all-wrapper { + max-width: 1200px; + margin: 0 auto 40px; + padding: 20px; + width: 100%; + box-sizing: border-box; +} + +.yacht-calendar-all { + width: 100%; + /* Ciemne granatowe tło — emuluje styl /rezerwacja-maja/ gdzie komórki bg-event + są semi-transparent (#f5f9ff @ 0.66) nad ciemnym tłem parent containera. */ + background: #0e2036; +} + +/* Komórki przyszłe — semi-transparent biały, daje ciemnoszary efekt nad #0e2036 + (efekt identyczny jak yacht-day-available bg-event w single-yacht widget). */ +.yacht-calendar-all .fc-daygrid-day { + background: rgba(245, 249, 255, 0.4); +} + +/* Przeszłe — białe (jak yacht-day-available .fc-event-past w single-yacht). */ +.yacht-calendar-all .fc-daygrid-day.fc-day-past { + background: #ffffff; +} + +/* Dziś — lekko jaśniejszy ciemny. */ +.yacht-calendar-all .fc-daygrid-day.fc-day-today { + background: rgba(255, 255, 255, 0.55); +} + +/* Sąsiedni miesiąc — taki sam jak przyszłe (spójny ciemny). */ +.yacht-calendar-all .fc-daygrid-day.fc-day-other { + background: rgba(245, 249, 255, 0.32); +} + + +/* Select w formularzu zapytania — calendar.css nie ma reguł dla + + + + + + + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +

+ ' . esc_html__( 'regulamin', 'yacht-booking' ) . '' + ); + ?> +

+ + +
+ +
+ +
+ + + + + + 650, + 'show_legend' => 'yes', + ), + $atts, + 'yacht_calendar_all' + ); + + // Lazy-load widget class for the View helper. + if ( ! class_exists( '\YachtBooking\Calendar_All_View' ) ) { + require_once YACHT_BOOKING_PLUGIN_DIR . 'frontend/class-calendar-widget-all.php'; + } + + $dom_id = 'yacht-calendar-all-' . wp_rand( 1000, 9999 ); + $height = (int) $atts['height']; + $show_legend = 'yes' === strtolower( (string) $atts['show_legend'] ); + + return Calendar_All_View::render( $dom_id, $height, $show_legend ); } /** diff --git a/wp-content/plugins/yacht-booking-system/includes/class-installer.php b/wp-content/plugins/yacht-booking-system/includes/class-installer.php index 0fff8a6..f66a9de 100644 --- a/wp-content/plugins/yacht-booking-system/includes/class-installer.php +++ b/wp-content/plugins/yacht-booking-system/includes/class-installer.php @@ -167,6 +167,7 @@ class Installer { 'yacht_booking_global_ical_import_url' => '', 'yacht_booking_global_ical_token' => '', 'yacht_booking_global_ical_last_import' => '', + 'yacht_booking_ical_sync_mode' => 'per_yacht', ); foreach ( $options as $key => $value ) { diff --git a/wp-content/plugins/yacht-booking-system/includes/class-settings.php b/wp-content/plugins/yacht-booking-system/includes/class-settings.php index e01083c..cf7502c 100644 --- a/wp-content/plugins/yacht-booking-system/includes/class-settings.php +++ b/wp-content/plugins/yacht-booking-system/includes/class-settings.php @@ -113,6 +113,20 @@ class Settings { return trim( number_format_i18n( (float) $amount, 2 ) . ' ' . self::get_currency_symbol() ); } + /** + * Get iCal sync mode. + * + * Tryb synchronizacji iCal: + * - per_yacht: import po prefiksie SUMMARY ("Nazwa - opis"), wpisy do availability + * - global: wszystkie eventy zapisane jako wspólne wydarzenia kalendarza, bez wpływu na availability + * + * @return string + */ + public static function get_ical_sync_mode() { + $mode = get_option( 'yacht_booking_ical_sync_mode', 'per_yacht' ); + return in_array( $mode, array( 'per_yacht', 'global' ), true ) ? $mode : 'per_yacht'; + } + /** * Get terms page ID. * diff --git a/wp-content/plugins/yacht-booking-system/includes/class-yacht-booking.php b/wp-content/plugins/yacht-booking-system/includes/class-yacht-booking.php index 62d4b77..799cad2 100644 --- a/wp-content/plugins/yacht-booking-system/includes/class-yacht-booking.php +++ b/wp-content/plugins/yacht-booking-system/includes/class-yacht-booking.php @@ -77,6 +77,9 @@ class Yacht_Booking { require_once YACHT_BOOKING_PLUGIN_DIR . 'includes/class-booking.php'; require_once YACHT_BOOKING_PLUGIN_DIR . 'includes/class-availability.php'; + // REST controller — eagerly loaded because View helpers use its color palette + stałe. + require_once YACHT_BOOKING_PLUGIN_DIR . 'api/class-rest-controller.php'; + // Load admin classes if ( is_admin() ) { require_once YACHT_BOOKING_PLUGIN_DIR . 'admin/class-admin.php'; @@ -157,6 +160,24 @@ class Yacht_Booking { true ); + // Calendar (all yachts) — wspólny widget. Załaduj tylko gdy strona go potrzebuje. + if ( $this->should_load_calendar_all_assets() ) { + wp_enqueue_style( + 'yacht-booking-calendar-all', + YACHT_BOOKING_PLUGIN_URL . 'frontend/assets/css/calendar-all.css', + array( 'fullcalendar' ), + filemtime( YACHT_BOOKING_PLUGIN_DIR . 'frontend/assets/css/calendar-all.css' ) + ); + + wp_enqueue_script( + 'yacht-booking-calendar-all', + YACHT_BOOKING_PLUGIN_URL . 'frontend/assets/js/calendar-all.js', + array( 'jquery', 'fullcalendar', 'fullcalendar-pl' ), + filemtime( YACHT_BOOKING_PLUGIN_DIR . 'frontend/assets/js/calendar-all.js' ), + true + ); + } + // Localize script wp_localize_script( 'yacht-booking-calendar', @@ -228,8 +249,53 @@ class Yacht_Booking { return true; } - // Check if post contains yacht calendar shortcode or widget - if ( $post && ( has_shortcode( $post->post_content, 'yacht_calendar' ) || $this->has_yacht_calendar_widget( $post->ID ) ) ) { + // Check if post contains yacht calendar shortcode or widget (per-jacht lub wszystkie) + if ( $post ) { + if ( has_shortcode( $post->post_content, 'yacht_calendar' ) + || has_shortcode( $post->post_content, 'yacht_calendar_all' ) + || $this->has_yacht_calendar_widget( $post->ID ) + || $this->has_yacht_calendar_all_widget( $post->ID ) ) { + return true; + } + } + + return false; + } + + /** + * Check if post contains the wspólny widget kalendarza floty. + * + * @param int $post_id Post ID. + * @return bool + */ + private function has_yacht_calendar_all_widget( $post_id ) { + if ( ! class_exists( '\Elementor\Plugin' ) ) { + return false; + } + + $document = \Elementor\Plugin::$instance->documents->get( $post_id ); + if ( ! $document ) { + return false; + } + + $data = $document->get_elements_data(); + return $this->find_widget_recursive( $data, 'yacht-calendar-all' ); + } + + /** + * Check if calendar-all (wspólny) assets should be loaded — used to enqueue + * dodatkowe pliki tylko na stronach które ich potrzebują. + * + * @return bool + */ + private function should_load_calendar_all_assets() { + global $post; + + if ( class_exists( '\Elementor\Plugin' ) && \Elementor\Plugin::$instance->preview->is_preview_mode() ) { + return true; + } + + if ( $post && ( has_shortcode( $post->post_content, 'yacht_calendar_all' ) || $this->has_yacht_calendar_all_widget( $post->ID ) ) ) { return true; } @@ -296,11 +362,13 @@ class Yacht_Booking { * @param object $widgets_manager Elementor widgets manager. */ public function register_elementor_widgets( $widgets_manager ) { - // Load widget class + // Load widget classes require_once YACHT_BOOKING_PLUGIN_DIR . 'frontend/class-calendar-widget.php'; + require_once YACHT_BOOKING_PLUGIN_DIR . 'frontend/class-calendar-widget-all.php'; - // Register widget + // Register widgets $widgets_manager->register( new Calendar_Widget() ); + $widgets_manager->register( new Calendar_Widget_All() ); } /** diff --git a/wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-import.php b/wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-import.php index 60b197e..b678948 100644 --- a/wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-import.php +++ b/wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-import.php @@ -11,6 +11,7 @@ namespace YachtBooking\Integrations\ICal; use YachtBooking\Availability; +use YachtBooking\Settings; use YachtBooking\Yacht; // Exit if accessed directly. @@ -29,6 +30,14 @@ class ICal_Import { */ const GLOBAL_IMPORT_SOURCE = 'ical_import_global'; + /** + * Booking source identifier dla trybu "wspólny kalendarz" (sync_mode=global). + * + * Eventy z tym source nie blokują dostępności jachtów (yacht_id=0) i są pokazywane + * tylko na widgecie zbiorczym "wszystkie jachty". + */ + const GLOBAL_CALENDAR_SOURCE = 'ical_global_calendar'; + /** * Separator między prefiksem nazwy jachtu a resztą tytułu eventu. * @@ -95,13 +104,30 @@ class ICal_Import { return false; } - $events = self::parse_ics( $body ); + $events = self::parse_ics( $body ); + $mode = Settings::get_ical_sync_mode(); + + if ( 'global' === $mode ) { + self::run_global_calendar_mode( $events ); + } else { + self::run_per_yacht_mode( $events ); + } + + update_option( 'yacht_booking_global_ical_last_import', current_time( 'mysql' ) ); + return true; + } + + /** + * Tryb per-jacht: dopasowanie po prefiksie SUMMARY, wpisy do availability. + * + * @param array $events Sparsowane eventy iCal. + */ + protected static function run_per_yacht_mode( $events ) { $yacht_map = self::build_yacht_lookup_map(); if ( empty( $yacht_map ) ) { - self::log( 'Global iCal: no yachts in DB — nothing to match', 'error' ); - update_option( 'yacht_booking_global_ical_last_import', current_time( 'mysql' ) ); - return true; + self::log( 'Per-yacht iCal: no yachts in DB — nothing to match', 'error' ); + return; } $existing_map = self::get_existing_global_import_map(); @@ -117,11 +143,11 @@ class ICal_Import { continue; } - $summary = isset( $event['summary'] ) ? (string) $event['summary'] : ''; + $summary = isset( $event['summary'] ) ? (string) $event['summary'] : ''; $yacht_id = self::match_yacht_by_prefix( $summary, $yacht_map ); if ( ! $yacht_id ) { - self::log( sprintf( 'Global iCal: skip event "%s" — no yacht match for prefix', $summary ) ); + self::log( sprintf( 'Per-yacht iCal: skip event "%s" — no yacht match for prefix', $summary ) ); continue; } @@ -131,20 +157,76 @@ class ICal_Import { $booking_id = self::upsert_global_booking( $yacht_id, $event, $existing_id ); if ( ! $booking_id ) { - self::log( sprintf( 'Global iCal: failed to upsert event %s', $event['uid'] ), 'error' ); + self::log( sprintf( 'Per-yacht iCal: failed to upsert event %s', $event['uid'] ), 'error' ); } } // Stale cleanup — usuń bookingi których UID nie ma już w feedzie. + // Ograniczone do GLOBAL_IMPORT_SOURCE (per-yacht) — nie tyka GLOBAL_CALENDAR_SOURCE. foreach ( $existing_map as $uid => $booking_id ) { if ( ! in_array( $uid, $seen_uids, true ) ) { \YachtBooking\Availability::clear_booking_availability( $booking_id ); wp_delete_post( $booking_id, true ); } } + } - update_option( 'yacht_booking_global_ical_last_import', current_time( 'mysql' ) ); - return true; + /** + * Tryb global: wszystkie eventy (bez filtrowania) zapisane jako wspólne wydarzenia + * kalendarza. yacht_id=0, brak wpisów do wp_yacht_availability. + * + * @param array $events Sparsowane eventy iCal. + */ + protected static function run_global_calendar_mode( $events ) { + $existing_map = self::get_existing_global_calendar_map(); + $seen_uids = array(); + $imported = 0; + $updated = 0; + + foreach ( $events as $event ) { + if ( empty( $event['uid'] ) || empty( $event['start'] ) || empty( $event['end'] ) ) { + continue; + } + + // Skip past events. + if ( strtotime( $event['end'] ) < time() ) { + continue; + } + + $seen_uids[] = $event['uid']; + $existing_id = isset( $existing_map[ $event['uid'] ] ) ? (int) $existing_map[ $event['uid'] ] : 0; + + $booking_id = self::upsert_global_calendar_event( $event, $existing_id ); + + if ( ! $booking_id ) { + self::log( sprintf( 'Global calendar iCal: failed to upsert event %s', $event['uid'] ), 'error' ); + continue; + } + + if ( $existing_id > 0 ) { + $updated++; + } else { + $imported++; + } + } + + // Stale cleanup — usuń tylko eventy z GLOBAL_CALENDAR_SOURCE których UID brakuje. + $deleted = 0; + foreach ( $existing_map as $uid => $booking_id ) { + if ( ! in_array( $uid, $seen_uids, true ) ) { + wp_delete_post( $booking_id, true ); + $deleted++; + } + } + + self::log( + sprintf( + 'Global calendar iCal: imported=%d, updated=%d, deleted=%d', + $imported, + $updated, + $deleted + ) + ); } /** @@ -297,6 +379,95 @@ class ICal_Import { return (int) $booking_id; } + /** + * Mapa istniejących eventów GLOBAL_CALENDAR_SOURCE (tryb global): uid => booking_id. + * + * @return array + */ + protected static function get_existing_global_calendar_map() { + $bookings = get_posts( + array( + 'post_type' => 'yacht_booking', + 'post_status' => 'publish', + 'posts_per_page' => -1, + 'fields' => 'ids', + 'meta_query' => array( + array( + 'key' => '_booking_source', + 'value' => self::GLOBAL_CALENDAR_SOURCE, + ), + ), + ) + ); + + $map = array(); + foreach ( $bookings as $booking_id ) { + $uid = get_post_meta( $booking_id, '_ical_event_uid', true ); + if ( $uid ) { + $map[ $uid ] = (int) $booking_id; + } + } + + return $map; + } + + /** + * Tworzy lub aktualizuje wspólne wydarzenie kalendarza (sync_mode=global). + * + * yacht_id=0, brak wpisów do availability — event tylko informacyjny na widgecie zbiorczym. + * + * @param array $event Parsed event data. + * @param int $existing_id Existing booking ID (0 dla nowego). + * @return int|false + */ + protected static function upsert_global_calendar_event( $event, $existing_id = 0 ) { + $summary = ! empty( $event['summary'] ) + ? sanitize_text_field( $event['summary'] ) + : __( 'Wydarzenie kalendarza', 'yacht-booking' ); + + $post_data = array( + 'post_type' => 'yacht_booking', + 'post_status' => 'publish', + 'post_title' => sprintf( + /* translators: %s: event summary */ + __( 'GCal (wspólny): %s', 'yacht-booking' ), + $summary + ), + ); + + if ( $existing_id > 0 ) { + $post_data['ID'] = $existing_id; + $booking_id = wp_update_post( $post_data, true ); + } else { + $booking_id = wp_insert_post( $post_data, true ); + } + + if ( is_wp_error( $booking_id ) || ! $booking_id ) { + return false; + } + + update_post_meta( $booking_id, '_booking_yacht_id', 0 ); + update_post_meta( $booking_id, '_booking_start_date', $event['start'] ); + update_post_meta( $booking_id, '_booking_end_date', $event['end'] ); + update_post_meta( $booking_id, '_booking_status', 'confirmed' ); + update_post_meta( $booking_id, '_booking_customer_name', __( 'Google Calendar (kalendarz wspólny)', 'yacht-booking' ) ); + update_post_meta( $booking_id, '_booking_customer_email', '' ); + update_post_meta( $booking_id, '_booking_customer_phone', '' ); + update_post_meta( $booking_id, '_booking_total_price', 0 ); + update_post_meta( $booking_id, '_booking_source', self::GLOBAL_CALENDAR_SOURCE ); + update_post_meta( $booking_id, '_ical_event_uid', $event['uid'] ); + update_post_meta( $booking_id, '_booking_notes', $summary ); + + // CELOWO BEZ Availability::mark_as_booked() — wspólne eventy nie blokują dostępności jachtów. + // Defensywnie: jeśli kiedyś existing_id miał wpisy availability (np. po przełączeniu trybu), + // usuń je, by nie zostawiały śmieci. + if ( $existing_id > 0 ) { + \YachtBooking\Availability::clear_booking_availability( $existing_id ); + } + + return (int) $booking_id; + } + /** * Parse .ics content into array of events. * diff --git a/wp-content/plugins/yacht-booking-system/yacht-booking-system.php b/wp-content/plugins/yacht-booking-system/yacht-booking-system.php index afd7b5a..a6e0d71 100644 --- a/wp-content/plugins/yacht-booking-system/yacht-booking-system.php +++ b/wp-content/plugins/yacht-booking-system/yacht-booking-system.php @@ -3,7 +3,7 @@ * Plugin Name: Yacht Booking System * Plugin URI: https://jachty.pagedev.pl * Description: System rezerwacji jachtów z kalendarzem i integracją z Google Calendar - * Version: 1.0.0 + * Version: 1.1.0 * Author: PageDev * Author URI: https://pagedev.pl * Text Domain: yacht-booking @@ -20,7 +20,7 @@ if ( ! defined( 'ABSPATH' ) ) { } // Define plugin constants -define( 'YACHT_BOOKING_VERSION', '1.0.0' ); +define( 'YACHT_BOOKING_VERSION', '1.1.0' ); define( 'YACHT_BOOKING_PLUGIN_FILE', __FILE__ ); define( 'YACHT_BOOKING_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); define( 'YACHT_BOOKING_PLUGIN_URL', plugin_dir_url( __FILE__ ) );