Files
2026-05-08 00:12:37 +02:00

11 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, delegation
phase plan type wave depends_on files_modified autonomous delegation
09-finalizacja 06 execute 1
09-05
wp-content/plugins/yacht-booking-system/api/class-rest-controller.php
wp-content/plugins/yacht-booking-system/frontend/assets/js/calendar-all.js
wp-content/plugins/yacht-booking-system/yacht-booking-system.php
false off
## Goal Ograniczyć nawigację na kalendarzu zbiorczym `[yacht_calendar_all]` (`/rezerwacja/`): - Przycisk **"prev"** zablokowany gdy aktualny widok = bieżący miesiąc (nie da się cofnąć w przeszłość). - Przycisk **"next"** zablokowany gdy nie istnieje żadna rezerwacja w miesiącach późniejszych niż widok bieżący — efektywnie: max widok = miesiąc, w którym kończy się ostatnia rezerwacja.

Purpose

Klient nie chce, żeby odwiedzający stronę /rezerwacja/ przeglądali historię rezerwacji ani puste przyszłe miesiące — kalendarz ma pokazywać tylko sensowny przedział "od dziś do ostatniej znanej rezerwacji".

Output

  • Nowy REST endpoint GET /yacht-booking/v1/availability/bounds zwracający { max_booking_date: 'YYYY-MM-DD' | null } (najpóźniejsza data zakończenia rezerwacji confirmed/pending).
  • JS calendar-all.js przed inicjalizacją FullCalendar fetchuje bounds i ustawia validRange blokujący nawigację.
  • Plugin bumped → 1.2.1 (cache busting).
- **Nawigacja w przód** — Jak ma działać blokada w przód? → Odpowiedź: Do miesiąca ostatniej rezerwacji (next aktywny tylko gdy istnieje rezerwacja w późniejszym miesiącu). - **Zakres** — Który kalendarz dotyczy tej zmiany? → Odpowiedź: Tylko widget zbiorczy `[yacht_calendar_all]` na `/rezerwacja/`. Single-yacht (`/rezerwacja-maja/` itp.) bez zmian. - **Granica wstecz** — Od kiedy nie można się cofać? → Odpowiedź: Bieżący miesiąc (dziś) — prev wyłączony gdy widok = aktualny miesiąc.

Project Context

@.paul/PROJECT.md @.paul/STATE.md @.paul/codebase/architecture.md

Source Files

@wp-content/plugins/yacht-booking-system/api/class-rest-controller.php @wp-content/plugins/yacht-booking-system/frontend/assets/js/calendar-all.js @wp-content/plugins/yacht-booking-system/yacht-booking-system.php

<acceptance_criteria>

AC-1: REST endpoint zwraca max booking date

Given baza zawiera rezerwacje confirmed/pending z `_booking_end_date` od 2026-05-15 do 2026-08-20
When frontend wywołuje GET /wp-json/yacht-booking/v1/availability/bounds
Then odpowiedź = `{ "max_booking_date": "2026-08-20" }` (HTTP 200, public, brak nonce required)

AC-2: Brak rezerwacji → null

Given baza nie zawiera żadnych rezerwacji confirmed/pending z `_booking_end_date >= dziś`
When frontend wywołuje GET /availability/bounds
Then odpowiedź = `{ "max_booking_date": null }`

AC-3: Prev disabled w bieżącym miesiącu

Given strona /rezerwacja/ wczytana, widok = aktualny miesiąc (dziś)
When kalendarz wyrenderowany
Then przycisk "prev" w toolbar FC ma atrybut `disabled` (lub klasę `fc-button-disabled`); klik nie zmienia widoku
And przycisk "today" również zachowuje się normalnie (jest na bieżącym miesiącu — nic się nie zmienia)

AC-4: Next disabled na/po miesiącu ostatniej rezerwacji

Given max_booking_date = "2026-08-20", widok bieżący = sierpień 2026
When kalendarz wyrenderowany
Then przycisk "next" wyłączony (validRange.end = 2026-09-01)

AC-5: Next disabled gdy brak przyszłych rezerwacji

Given max_booking_date = null
When kalendarz wyrenderowany na bieżącym miesiącu
Then przyciski "prev" i "next" są wyłączone (validRange = bieżący miesiąc tylko)

AC-6: Nawigacja w obrębie zakresu działa

Given max_booking_date = "2026-08-20", aktualny miesiąc = maj 2026, widok = maj 2026
When user klika "next" 3 razy
Then widok zmienia się czerwiec → lipiec → sierpień; w sierpniu "next" jest wyłączony

</acceptance_criteria>

Task 1: REST endpoint /availability/bounds wp-content/plugins/yacht-booking-system/api/class-rest-controller.php W `Rest_Controller::register_rest_routes()` dodaj rejestrację nowego route'u (obok istniejącego `/availability/all`):
```php
register_rest_route(
  self::NAMESPACE,
  '/availability/bounds',
  array(
    'methods'             => 'GET',
    'callback'            => array( $this, 'get_availability_bounds' ),
    'permission_callback' => '__return_true',
  )
);
```

Dodaj nową metodę publiczną `get_availability_bounds( $request )`:
- Zapytanie WP_Query lub `get_posts` o `post_type = yacht_booking`, `post_status = publish`, status confirmed/pending (`_booking_status` IN), `posts_per_page = 1`, sortowane malejąco po `_booking_end_date` (meta_key + orderby=meta_value + meta_type=DATE).
- Pobierz `_booking_end_date` z pierwszego wyniku.
- Walidacja: jeśli niepuste i pasuje do `^\d{4}-\d{2}-\d{2}$` → zwróć `array( 'max_booking_date' => $date )`. W przeciwnym razie `array( 'max_booking_date' => null )`.
- Filtruj tylko rezerwacje z `_booking_end_date >= dziś` (gmdate('Y-m-d')) — historyczne nie powinny rozszerzać zakresu.

Avoid: dotykać innych endpointów, nie zmieniaj `get_all_availability`. Brak `permission_callback => 'admin'` — endpoint publiczny (potrzebny przy renderze widgetu dla anonimowych userów).
1. `php -l class-rest-controller.php` → no syntax errors. 2. Po deploy: `curl 'https://jachty3.pagedev.pl/wp-json/yacht-booking/v1/availability/bounds'` → JSON z polem `max_booking_date`. 3. Wartość powinna pasować do najpóźniejszej rezerwacji w bazie (confirmed/pending, end_date >= dziś). AC-1, AC-2 spełnione. Task 2: JS bootstrap bounds + validRange wp-content/plugins/yacht-booking-system/frontend/assets/js/calendar-all.js Zmień `initCalendar(wrapper)` tak, by przed `new FullCalendar.Calendar(...)` wykonał `$.getJSON` do `/availability/bounds` i ustawił `validRange` na bazie odpowiedzi:
1. Helper na początku pliku (poza initCalendar):
   ```js
   function firstOfMonth(d) {
     return new Date(d.getFullYear(), d.getMonth(), 1);
   }
   function nextMonthFirst(d) {
     return new Date(d.getFullYear(), d.getMonth() + 1, 1);
   }
   ```

2. W `initCalendar`: wyciągnij `restBase` z `restUrl` (zamień `/availability/all` na bazową ścieżkę REST namespace, czyli najprościej: zbuduj `boundsUrl` przez `restUrl.replace(/\/availability\/all.*$/, '/availability/bounds')`).

3. Wykonaj `$.getJSON(boundsUrl)` PRZED instancjacją FC. W `done` callback:
   - `var today = new Date();`
   - `var rangeStart = firstOfMonth(today);`
   - `var maxDate = data && data.max_booking_date ? new Date(data.max_booking_date + 'T00:00:00') : null;`
   - `var rangeEnd;`
     - Jeśli `maxDate && maxDate >= today`: `rangeEnd = nextMonthFirst(maxDate);` (validRange.end jest exclusive — daje cały miesiąc maxDate)
     - W przeciwnym wypadku (null lub przeszłość): `rangeEnd = nextMonthFirst(today);` (tylko bieżący miesiąc dostępny)
   - Następnie wywołaj funkcję `buildCalendar(wrapper, $cal, restUrl, heightPx, rangeStart, rangeEnd)` która zawiera dotychczasową logikę inicjalizacji FC z dodanym `validRange: { start: rangeStart, end: rangeEnd }` w opcjach Calendar.
 - W `fail` callback: zbuduj kalendarz bez `validRange` (graceful degradation — lepsze niż brak kalendarza).

4. Pozostała logika (events fetch, eventDidMount, eventContent, form submit) bez zmian.

Avoid: nie usuwaj istniejących handlerów (form submit, half-day gradient, tooltip). Nie zmieniaj `eventContent` ani `applyHalfDayGradient`.
1. W przeglądarce na /rezerwacja/: DevTools Network — request do `/availability/bounds` przed/równolegle do `/availability/all`. 2. Klik "prev" w bieżącym miesiącu — brak akcji; przycisk wizualnie disabled. 3. Klik "next" wielokrotny — kalendarz dochodzi do miesiąca ostatniej rezerwacji i tam się zatrzymuje. AC-3, AC-4, AC-5, AC-6 spełnione. Task 3: Bump wersji pluginu wp-content/plugins/yacht-booking-system/yacht-booking-system.php Zmień `Version: 1.2.0` → `Version: 1.2.1` i `define( 'YACHT_BOOKING_VERSION', '1.2.0' )` → `'1.2.1'` (2 wystąpienia). `grep -n "1.2.1"` → 2 trafienia. Wersja podbita; assets cache busted po deploy. - REST `/availability/bounds` zwraca `max_booking_date` (data ostatniej rezerwacji confirmed/pending). - JS przed init kalendarza pobiera bounds i ustawia `validRange` (start = pierwszy dzień bieżącego miesiąca, end = pierwszy dzień miesiąca PO miesiącu max_booking_date — exclusive). - Prev disabled na bieżącym miesiącu, next disabled na/po miesiącu ostatniej rezerwacji. - Plugin v1.2.1. 1. Wgraj 3 zmienione pliki przez ftp-kr. 2. Otwórz https://jachty3.pagedev.pl/rezerwacja/ z Ctrl+F5. 3. Zweryfikuj: - Prev przycisk wyglądnięci (disabled / wyszarzony) bo widok = bieżący miesiąc. - Klikaj "next" — kalendarz przechodzi przez miesiące tylko do miesiąca, w którym kończy się ostatnia rezerwacja. - W ostatnim miesiącu z rezerwacjami next staje się disabled. 4. DevTools → Network → request `/availability/bounds` zwraca poprawny JSON. 5. Edge case (jeśli możliwe do przetestowania): wyłącz wszystkie przyszłe rezerwacje → reload → kalendarz pokazuje tylko bieżący miesiąc, oba przyciski disabled. Wpisz "approved" lub opisz problemy.

DO NOT CHANGE

  • get_all_availability() w REST — kontrakt eventów per-day allDay zachowany.
  • Single-yacht widget (frontend/class-calendar-widget.php, calendar.js, calendar.css) — out of scope.
  • Inquiry form i jego submit handler — out of scope.
  • eventContent, applyHalfDayGradient, eventDidMount — bez zmian.

SCOPE LIMITS

  • Plan dotyczy tylko widgetu zbiorczego na /rezerwacja/.
  • NIE dodajemy admin-only logiki (publiczny endpoint).
  • NIE cache'ujemy odpowiedzi /bounds długoterminowo (jeden fetch per page load wystarczy; dane zmieniają się rzadko).
  • NIE zmieniamy nawigacji per-rok (tylko miesiąc-po-miesiącu).
- [ ] `php -l` na class-rest-controller.php pass - [ ] curl `/bounds` zwraca poprawny JSON - [ ] Prev disabled w bieżącym miesiącu - [ ] Next disabled na miesiącu max_booking_date - [ ] Wszystkie AC spełnione

<success_criteria>

  • 3 zadania auto + checkpoint zatwierdzone
  • Brak fatal errors / warnings PHP
  • Klient zaakceptował zachowanie nawigacji
  • Plugin v1.2.1 deployed </success_criteria>
After completion, create `.paul/phases/09-finalizacja/09-06-SUMMARY.md`