--- phase: 09-finalizacja plan: 06 subsystem: ui, api tags: [fullcalendar, validRange, rest, ux] requires: - phase: 09-finalizacja provides: widget zbiorczy `[yacht_calendar_all]` + REST `/availability/all` (09-04, 09-05) provides: - REST endpoint `/availability/bounds` (publiczny, max booking date) - JS bootstrap fetch bounds przed init FullCalendar - validRange w FullCalendar (prev disabled na bieżącym miesiącu, next disabled po miesiącu max booking) affects: [security audit (09-07) — kolejny publiczny endpoint do oceny] tech-stack: added: [] patterns: - bootstrap fetch w JS (osobny request przed instancjacją FC) + graceful degradation w fail callback - publiczny REST endpoint zwracający tylko zagregowaną datę (brak ujawnienia rezerwacji) - validRange jako kontrakt server↔client dla nawigacji key-files: created: [] modified: - 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 key-decisions: - "Endpoint publiczny (`__return_true`) — frontend renderuje widget dla anonimowych userów" - "Filtr `_booking_end_date >= dziś` — historyczne rezerwacje nie rozszerzają zakresu w przyszłość" - "Graceful degradation: fail bounds → kalendarz bez validRange (lepsze niż brak kalendarza)" - "validRange.end exclusive (pierwszy dzień miesiąca PO maxDate) — daje cały miesiąc maxDate dostępny" patterns-established: - "Boundary endpoints: lekkie REST endpointy zwracające metadane (max date, count) zamiast zagregowanych danych — minimalizuje payload + privacy" - "Fetch-before-init pattern w FullCalendar: jeden bootstrap call, potem instancjacja z opcjami zależnymi od odpowiedzi" duration: ~25min started: 2026-05-08T00:00:00Z completed: 2026-05-08T00:25:00Z --- # Phase 09 Plan 06: Blokada nawigacji kalendarza **Kalendarz zbiorczy `[yacht_calendar_all]` na `/rezerwacja/` blokuje nawigację: prev disabled na bieżącym miesiącu, next disabled po miesiącu zawierającym ostatnią rezerwację confirmed/pending — bazując na nowym publicznym REST endpoint `/availability/bounds`.** ## Performance | Metric | Value | |--------|-------| | Duration | ~25min | | Started | 2026-05-08T00:00:00Z | | Completed | 2026-05-08T00:25:00Z | | Tasks | 3 auto + 1 checkpoint | | Files modified | 3 | ## Acceptance Criteria Results | Criterion | Status | Notes | |-----------|--------|-------| | AC-1: REST zwraca max booking date | Pass | `get_availability_bounds()` filtruje confirmed/pending + end >= dziś, sortuje DESC po `_booking_end_date` | | AC-2: Brak rezerwacji → null | Pass | `max_booking_date: null` gdy `get_posts` pusty | | AC-3: Prev disabled w bieżącym miesiącu | Pass | `validRange.start = pierwszy dzień bieżącego miesiąca` — FC sam wyłącza prev | | AC-4: Next disabled na miesiącu max booking | Pass | `validRange.end = pierwszy dzień miesiąca PO maxDate` (exclusive) | | AC-5: Next disabled gdy brak rezerwacji | Pass | `rangeEnd = nextMonthFirst(today)` gdy maxDate null lub w przeszłości | | AC-6: Nawigacja w obrębie zakresu | Pass | Klient zatwierdził w checkpoincie | ## Accomplishments - **REST `/availability/bounds`** — lekki publiczny endpoint zwracający tylko `{ max_booking_date }`, z filtrem statusu (confirmed/pending) i `end_date >= dziś`. - **Bootstrap fetch w JS** — bounds pobierane przed instancjacją FC, z graceful degradation w `fail` callback. - **validRange w FullCalendar** — start = pierwszy dzień bieżącego miesiąca (prev blocked), end = pierwszy dzień miesiąca PO miesiącu ostatniej rezerwacji (exclusive — daje cały miesiąc maxDate dostępny). - **Plugin v1.2.1** — cache busting po deploy. ## Files Created/Modified | File | Change | Purpose | |------|--------|---------| | `api/class-rest-controller.php` | Modified | Nowy route `/availability/bounds` + metoda `get_availability_bounds()` (sortowanie DESC po meta_value DATE, filtr status + end_date >= dziś) | | `frontend/assets/js/calendar-all.js` | Modified | Helpers `firstOfMonth`/`nextMonthFirst`; `initCalendar` rozdzielony na fetch bounds + nową funkcję `buildCalendar` z `validRange` | | `yacht-booking-system.php` | Modified | Bump 1.2.0 → 1.2.1 | ## Decisions Made | Decision | Rationale | Impact | |----------|-----------|--------| | Endpoint publiczny | Frontend renderuje widget dla anonimowych userów; brak nonce wymaga `__return_true` | Każdy odwiedzający może zobaczyć datę ostatniej rezerwacji (security audit do oceny) | | Filtr `end_date >= dziś` w SQL | Historyczne rezerwacje nie powinny rozszerzać zakresu w przyszłość | Próba np. usunięcia ostatniej przyszłej rezerwacji od razu skraca dostępny zakres po reload | | Graceful degradation w fail | Lepiej pokazać kalendarz bez validRange niż wcale | Awaria endpointu nie psuje strony; userzy nadal widzą kalendarz | | validRange.end exclusive | FC tak interpretuje validRange — exclusive end | `nextMonthFirst(maxDate)` daje cały miesiąc maxDate dostępny | ## Deviations from Plan ### Summary | Type | Count | Impact | |------|-------|--------| | Auto-fixed | 0 | — | | Scope additions | 0 | — | | Deferred | 0 | — | **Total impact:** Plan wykonany dokładnie jak zaplanowany. Brak iteracji UX po checkpoincie. ### Deferred Items None. ## Issues Encountered | Issue | Resolution | |-------|------------| | Edit z literówką `new_str ng` zamiast `new_string` | Powtórzony Edit z prawidłowym kluczem | ## Skill Audit `.paul/SPECIAL-FLOWS.md` nie istnieje — skill audit pominięty. ## Next Phase Readiness **Ready:** - Widget zbiorczy `/rezerwacja/` w pełni funkcjonalny i ograniczony do sensownego zakresu dat - Kolejny publiczny endpoint `/availability/bounds` do uwzględnienia w security audit (09-07) - Plugin v1.2.1 deployed **Concerns:** - **Trzy publiczne endpointy w REST API** (`/availability/{yacht_id}`, `/availability/all`, `/availability/bounds`) — security audit (09-07) powinien przeanalizować łącznie: 1. Czy `/bounds` przez ujawnienie max_booking_date nie daje informacji wrażliwej (typowo nie — tylko data graniczna). 2. Czy `/all` nadal nie wycieka `customer_email`/`customer_phone` (potwierdzono w 09-05 że nie). 3. Throttling/rate-limiting publicznych endpointów. - Brak automatycznych testów — regresje w `validRange` mogą umknąć przy kolejnych zmianach JS **Blockers:** - None --- *Phase: 09-finalizacja, Plan: 06* *Completed: 2026-05-08*