This commit is contained in:
2026-05-08 00:12:37 +02:00
parent 811069a25c
commit 7278a422af
18 changed files with 1356 additions and 43 deletions

View File

@@ -0,0 +1,139 @@
---
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*