update
This commit is contained in:
@@ -50,8 +50,9 @@ Szczegóły w `wp-content/plugins/yacht-booking-system/PROJECT-STATUS.md`.
|
||||
- [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
|
||||
- [x] 09-05: UX rezerwacja — tytuły rezerwacji z GCal w paskach + per-day allDay events + tooltip na hover (cofa privacy z 09-04 per żądanie klienta) ✅ 2026-05-07
|
||||
- [x] 09-06: UX rezerwacja — blokada nawigacji kalendarza (REST `/availability/bounds` + validRange w FC) ✅ 2026-05-08
|
||||
- [ ] 09-07: Security audit i poprawki
|
||||
- [ ] 09-08: Testy + tłumaczenia + dokumentacja
|
||||
- [ ] 09-07: Kolory per jacht w kalendarzu zbiorczym (color picker w settings + dopasowanie po nazwie w tytule globalnych eventów GCal)
|
||||
- [ ] 09-08: Security audit i poprawki
|
||||
- [ ] 09-09: Testy + tłumaczenia + dokumentacja
|
||||
|
||||
---
|
||||
*Roadmap created: 2026-05-05*
|
||||
|
||||
@@ -10,21 +10,21 @@ See: .paul/PROJECT.md (updated 2026-05-05)
|
||||
## Current Position
|
||||
|
||||
Milestone: v1.0 Production Release (v1.0.0)
|
||||
Phase: 9 of 9 (Finalizacja) — In progress
|
||||
Plan: 09-06 — Complete (blokada nawigacji kalendarza)
|
||||
Status: Loop closed, ready for next plan (09-07 Security audit)
|
||||
Last activity: 2026-05-08 — Closed loop 09-06 (validRange + REST /availability/bounds)
|
||||
Phase: 9 of 9 (Finalizacja) — Planning
|
||||
Plan: 09-07 created, awaiting approval (Kolory per jacht w kalendarzu zbiorczym)
|
||||
Status: PLAN created, ready for APPLY
|
||||
Last activity: 2026-05-10 — Created .paul/phases/09-finalizacja/09-07-PLAN.md
|
||||
|
||||
Progress:
|
||||
- Milestone: [█████████░] 97%
|
||||
- Phase 9: [████████░░] 75% (6 of 8 plans complete)
|
||||
- Phase 9: [██████░░░░] 67% (6 of 9 plans complete — security i docs przesunięte na 09-08/09-09)
|
||||
|
||||
## Loop Position
|
||||
|
||||
Current loop state:
|
||||
```
|
||||
PLAN ──▶ APPLY ──▶ UNIFY
|
||||
✓ ✓ ✓ [Loop 09-06 complete, ready for 09-07]
|
||||
✓ ○ ○ [Plan 09-07 created, awaiting approval]
|
||||
```
|
||||
|
||||
## Accumulated Context
|
||||
@@ -44,9 +44,9 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||
|
||||
| Issue | Origin | Effort | Revisit |
|
||||
|-------|--------|--------|---------|
|
||||
| Tłumaczenia PL | Phase 1-8 | M | Phase 9 (plan 09-08) |
|
||||
| Security audit | Phase 1-8 + privacy revert 09-05 + bounds 09-06 | M | Phase 9 (plan 09-07) |
|
||||
| Dokumentacja PHP Doc | Phase 1-8 | L | Phase 9 (plan 09-08) |
|
||||
| Tłumaczenia PL | Phase 1-8 | M | Phase 9 (plan 09-09) |
|
||||
| Security audit | Phase 1-8 + privacy revert 09-05 + bounds 09-06 | M | Phase 9 (plan 09-08) |
|
||||
| Dokumentacja PHP Doc | Phase 1-8 | L | Phase 9 (plan 09-09) |
|
||||
|
||||
### Blockers/Concerns
|
||||
|
||||
@@ -54,14 +54,14 @@ None.
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-05-08
|
||||
Stopped at: Loop 09-06 zamknięty — blokada nawigacji kalendarza zatwierdzona
|
||||
Next action: Run /paul:plan to plan 09-07 (Security audit)
|
||||
Resume file: .paul/phases/09-finalizacja/09-06-SUMMARY.md
|
||||
Last session: 2026-05-10
|
||||
Stopped at: Plan 09-07 utworzony — kolory per jacht w kalendarzu zbiorczym
|
||||
Next action: Review and approve plan, then run /paul:apply .paul/phases/09-finalizacja/09-07-PLAN.md
|
||||
Resume file: .paul/phases/09-finalizacja/09-07-PLAN.md
|
||||
Resume context:
|
||||
- Faza 9: 6/8 planów ukończonych (75%), milestone v1.0 97%
|
||||
- Plugin v1.2.1: validRange w widgecie zbiorczym (prev disabled na bieżącym miesiącu, next disabled po maxDate)
|
||||
- 09-07 (Security audit) ma TRZY publiczne endpointy do oceny: `/availability/{yacht_id}`, `/availability/all`, `/availability/bounds`; dodatkowo privacy revert (tytuły rezerwacji publicznie)
|
||||
- Plan 09-07 wstawiony przed security audit (per żądaniu klienta — kolory w `/rezerwacja/`)
|
||||
- Roadmap przesunięty: 09-07 kolory, 09-08 security, 09-09 docs
|
||||
- 1 checkpoint human-verify na końcu (autonomous: false)
|
||||
- Brak open issues, brak blockers
|
||||
|
||||
---
|
||||
|
||||
25
.paul/governance/governance_2026-05-10.jsonl
Normal file
25
.paul/governance/governance_2026-05-10.jsonl
Normal file
@@ -0,0 +1,25 @@
|
||||
{"ts":"2026-05-10T11:56:04Z","tool":"Bash","cmd":"mkdir -p \".paul/phases/09-finalizacja\"\",\"description\":\"Ensure phase dir exists\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"noOutputExpected\":","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"}
|
||||
{"ts":"2026-05-10T11:57:43Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\.paul\\\\phases\\\\09-finalizacja\\\\09-07-PLAN.md","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"}
|
||||
{"ts":"2026-05-10T11:57:51Z","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-10T11:57:58Z","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-10T11:58:02Z","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-10T11:58:10Z","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-10T12:02:43Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\includes\\\\class-yacht.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"}
|
||||
{"ts":"2026-05-10T12:02:53Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\admin\\\\views\\\\yacht-edit.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"}
|
||||
{"ts":"2026-05-10T12:02:57Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\admin\\\\views\\\\yacht-edit.php","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"}
|
||||
{"ts":"2026-05-10T12:03:12Z","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-10T12:03:18Z","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-10T12:03:25Z","tool":"Bash","cmd":"php -l \"wp-content/plugins/yacht-booking-system/includes/class-yacht.php\" && php -l \"wp-content/plugins/yacht-booking-system/admin/class-admin.php\" && php -l \"wp-content/plugins/yacht-booking-sys","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"}
|
||||
{"ts":"2026-05-10T12:03:52Z","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-10T12:04:15Z","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-10T12:04:22Z","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-10T12:04:31Z","tool":"Bash","cmd":"php -l \"wp-content/plugins/yacht-booking-system/api/class-rest-controller.php\"\",\"description\":\"PHP lint Task 2\"},\"tool_response\":{\"stdout\":\"No syntax errors detected in wp-content/plugins/","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"}
|
||||
{"ts":"2026-05-10T12:04:48Z","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-10T12:04:54Z","tool":"Bash","cmd":"php -l \"wp-content/plugins/yacht-booking-system/frontend/class-calendar-widget-all.php\"\",\"description\":\"PHP lint Task 3\"},\"tool_response\":{\"stdout\":\"No syntax errors detected in wp-content","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"}
|
||||
{"ts":"2026-05-10T12:08:06Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\css\\\\calendar.scss","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"}
|
||||
{"ts":"2026-05-10T12:08:16Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\jachty3.pagedev.pl\\\\wp-content\\\\plugins\\\\yacht-booking-system\\\\frontend\\\\assets\\\\css\\\\calendar.css","cwd":"/c/visual studio code/projekty/jachty3.pagedev.pl"}
|
||||
{"ts":"2026-05-10T12:52: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-10T12:52: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-10T12:54:48Z","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-10T13:01:31Z","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-10T13:01:34Z","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"}
|
||||
225
.paul/phases/09-finalizacja/09-07-PLAN.md
Normal file
225
.paul/phases/09-finalizacja/09-07-PLAN.md
Normal file
@@ -0,0 +1,225 @@
|
||||
---
|
||||
phase: 09-finalizacja
|
||||
plan: 07
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- wp-content/plugins/yacht-booking-system/includes/class-yacht.php
|
||||
- wp-content/plugins/yacht-booking-system/admin/class-admin.php
|
||||
- wp-content/plugins/yacht-booking-system/admin/views/yacht-edit.php
|
||||
- wp-content/plugins/yacht-booking-system/api/class-rest-controller.php
|
||||
- wp-content/plugins/yacht-booking-system/frontend/class-calendar-widget-all.php
|
||||
- wp-content/plugins/yacht-booking-system/yacht-booking-system.php
|
||||
- .paul/ROADMAP.md
|
||||
autonomous: false
|
||||
delegation: off
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Dodać administracyjny wybór koloru per jacht (`_yacht_color`) i zastosować go w widgecie zbiorczym `/rezerwacja/`. Dla rezerwacji bez przypisanego `yacht_id` (eventy z globalnego Google Calendar, source=`ical_global_calendar`) dopasować jacht po występowaniu jego nazwy/aliasu w tytule rezerwacji (case-insensitive, w dowolnym miejscu) i pokolorować event kolorem tego jachtu.
|
||||
|
||||
## Purpose
|
||||
Klient chce rozróżniać jachty wzrokiem w jednym wspólnym kalendarzu — obecnie kolory są przydzielane automatycznie z palety i nie ma kontroli nad wyborem; dodatkowo eventy z globalnego GCal lecą jednym kolorem `GLOBAL_EVENT_COLOR`, mimo że tytuł zawiera nazwę jachtu.
|
||||
|
||||
## Output
|
||||
- Pole color picker w formularzu jachtu (`yacht-edit.php`) zapisywane do meta `_yacht_color`
|
||||
- Backend `Rest_Controller::get_yacht_color_palette()` honoruje meta `_yacht_color` z fallbackiem na obecną paletę (deterministyczna paleta po `yacht_id` zachowana dla braków)
|
||||
- `get_all_availability()` dla eventów `is_global_event === true` próbuje dopasować jacht po nazwie/aliasie w `post_title` rezerwacji i ustawia `color` + dodaje `yacht_id` do eventu (tylko do kolorystyki frontendu, bez zmiany danych w DB)
|
||||
- Legenda widgetu zbiorczego pokazuje wybrane kolory
|
||||
- ROADMAP zaktualizowany: 09-07 = ten plan, 09-08 = security audit, 09-09 = testy/tłumaczenia/dokumentacja
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
<clarifications>
|
||||
- **Numer planu** — Gdzie ten plan wpasować w roadmapę fazy 9?
|
||||
→ Odpowiedź: 09-07 (przed security audit). Security przesuwamy na 09-08, dokumentacja na 09-09.
|
||||
- **Fallback** — Jak ma działać brak ustawionego koloru?
|
||||
→ Odpowiedź: Auto z palety (jak teraz, deterministycznie po `yacht_id`).
|
||||
- **Zakres** — Czy kolor stosować również w widgecie pojedynczego jachtu?
|
||||
→ Odpowiedź: Tylko zbiorczy (`/rezerwacja/`). Pojedynczy widget bez zmian.
|
||||
- **Global iCal** — Czy eventy z yacht_id=0 mają korzystać z koloru jachtu?
|
||||
→ Odpowiedź: Tak — nazwa jachtu może wystąpić w dowolnym miejscu w tytule rezerwacji (case-insensitive, substring). Najdłuższe trafienie wygrywa, brak trafienia → `GLOBAL_EVENT_COLOR`.
|
||||
</clarifications>
|
||||
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@wp-content/plugins/yacht-booking-system/api/class-rest-controller.php
|
||||
@wp-content/plugins/yacht-booking-system/frontend/class-calendar-widget-all.php
|
||||
@wp-content/plugins/yacht-booking-system/admin/views/yacht-edit.php
|
||||
@wp-content/plugins/yacht-booking-system/admin/class-admin.php
|
||||
@wp-content/plugins/yacht-booking-system/includes/class-yacht.php
|
||||
@wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-import.php
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Admin może wybrać kolor jachtu
|
||||
```gherkin
|
||||
Given administrator otwiera formularz edycji jachtu (yacht-edit.php)
|
||||
When wybierze kolor w polu "Kolor jachtu w kalendarzu zbiorczym" i zapisze formularz
|
||||
Then meta `_yacht_color` jachtu zostaje zapisana jako sanitowany hex (`#rrggbb`)
|
||||
And po przeładowaniu formularza widzi zapisany kolor w polu
|
||||
```
|
||||
|
||||
## AC-2: Kolor admina ma priorytet w widgecie zbiorczym
|
||||
```gherkin
|
||||
Given jacht ma ustawione `_yacht_color = #ff6600`
|
||||
When frontend pobiera `GET /availability/all`
|
||||
Then eventy tego jachtu (yacht_id > 0) mają `backgroundColor = #ff6600`
|
||||
And legenda widgetu pokazuje kolor #ff6600 obok jego nazwy
|
||||
```
|
||||
|
||||
## AC-3: Fallback z palety dla jachtów bez ustawionego koloru
|
||||
```gherkin
|
||||
Given jacht nie ma `_yacht_color` (brak meta lub pusta wartość)
|
||||
When frontend pobiera `GET /availability/all`
|
||||
Then eventy tego jachtu otrzymują kolor z `YACHT_COLOR_PALETTE` (deterministycznie po posortowanym yacht_id, jak obecnie)
|
||||
```
|
||||
|
||||
## AC-4: Globalne eventy GCal kolorowane po nazwie jachtu w tytule
|
||||
```gherkin
|
||||
Given event z globalnego kalendarza (yacht_id=0, source=`ical_global_calendar`) o tytule "Rezerwacja - Maja - Kowalski"
|
||||
And istnieje jacht o `post_title = "Maja"` z kolorem `#3498db` (lub fallback z palety)
|
||||
When frontend pobiera `GET /availability/all`
|
||||
Then ten event ma `backgroundColor = #3498db`
|
||||
And jeśli żadna nazwa/alias jachtu nie pojawia się w tytule → `backgroundColor = GLOBAL_EVENT_COLOR`
|
||||
And gdy w tytule pasuje wiele nazw → wybierany jest najdłuższy (np. "Maja Bis" wygrywa z "Maja")
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Pole koloru w formularzu jachtu + meta `_yacht_color`</name>
|
||||
<files>
|
||||
wp-content/plugins/yacht-booking-system/admin/views/yacht-edit.php,
|
||||
wp-content/plugins/yacht-booking-system/admin/class-admin.php,
|
||||
wp-content/plugins/yacht-booking-system/includes/class-yacht.php,
|
||||
wp-content/plugins/yacht-booking-system/yacht-booking-system.php
|
||||
</files>
|
||||
<action>
|
||||
1. `class-yacht.php`: dodaj statyczne metody `get_color( $yacht_id ): string` (zwraca '' gdy brak meta) i `update_color( $yacht_id, $color )` z sanityzacją do `#rrggbb` (regex `/^#[0-9a-f]{6}$/i`, lowercase). Klucz meta: `_yacht_color`.
|
||||
2. `admin/views/yacht-edit.php`: w sekcji ustawień jachtu dodaj pole `<input type="text" class="yacht-color-picker" name="yacht_color" value="<?php echo esc_attr( Yacht::get_color( $yacht_id ) ); ?>">` z labelem "Kolor jachtu (kalendarz zbiorczy)". Pod inputem krótki tekst pomocniczy: "Pozostaw puste, aby użyć automatycznego koloru z palety."
|
||||
3. `class-admin.php` → `save_yacht()`: po istniejących wpisach meta wywołaj `Yacht::update_color( $saved_id, $_POST['yacht_color'] ?? '' )`. Pusty string usuwa meta (delete_post_meta).
|
||||
4. `class-admin.php` → `enqueue_admin_assets()` lub punkt rejestrujący style admin: na stronie `yacht-bookings-add-yacht` enqueue `wp-color-picker` (style + script) oraz inline init: `jQuery('.yacht-color-picker').wpColorPicker();`. Inline JS wpięty przez `wp_add_inline_script( 'wp-color-picker', ... )`.
|
||||
Avoid: zapisywanie surowej wartości z $_POST bez sanityzacji; nadpisywanie istniejącej meta gdy POST nie zawiera `yacht_color` (ale tutaj formularz zawsze wysyła to pole).
|
||||
</action>
|
||||
<verify>
|
||||
Edytuj jacht w panelu, ustaw kolor `#ff6600`, zapisz. Sprawdź `wp_postmeta`: `meta_key = _yacht_color`, `meta_value = #ff6600`. Wyczyść pole, zapisz → meta usunięta. `php -l` na każdym zmienionym pliku.
|
||||
</verify>
|
||||
<done>AC-1 satisfied.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Backend — `_yacht_color` w palecie + matching globalnych eventów po nazwie</name>
|
||||
<files>
|
||||
wp-content/plugins/yacht-booking-system/api/class-rest-controller.php
|
||||
</files>
|
||||
<action>
|
||||
1. `get_yacht_color_palette( $yacht_ids )`: dla każdego `yacht_id` najpierw spróbuj `Yacht::get_color( $yacht_id )`; jeśli niepuste — użyj. Inaczej fallback na obecny algorytm `palette[i % count]` (i = pozycja w posortowanej tablicy ID, jak teraz). Wynik nadal `array<int,string>`.
|
||||
2. `get_all_availability()`: zbuduj mapę dopasowań nazwa→yacht_id raz przed pętlą eventów:
|
||||
- Pobierz wszystkie publish jachty z polami `ID, post_title`.
|
||||
- Dla każdego: dodaj wpis `[strtolower(post_title) => yacht_id]` oraz, jeśli `Yacht::get_gcal_alias($id)` niepusty, `[strtolower($alias) => yacht_id]`.
|
||||
- Posortuj klucze malejąco po długości (najdłuższy match wygrywa).
|
||||
3. W pętli eventów, gdy `$is_global_event === true` (yacht_id=0 lub source=GLOBAL_CALENDAR_SOURCE):
|
||||
- Weź `post_title` rezerwacji (`$booking->post_title`), zlowercase'uj.
|
||||
- Iteruj po posortowanej liście kluczy; pierwszy `mb_strpos($title_lower, $key) !== false` wygrywa → `$matched_yacht_id = mapa[key]`.
|
||||
- Jeśli match → `$color = $color_map[$matched_yacht_id] ?? GLOBAL_EVENT_COLOR`. Brak → `$color = GLOBAL_EVENT_COLOR` (jak teraz).
|
||||
- Nie modyfikuj `yacht_id` w odpowiedzi (zostaje 0); kolor wystarczy do wizualizacji.
|
||||
4. Zachowaj obecne zachowanie dla eventów per-yacht (`yacht_id > 0`) bez zmian.
|
||||
Avoid: case-sensitive porównań; budowania mapy w pętli (perf); modyfikowania danych eventu poza polem `color`.
|
||||
</action>
|
||||
<verify>
|
||||
`php -l class-rest-controller.php`. Ręcznie: `curl '/wp-json/yacht-booking/v1/availability/all?start=2026-05-01&end=2026-06-01' | jq '.[] | {title, color}'` — eventy globalne z nazwą jachtu w tytule mają kolor jachtu, bez nazwy → `#7fb3d5`. Per-yacht jachty z ustawionym `_yacht_color` mają ten kolor.
|
||||
</verify>
|
||||
<done>AC-2, AC-3, AC-4 satisfied.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Legenda widgetu zbiorczego + ROADMAP update</name>
|
||||
<files>
|
||||
wp-content/plugins/yacht-booking-system/frontend/class-calendar-widget-all.php,
|
||||
.paul/ROADMAP.md
|
||||
</files>
|
||||
<action>
|
||||
1. `class-calendar-widget-all.php`: linia ~149-179 — `get_yacht_color_palette()` po Task 2 już honoruje `_yacht_color`, więc legenda dostanie poprawne kolory bez zmian logiki. Zweryfikuj że `$color_map` przekazywana do legendy jest taką samą referencją (jeśli nie — ujednolicić). Jeśli logika legendy oczekuje `palette[i % count]`, zaktualizować by używała tej samej funkcji co backend.
|
||||
2. `.paul/ROADMAP.md`: w sekcji "Plans" Phase 9:
|
||||
- Dodaj `- [ ] 09-07: Kolory per jacht w kalendarzu zbiorczym (color picker + dopasowanie po nazwie)`
|
||||
- Przesuń istniejące: `09-07: Security audit` → `09-08`, `09-08: Testy + tłumaczenia + dokumentacja` → `09-09`.
|
||||
- Zaktualizuj nagłówek "Phases: 8 of 9 complete" oraz tabelę faz (Phase 9 plans count) jeśli to potrzebne.
|
||||
Avoid: dotykania STATE.md tutaj — to robi krok update_state w workflow.
|
||||
</action>
|
||||
<verify>
|
||||
Otwórz `/rezerwacja/`, sprawdź wzrokowo: legenda u góry pokazuje kolory jachtów = wybranym w panelu (lub fallback z palety dla braków). `.paul/ROADMAP.md` zawiera 09-07 jako kolory, 09-08 jako security, 09-09 jako docs.
|
||||
</verify>
|
||||
<done>AC-2 (legenda) satisfied; roadmap zsynchronizowany z planem.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>
|
||||
Pole koloru w formularzu jachtu + zastosowanie kolorów w widgecie zbiorczym (`/rezerwacja/`) z dopasowaniem nazwy jachtu w tytule eventów globalnych.
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
1. Wejdź do panelu admina → Jachty → wybierz dwa jachty, ustaw różne kolory (np. `#ff6600`, `#1abc9c`). Trzeci zostaw bez koloru.
|
||||
2. Odśwież `https://jachty3.pagedev.pl/rezerwacja/`.
|
||||
3. Sprawdź:
|
||||
- Rezerwacje pierwszych dwóch jachtów mają wybrane kolory.
|
||||
- Rezerwacje trzeciego jachtu mają kolor z palety (auto).
|
||||
- Eventy z globalnego GCal, których tytuł zawiera nazwę jednego z dwóch pierwszych jachtów, też dostają jego kolor.
|
||||
- Legenda u góry kalendarza zbiorczego pokazuje te same kolory.
|
||||
4. Wyczyść kolor jednego jachtu w panelu, zapisz, odśwież `/rezerwacja/` → ten jacht wraca do koloru z palety.
|
||||
</how-to-verify>
|
||||
<resume-signal>Wpisz "approved" aby zamknąć plan przez `/paul:unify`, lub opisz zaobserwowane problemy.</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- `wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-import.php` (per-yacht prefix matching pozostaje bez zmian — zmieniamy tylko prezentację w `/availability/all`)
|
||||
- `wp_yacht_availability` (schemat)
|
||||
- Pojedynczy widget jachtu (`class-calendar-widget.php`) — kolory tam bez zmian
|
||||
- Endpoint `/availability/{yacht_id}` — bez zmian
|
||||
- `GLOBAL_EVENT_COLOR` jako fallback (nadal `#7fb3d5`)
|
||||
- `YACHT_COLOR_PALETTE` jako fallback (nadal 8 hex)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Bez migracji DB (meta dodawana on-demand przez update_post_meta)
|
||||
- Bez zmian w warstwie GCal sync (push/pull do Google) — kolory są tylko widoczne w naszym widgecie zbiorczym
|
||||
- Bez tłumaczeń pól (PL hardcoded; tłumaczenia objęte planem 09-09)
|
||||
- Bez zmian w endpointach REST poza `get_all_availability()`
|
||||
- Brak wsparcia dla CSS gradient/secondary color — jeden hex per jacht
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
- [ ] `php -l` przechodzi na wszystkich zmienionych plikach PHP
|
||||
- [ ] Po zapisie formularza jachtu z kolorem `_yacht_color` jest w `wp_postmeta`
|
||||
- [ ] Po wyczyszczeniu pola meta jest usuwana
|
||||
- [ ] `GET /wp-json/yacht-booking/v1/availability/all` zwraca `color` zgodny z meta lub paletą fallback
|
||||
- [ ] Globalne eventy GCal z nazwą jachtu w tytule kolorowane kolorem tego jachtu
|
||||
- [ ] Najdłuższe dopasowanie wygrywa (np. "Maja Bis" > "Maja")
|
||||
- [ ] Brak match → `GLOBAL_EVENT_COLOR`
|
||||
- [ ] Legenda widgetu zbiorczego spójna z kolorami eventów
|
||||
- [ ] ROADMAP.md odzwierciedla nową kolejność (07 kolory, 08 security, 09 docs)
|
||||
- [ ] Wszystkie kryteria akceptacji spełnione przy weryfikacji manualnej
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie zadania ukończone i zweryfikowane
|
||||
- Brak regresji w widgecie pojedynczego jachtu
|
||||
- Brak regresji w existing per-yacht iCal import (matching prefix nadal działa)
|
||||
- Klient akceptuje kolory na `/rezerwacja/`
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Po ukończeniu utwórz `.paul/phases/09-finalizacja/09-07-SUMMARY.md`.
|
||||
</output>
|
||||
@@ -412,6 +412,10 @@ class Admin {
|
||||
$gcal_alias = isset( $data['yacht_gcal_alias'] ) ? sanitize_text_field( wp_unslash( $data['yacht_gcal_alias'] ) ) : '';
|
||||
Yacht::update_gcal_alias( $saved_id, $gcal_alias );
|
||||
|
||||
// Save admin-selected color for aggregated calendar.
|
||||
$yacht_color = isset( $data['yacht_color'] ) ? sanitize_text_field( wp_unslash( $data['yacht_color'] ) ) : '';
|
||||
Yacht::update_color( $saved_id, $yacht_color );
|
||||
|
||||
return $saved_id;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
// Get yacht data
|
||||
$title = $yacht ? $yacht->post_title : '';
|
||||
$content = $yacht ? $yacht->post_content : '';
|
||||
$gcal_alias = $yacht ? \YachtBooking\Yacht::get_gcal_alias( $yacht->ID ) : '';
|
||||
$gcal_alias = $yacht ? \YachtBooking\Yacht::get_gcal_alias( $yacht->ID ) : '';
|
||||
$yacht_color = $yacht ? \YachtBooking\Yacht::get_color( $yacht->ID ) : '';
|
||||
|
||||
$page_title = $yacht ? __( 'Edytuj Jacht', 'yacht-booking' ) : __( 'Dodaj Jacht', 'yacht-booking' );
|
||||
?>
|
||||
@@ -86,6 +87,28 @@ $page_title = $yacht ? __( 'Edytuj Jacht', 'yacht-booking' ) : __( 'Dodaj Jacht'
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Kolor jachtu w kalendarzu zbiorczym -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="yacht_color">
|
||||
<?php esc_html_e( 'Kolor w kalendarzu zbiorczym', 'yacht-booking' ); ?>
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
name="yacht_color"
|
||||
id="yacht_color"
|
||||
class="yacht-color-picker"
|
||||
value="<?php echo esc_attr( $yacht_color ); ?>"
|
||||
data-default-color=""
|
||||
/>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Kolor pasków rezerwacji tego jachtu w widgecie zbiorczym (/rezerwacja/). Pozostaw puste, aby użyć automatycznego koloru z palety.', 'yacht-booking' ); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Alias dla globalnej synchronizacji iCal -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
|
||||
@@ -376,18 +376,40 @@ class Rest_Controller extends \WP_REST_Controller {
|
||||
|
||||
$is_global_mode = ( 'global' === Settings::get_ical_sync_mode() );
|
||||
|
||||
// Build yacht_id → color map (deterministic by ascending yacht_id).
|
||||
$yacht_posts = get_posts(
|
||||
// Build yacht_id → color map (admin-selected `_yacht_color` lub fallback z palety po ID).
|
||||
$yacht_posts_full = 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 );
|
||||
$yacht_ids = array();
|
||||
foreach ( $yacht_posts_full as $yp ) {
|
||||
$yacht_ids[] = (int) $yp->ID;
|
||||
}
|
||||
$color_map = self::get_yacht_color_palette( $yacht_ids );
|
||||
|
||||
// Build name/alias → yacht_id map (lowercase keys, sorted by length DESC for longest match).
|
||||
$name_map = array();
|
||||
foreach ( $yacht_posts_full as $yp ) {
|
||||
$title = mb_strtolower( trim( (string) $yp->post_title ) );
|
||||
if ( '' !== $title ) {
|
||||
$name_map[ $title ] = (int) $yp->ID;
|
||||
}
|
||||
$alias = mb_strtolower( trim( (string) \YachtBooking\Yacht::get_gcal_alias( $yp->ID ) ) );
|
||||
if ( '' !== $alias ) {
|
||||
$name_map[ $alias ] = (int) $yp->ID;
|
||||
}
|
||||
}
|
||||
uksort(
|
||||
$name_map,
|
||||
function( $a, $b ) {
|
||||
return mb_strlen( $b ) - mb_strlen( $a );
|
||||
}
|
||||
);
|
||||
|
||||
// Query bookings overlapping [start, end] with status confirmed or pending.
|
||||
$bookings = get_posts(
|
||||
@@ -437,15 +459,6 @@ class Rest_Controller extends \WP_REST_Controller {
|
||||
$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 );
|
||||
|
||||
// Color: zachowane z poprzedniej logiki (per-yacht paleta lub kolor global).
|
||||
if ( $is_global_mode || $is_global_event ) {
|
||||
$color = self::GLOBAL_EVENT_COLOR;
|
||||
$y_id = 0;
|
||||
} else {
|
||||
$color = isset( $color_map[ $yacht_id ] ) ? $color_map[ $yacht_id ] : self::GLOBAL_EVENT_COLOR;
|
||||
$y_id = $yacht_id;
|
||||
}
|
||||
|
||||
// Title: raw SUMMARY z _booking_notes (iCal) lub customer_name (frontend).
|
||||
// Klient świadomie cofa privacy z 09-04 — tytuły rezerwacji widoczne publicznie.
|
||||
if ( in_array( $source, $ical_sources, true ) ) {
|
||||
@@ -457,6 +470,26 @@ class Rest_Controller extends \WP_REST_Controller {
|
||||
}
|
||||
$title = sanitize_text_field( $title );
|
||||
|
||||
// Color resolution:
|
||||
// - per-yacht event (yacht_id > 0): admin color or palette fallback
|
||||
// - global event (yacht_id = 0): match yacht name/alias anywhere in title (longest wins)
|
||||
if ( ! $is_global_event ) {
|
||||
$color = isset( $color_map[ $yacht_id ] ) ? $color_map[ $yacht_id ] : self::GLOBAL_EVENT_COLOR;
|
||||
$y_id = $yacht_id;
|
||||
} else {
|
||||
$color = self::GLOBAL_EVENT_COLOR;
|
||||
$y_id = 0;
|
||||
$title_lower = mb_strtolower( $title );
|
||||
foreach ( $name_map as $needle => $matched_id ) {
|
||||
if ( '' !== $needle && false !== mb_strpos( $title_lower, $needle ) ) {
|
||||
if ( isset( $color_map[ $matched_id ] ) ) {
|
||||
$color = $color_map[ $matched_id ];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Split na N eventów per dzień (allDay = każdy event mieści się w jednej komórce).
|
||||
// Iteracja od start_date do end_date INCLUSIVE — pierwszy i ostatni dzień
|
||||
// mają half-day visual (yacht odbierany / zwracany w południe).
|
||||
@@ -562,7 +595,8 @@ class Rest_Controller extends \WP_REST_Controller {
|
||||
$map = array();
|
||||
|
||||
foreach ( $ids as $i => $yacht_id ) {
|
||||
$map[ $yacht_id ] = $palette[ $i % $count ];
|
||||
$admin_color = \YachtBooking\Yacht::get_color( $yacht_id );
|
||||
$map[ $yacht_id ] = '' !== $admin_color ? $admin_color : $palette[ $i % $count ];
|
||||
}
|
||||
|
||||
return $map;
|
||||
|
||||
@@ -79,11 +79,20 @@
|
||||
/* Event styling — pełne wypełnienie kafelka kolorem jachtu, bez kropek/czasu */
|
||||
.yacht-calendar-all .fc-event {
|
||||
border: none !important;
|
||||
padding: 2px 4px;
|
||||
padding: 0 1px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
cursor: default;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.yacht-calendar-all .fc-event-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.yacht-calendar-all .fc-daygrid-event-dot,
|
||||
@@ -93,7 +102,7 @@
|
||||
|
||||
/* Custom kontener tytułu (renderowany przez eventContent w JS). */
|
||||
.yacht-calendar-all .yc-event-title {
|
||||
padding: 1px 6px;
|
||||
padding: 0 2px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
@@ -102,13 +111,15 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.3;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Pasek eventu wyższy + gap między dziennymi segmentami (rezerwacja wielonocna
|
||||
= N osobnych pasków zamiast jednej belki — patrz REST split per-day). */
|
||||
.yacht-calendar-all .fc-daygrid-event {
|
||||
min-height: 18px;
|
||||
margin: 1px 2px !important;
|
||||
margin: 1px 0 !important;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
@@ -123,7 +134,7 @@
|
||||
|
||||
.yacht-calendar-all .yc-event-title {
|
||||
font-size: 10px;
|
||||
padding: 1px 4px;
|
||||
padding: 0 1px;
|
||||
}
|
||||
|
||||
.yacht-calendar-all .fc-toolbar.fc-header-toolbar {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -167,19 +167,18 @@ class Calendar_All_View {
|
||||
|
||||
<?php if ( $show_legend ) : ?>
|
||||
<div class="yacht-calendar-legend yacht-calendar-all-legend" aria-label="<?php esc_attr_e( 'Legenda kalendarza', 'yacht-booking' ); ?>">
|
||||
<?php foreach ( $yacht_posts as $yacht ) : ?>
|
||||
<?php $color = isset( $color_map[ $yacht->ID ] ) ? $color_map[ $yacht->ID ] : $global_color; ?>
|
||||
<span class="yacht-legend-item">
|
||||
<span class="yacht-legend-swatch" style="background-color: <?php echo esc_attr( $color ); ?>;"></span>
|
||||
<?php echo esc_html( $yacht->post_title ); ?>
|
||||
</span>
|
||||
<?php endforeach; ?>
|
||||
<?php if ( 'global' === $sync_mode ) : ?>
|
||||
<span class="yacht-legend-item">
|
||||
<span class="yacht-legend-swatch" style="background-color: <?php echo esc_attr( $global_color ); ?>;"></span>
|
||||
<?php esc_html_e( 'Rezerwacja', 'yacht-booking' ); ?>
|
||||
<?php esc_html_e( 'Inne', 'yacht-booking' ); ?>
|
||||
</span>
|
||||
<?php else : ?>
|
||||
<?php foreach ( $yacht_posts as $yacht ) : ?>
|
||||
<?php $color = isset( $color_map[ $yacht->ID ] ) ? $color_map[ $yacht->ID ] : $global_color; ?>
|
||||
<span class="yacht-legend-item">
|
||||
<span class="yacht-legend-swatch" style="background-color: <?php echo esc_attr( $color ); ?>;"></span>
|
||||
<?php echo esc_html( $yacht->post_title ); ?>
|
||||
</span>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -218,6 +218,16 @@ class Yacht_Booking {
|
||||
YACHT_BOOKING_VERSION
|
||||
);
|
||||
|
||||
// WP color picker on yacht edit form.
|
||||
if ( isset( $_GET['page'] ) && 'yacht-bookings-add-yacht' === $_GET['page'] ) {
|
||||
wp_enqueue_style( 'wp-color-picker' );
|
||||
wp_enqueue_script( 'wp-color-picker' );
|
||||
wp_add_inline_script(
|
||||
'wp-color-picker',
|
||||
'jQuery(function($){ $(".yacht-color-picker").wpColorPicker(); });'
|
||||
);
|
||||
}
|
||||
|
||||
wp_enqueue_script(
|
||||
'yacht-booking-admin',
|
||||
YACHT_BOOKING_PLUGIN_URL . 'admin/assets/js/admin.js',
|
||||
|
||||
@@ -152,4 +152,37 @@ class Yacht {
|
||||
public static function update_features( $yacht_id, $features ) {
|
||||
update_post_meta( $yacht_id, '_yacht_features', is_array( $features ) ? $features : array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get admin-selected yacht color for the aggregated calendar.
|
||||
*
|
||||
* Returns sanitized hex (#rrggbb, lowercase) or '' when not set.
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_color( $yacht_id ) {
|
||||
$value = (string) get_post_meta( $yacht_id, '_yacht_color', true );
|
||||
if ( '' === $value ) {
|
||||
return '';
|
||||
}
|
||||
return preg_match( '/^#[0-9a-f]{6}$/i', $value ) ? strtolower( $value ) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Update yacht color. Empty value removes the meta (fallback to palette).
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @param string $color Hex color (#rrggbb) or empty string.
|
||||
*/
|
||||
public static function update_color( $yacht_id, $color ) {
|
||||
$color = trim( (string) $color );
|
||||
if ( '' === $color ) {
|
||||
delete_post_meta( $yacht_id, '_yacht_color' );
|
||||
return;
|
||||
}
|
||||
if ( preg_match( '/^#[0-9a-f]{6}$/i', $color ) ) {
|
||||
update_post_meta( $yacht_id, '_yacht_color', strtolower( $color ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user