--- 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 `