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-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-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
|
- [x] 09-06: UX rezerwacja — blokada nawigacji kalendarza (REST `/availability/bounds` + validRange w FC) ✅ 2026-05-08
|
||||||
- [ ] 09-07: Security audit i poprawki
|
- [ ] 09-07: Kolory per jacht w kalendarzu zbiorczym (color picker w settings + dopasowanie po nazwie w tytule globalnych eventów GCal)
|
||||||
- [ ] 09-08: Testy + tłumaczenia + dokumentacja
|
- [ ] 09-08: Security audit i poprawki
|
||||||
|
- [ ] 09-09: Testy + tłumaczenia + dokumentacja
|
||||||
|
|
||||||
---
|
---
|
||||||
*Roadmap created: 2026-05-05*
|
*Roadmap created: 2026-05-05*
|
||||||
|
|||||||
@@ -10,21 +10,21 @@ See: .paul/PROJECT.md (updated 2026-05-05)
|
|||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Milestone: v1.0 Production Release (v1.0.0)
|
Milestone: v1.0 Production Release (v1.0.0)
|
||||||
Phase: 9 of 9 (Finalizacja) — In progress
|
Phase: 9 of 9 (Finalizacja) — Planning
|
||||||
Plan: 09-06 — Complete (blokada nawigacji kalendarza)
|
Plan: 09-07 created, awaiting approval (Kolory per jacht w kalendarzu zbiorczym)
|
||||||
Status: Loop closed, ready for next plan (09-07 Security audit)
|
Status: PLAN created, ready for APPLY
|
||||||
Last activity: 2026-05-08 — Closed loop 09-06 (validRange + REST /availability/bounds)
|
Last activity: 2026-05-10 — Created .paul/phases/09-finalizacja/09-07-PLAN.md
|
||||||
|
|
||||||
Progress:
|
Progress:
|
||||||
- Milestone: [█████████░] 97%
|
- 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
|
## Loop Position
|
||||||
|
|
||||||
Current loop state:
|
Current loop state:
|
||||||
```
|
```
|
||||||
PLAN ──▶ APPLY ──▶ UNIFY
|
PLAN ──▶ APPLY ──▶ UNIFY
|
||||||
✓ ✓ ✓ [Loop 09-06 complete, ready for 09-07]
|
✓ ○ ○ [Plan 09-07 created, awaiting approval]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
@@ -44,9 +44,9 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
|||||||
|
|
||||||
| Issue | Origin | Effort | Revisit |
|
| Issue | Origin | Effort | Revisit |
|
||||||
|-------|--------|--------|---------|
|
|-------|--------|--------|---------|
|
||||||
| Tłumaczenia PL | Phase 1-8 | M | 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-07) |
|
| 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-08) |
|
| Dokumentacja PHP Doc | Phase 1-8 | L | Phase 9 (plan 09-09) |
|
||||||
|
|
||||||
### Blockers/Concerns
|
### Blockers/Concerns
|
||||||
|
|
||||||
@@ -54,14 +54,14 @@ None.
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-05-08
|
Last session: 2026-05-10
|
||||||
Stopped at: Loop 09-06 zamknięty — blokada nawigacji kalendarza zatwierdzona
|
Stopped at: Plan 09-07 utworzony — kolory per jacht w kalendarzu zbiorczym
|
||||||
Next action: Run /paul:plan to plan 09-07 (Security audit)
|
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-06-SUMMARY.md
|
Resume file: .paul/phases/09-finalizacja/09-07-PLAN.md
|
||||||
Resume context:
|
Resume context:
|
||||||
- Faza 9: 6/8 planów ukończonych (75%), milestone v1.0 97%
|
- Plan 09-07 wstawiony przed security audit (per żądaniu klienta — kolory w `/rezerwacja/`)
|
||||||
- Plugin v1.2.1: validRange w widgecie zbiorczym (prev disabled na bieżącym miesiącu, next disabled po maxDate)
|
- Roadmap przesunięty: 09-07 kolory, 09-08 security, 09-09 docs
|
||||||
- 09-07 (Security audit) ma TRZY publiczne endpointy do oceny: `/availability/{yacht_id}`, `/availability/all`, `/availability/bounds`; dodatkowo privacy revert (tytuły rezerwacji publicznie)
|
- 1 checkpoint human-verify na końcu (autonomous: false)
|
||||||
- Brak open issues, brak blockers
|
- 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'] ) ) : '';
|
$gcal_alias = isset( $data['yacht_gcal_alias'] ) ? sanitize_text_field( wp_unslash( $data['yacht_gcal_alias'] ) ) : '';
|
||||||
Yacht::update_gcal_alias( $saved_id, $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;
|
return $saved_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
$title = $yacht ? $yacht->post_title : '';
|
$title = $yacht ? $yacht->post_title : '';
|
||||||
$content = $yacht ? $yacht->post_content : '';
|
$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' );
|
$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>
|
</td>
|
||||||
</tr>
|
</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 -->
|
<!-- Alias dla globalnej synchronizacji iCal -->
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
|
|||||||
@@ -376,18 +376,40 @@ class Rest_Controller extends \WP_REST_Controller {
|
|||||||
|
|
||||||
$is_global_mode = ( 'global' === Settings::get_ical_sync_mode() );
|
$is_global_mode = ( 'global' === Settings::get_ical_sync_mode() );
|
||||||
|
|
||||||
// Build yacht_id → color map (deterministic by ascending yacht_id).
|
// Build yacht_id → color map (admin-selected `_yacht_color` lub fallback z palety po ID).
|
||||||
$yacht_posts = get_posts(
|
$yacht_posts_full = get_posts(
|
||||||
array(
|
array(
|
||||||
'post_type' => 'yacht',
|
'post_type' => 'yacht',
|
||||||
'post_status' => 'publish',
|
'post_status' => 'publish',
|
||||||
'posts_per_page' => -1,
|
'posts_per_page' => -1,
|
||||||
'orderby' => 'ID',
|
'orderby' => 'ID',
|
||||||
'order' => 'ASC',
|
'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.
|
// Query bookings overlapping [start, end] with status confirmed or pending.
|
||||||
$bookings = get_posts(
|
$bookings = get_posts(
|
||||||
@@ -437,15 +459,6 @@ class Rest_Controller extends \WP_REST_Controller {
|
|||||||
$source = (string) get_post_meta( $booking_id, '_booking_source', true );
|
$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 );
|
$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).
|
// Title: raw SUMMARY z _booking_notes (iCal) lub customer_name (frontend).
|
||||||
// Klient świadomie cofa privacy z 09-04 — tytuły rezerwacji widoczne publicznie.
|
// Klient świadomie cofa privacy z 09-04 — tytuły rezerwacji widoczne publicznie.
|
||||||
if ( in_array( $source, $ical_sources, true ) ) {
|
if ( in_array( $source, $ical_sources, true ) ) {
|
||||||
@@ -457,6 +470,26 @@ class Rest_Controller extends \WP_REST_Controller {
|
|||||||
}
|
}
|
||||||
$title = sanitize_text_field( $title );
|
$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).
|
// 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ń
|
// Iteracja od start_date do end_date INCLUSIVE — pierwszy i ostatni dzień
|
||||||
// mają half-day visual (yacht odbierany / zwracany w południe).
|
// mają half-day visual (yacht odbierany / zwracany w południe).
|
||||||
@@ -562,7 +595,8 @@ class Rest_Controller extends \WP_REST_Controller {
|
|||||||
$map = array();
|
$map = array();
|
||||||
|
|
||||||
foreach ( $ids as $i => $yacht_id ) {
|
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;
|
return $map;
|
||||||
|
|||||||
@@ -79,11 +79,20 @@
|
|||||||
/* Event styling — pełne wypełnienie kafelka kolorem jachtu, bez kropek/czasu */
|
/* Event styling — pełne wypełnienie kafelka kolorem jachtu, bez kropek/czasu */
|
||||||
.yacht-calendar-all .fc-event {
|
.yacht-calendar-all .fc-event {
|
||||||
border: none !important;
|
border: none !important;
|
||||||
padding: 2px 4px;
|
padding: 0 1px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
cursor: default;
|
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,
|
.yacht-calendar-all .fc-daygrid-event-dot,
|
||||||
@@ -93,7 +102,7 @@
|
|||||||
|
|
||||||
/* Custom kontener tytułu (renderowany przez eventContent w JS). */
|
/* Custom kontener tytułu (renderowany przez eventContent w JS). */
|
||||||
.yacht-calendar-all .yc-event-title {
|
.yacht-calendar-all .yc-event-title {
|
||||||
padding: 1px 6px;
|
padding: 0 2px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@@ -102,13 +111,15 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pasek eventu wyższy + gap między dziennymi segmentami (rezerwacja wielonocna
|
/* Pasek eventu wyższy + gap między dziennymi segmentami (rezerwacja wielonocna
|
||||||
= N osobnych pasków zamiast jednej belki — patrz REST split per-day). */
|
= N osobnych pasków zamiast jednej belki — patrz REST split per-day). */
|
||||||
.yacht-calendar-all .fc-daygrid-event {
|
.yacht-calendar-all .fc-daygrid-event {
|
||||||
min-height: 18px;
|
min-height: 18px;
|
||||||
margin: 1px 2px !important;
|
margin: 1px 0 !important;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +134,7 @@
|
|||||||
|
|
||||||
.yacht-calendar-all .yc-event-title {
|
.yacht-calendar-all .yc-event-title {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
padding: 1px 4px;
|
padding: 0 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-all .fc-toolbar.fc-header-toolbar {
|
.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
@@ -3,6 +3,7 @@
|
|||||||
margin: 0 auto 40px;
|
margin: 0 auto 40px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-switcher {
|
.yacht-calendar-switcher {
|
||||||
// display: flex;
|
// display: flex;
|
||||||
// flex-wrap: wrap;
|
// flex-wrap: wrap;
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
background: #0e2036;
|
background: #0e2036;
|
||||||
gap: 30px;
|
gap: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-switcher-label {
|
.yacht-calendar-switcher-label {
|
||||||
// font-size: 14px;
|
// font-size: 14px;
|
||||||
// font-weight: 700;
|
// font-weight: 700;
|
||||||
@@ -39,15 +41,18 @@
|
|||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
font-size: 64px;
|
font-size: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
font-size: 48px;
|
font-size: 48px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-switcher-buttons {
|
.yacht-calendar-switcher-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-switcher-button {
|
.yacht-calendar-switcher-button {
|
||||||
font-family: 'JetBrains Mono', Sans-serif !important;
|
font-family: 'JetBrains Mono', Sans-serif !important;
|
||||||
font-size: 0.75rem !important;
|
font-size: 0.75rem !important;
|
||||||
@@ -65,6 +70,7 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
background: #a9000c !important;
|
background: #a9000c !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// display: inline-flex;
|
// display: inline-flex;
|
||||||
// align-items: center;
|
// align-items: center;
|
||||||
// padding: 9px 14px;
|
// padding: 9px 14px;
|
||||||
@@ -91,26 +97,31 @@
|
|||||||
// transform: translateY(-1px);
|
// transform: translateY(-1px);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-switcher-button.is-active {
|
.yacht-calendar-switcher-button.is-active {
|
||||||
background: #bc1834;
|
background: #bc1834;
|
||||||
border-color: #bc1834;
|
border-color: #bc1834;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
box-shadow: 0 8px 18px rgba(188, 24, 52, 0.18);
|
box-shadow: 0 8px 18px rgba(188, 24, 52, 0.18);
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-header {
|
.yacht-calendar-header {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-title {
|
.yacht-calendar-title {
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #021526;
|
color: #021526;
|
||||||
margin: 0 0 15px 0;
|
margin: 0 0 15px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-description {
|
.yacht-calendar-description {
|
||||||
color: #666;
|
color: #666;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-instructions {
|
.yacht-calendar-instructions {
|
||||||
margin: 0 0 30px 0;
|
margin: 0 0 30px 0;
|
||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
@@ -128,6 +139,7 @@
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-legend {
|
.yacht-calendar-legend {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -135,6 +147,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0 0 18px 0;
|
margin: 0 0 18px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-legend-item {
|
.yacht-legend-item {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -145,24 +158,29 @@
|
|||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-legend-swatch {
|
.yacht-legend-swatch {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-legend-swatch-past {
|
.yacht-legend-swatch-past {
|
||||||
background: #d0d5dd;
|
background: #d0d5dd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar {
|
.yacht-calendar {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
background: rgba(255, 255, 255, 0.1) f;
|
background: rgba(255, 255, 255, 0.1) f;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
|
||||||
.fc {
|
.fc {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-button-primary {
|
.fc-button-primary {
|
||||||
background: #bc1834;
|
background: #bc1834;
|
||||||
border-color: #bc1834;
|
border-color: #bc1834;
|
||||||
@@ -170,21 +188,25 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #021526;
|
background: #021526;
|
||||||
border-color: #021526;
|
border-color: #021526;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:disabled) {
|
&:not(:disabled) {
|
||||||
&:active {
|
&:active {
|
||||||
background: #021526;
|
background: #021526;
|
||||||
border-color: #021526;
|
border-color: #021526;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:disabled).fc-button-active {
|
&:not(:disabled).fc-button-active {
|
||||||
background: #021526;
|
background: #021526;
|
||||||
border-color: #021526;
|
border-color: #021526;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-daygrid-day.fc-day-past {
|
.fc-daygrid-day.fc-day-past {
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -193,25 +215,30 @@
|
|||||||
color: #6c757d;
|
color: #6c757d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-daygrid-day-number,
|
.fc-daygrid-day-number,
|
||||||
a.fc-daygrid-day-number {
|
a.fc-daygrid-day-number {
|
||||||
color: #021526 !important;
|
color: #021526 !important;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-daygrid-day {
|
.fc-daygrid-day {
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #f8f9fa;
|
background: #f8f9fa;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-highlight {
|
.fc-highlight {
|
||||||
background: rgba(188, 24, 52, 0.1) !important;
|
background: rgba(188, 24, 52, 0.1) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-daygrid-day-top {
|
.fc-daygrid-day-top {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-daygrid-day-number {
|
.fc-daygrid-day-number {
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
min-width: 28px;
|
min-width: 28px;
|
||||||
@@ -220,65 +247,73 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-daygrid-day-frame {
|
.fc-daygrid-day-frame {
|
||||||
min-height: 80px;
|
min-height: 80px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-bg-event {
|
.fc-bg-event {
|
||||||
opacity: 0.78 !important;
|
opacity: 0.78 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-bg-event.yacht-day-available {
|
.fc-bg-event.yacht-day-available {
|
||||||
opacity: 0.66 !important;
|
opacity: 0.66 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-bg-event.yacht-day-booked {
|
.fc-bg-event.yacht-day-booked {
|
||||||
opacity: 0.92 !important;
|
opacity: 0.92 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-bg-event.yacht-day-blocked {
|
.fc-bg-event.yacht-day-blocked {
|
||||||
opacity: 0.92 !important;
|
opacity: 0.92 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Half-day rendering for first / last day of a booking or blockade.
|
// Half-day rendering for first / last day of a booking or blockade.
|
||||||
// Skos 45°. Pierwszy dzień: trójkąt bottom-left = wolny, top-right = zajęty (odbiór po południu).
|
// Skos 45°. Pierwszy dzień: trójkąt bottom-left = wolny, top-right = zajęty (odbiór po południu).
|
||||||
// Ostatni dzień: trójkąt top-left = zajęty, bottom-right = wolny (zwrot rano).
|
// Ostatni dzień: trójkąt top-left = zajęty, bottom-right = wolny (zwrot rano).
|
||||||
.fc-bg-event.yacht-day-booked-start,
|
.fc-bg-event.yacht-day-booked-start,
|
||||||
.fc-bg-event.yacht-day-blocked-start {
|
.fc-bg-event.yacht-day-blocked-start {
|
||||||
background: linear-gradient(
|
background: linear-gradient(135deg,
|
||||||
135deg,
|
|
||||||
var(--yacht-available-bg, #f5f9ff) 0%,
|
var(--yacht-available-bg, #f5f9ff) 0%,
|
||||||
var(--yacht-available-bg, #f5f9ff) 50%,
|
var(--yacht-available-bg, #f5f9ff) 50%,
|
||||||
var(--yacht-booked-bg, #bc1834) 50%,
|
var(--yacht-booked-bg, #bc1834) 50%,
|
||||||
var(--yacht-booked-bg, #bc1834) 100%
|
var(--yacht-booked-bg, #bc1834) 100%) !important;
|
||||||
) !important;
|
|
||||||
opacity: 0.92 !important;
|
opacity: 0.92 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-bg-event.yacht-day-booked-end,
|
.fc-bg-event.yacht-day-booked-end,
|
||||||
.fc-bg-event.yacht-day-blocked-end {
|
.fc-bg-event.yacht-day-blocked-end {
|
||||||
background: linear-gradient(
|
background: linear-gradient(135deg,
|
||||||
135deg,
|
|
||||||
var(--yacht-booked-bg, #bc1834) 0%,
|
var(--yacht-booked-bg, #bc1834) 0%,
|
||||||
var(--yacht-booked-bg, #bc1834) 50%,
|
var(--yacht-booked-bg, #bc1834) 50%,
|
||||||
var(--yacht-available-bg, #f5f9ff) 50%,
|
var(--yacht-available-bg, #f5f9ff) 50%,
|
||||||
var(--yacht-available-bg, #f5f9ff) 100%
|
var(--yacht-available-bg, #f5f9ff) 100%) !important;
|
||||||
) !important;
|
|
||||||
opacity: 0.92 !important;
|
opacity: 0.92 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-day-available {
|
.yacht-day-available {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-day-booked {
|
.yacht-day-booked {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-day-blocked {
|
.yacht-day-blocked {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-booking-form-container {
|
.yacht-booking-form-container {
|
||||||
background: #f9f9f9;
|
background: #f9f9f9;
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
border: 1px solid #e0e0e0;
|
border: 1px solid #e0e0e0;
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
margin: 0 0 25px 0;
|
margin: 0 0 25px 0;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
@@ -286,6 +321,7 @@
|
|||||||
color: #021526;
|
color: #021526;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-booking-form {
|
.yacht-booking-form {
|
||||||
.form-row {
|
.form-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -293,20 +329,24 @@
|
|||||||
gap: 20px;
|
gap: 20px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-field {
|
.form-field {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
color: #333;
|
color: #333;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
||||||
.required {
|
.required {
|
||||||
color: #bc1834;
|
color: #bc1834;
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='text'] {
|
input[type='text'] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px 15px;
|
padding: 12px 15px;
|
||||||
@@ -317,6 +357,7 @@
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='email'] {
|
input[type='email'] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px 15px;
|
padding: 12px 15px;
|
||||||
@@ -327,6 +368,7 @@
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='tel'] {
|
input[type='tel'] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px 15px;
|
padding: 12px 15px;
|
||||||
@@ -337,6 +379,7 @@
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='date'] {
|
input[type='date'] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px 15px;
|
padding: 12px 15px;
|
||||||
@@ -347,31 +390,37 @@
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: #bc1834;
|
border-color: #bc1834;
|
||||||
box-shadow: 0 0 0 3px rgba(188, 24, 52, 0.1);
|
box-shadow: 0 0 0 3px rgba(188, 24, 52, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:read-only {
|
&:read-only {
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-actions {
|
.form-actions {
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.booking-terms {
|
.booking-terms {
|
||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: #333;
|
color: #333;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #bc1834;
|
color: #bc1834;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-booking-submit {
|
.yacht-booking-submit {
|
||||||
font-family: 'JetBrains Mono', Sans-serif !important;
|
font-family: 'JetBrains Mono', Sans-serif !important;
|
||||||
font-size: 0.75rem !important;
|
font-size: 0.75rem !important;
|
||||||
@@ -391,6 +440,7 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
background: #a9000c !important;
|
background: #a9000c !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
background: #ccc;
|
background: #ccc;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
@@ -398,108 +448,131 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-booking-response {
|
.yacht-booking-response {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.booking-success {
|
.booking-success {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|
||||||
strong {
|
strong {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.booking-error {
|
.booking-error {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|
||||||
strong {
|
strong {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-error {
|
.yacht-calendar-error {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-notice {
|
.yacht-calendar-notice {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-view-only {
|
.yacht-calendar-view-only {
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
padding: 15px 15px 0 15px;
|
padding: 15px 15px 0 15px;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
||||||
.yacht-calendar-header {
|
.yacht-calendar-header {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-title {
|
.yacht-calendar-title {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-instructions {
|
.yacht-calendar-instructions {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-legend {
|
.yacht-calendar-legend {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar {
|
.yacht-calendar {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-daygrid-day-frame {
|
.fc-daygrid-day-frame {
|
||||||
min-height: 36px;
|
min-height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-daygrid-day-top {
|
.fc-daygrid-day-top {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-daygrid-day-number {
|
.fc-daygrid-day-number {
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
min-width: 22px;
|
min-width: 22px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-col-header-cell {
|
.fc-col-header-cell {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-button {
|
.fc-button {
|
||||||
font-size: 11px !important;
|
font-size: 11px !important;
|
||||||
padding: 5px 10px !important;
|
padding: 5px 10px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-toolbar-title {
|
.fc-toolbar-title {
|
||||||
font-size: 16px !important;
|
font-size: 16px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-inquiry-layout {
|
.yacht-inquiry-layout {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 2fr 1fr;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
align-items: start;
|
align-items: start;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-inquiry-form-container {
|
.yacht-inquiry-form-container {
|
||||||
background: #021526;
|
background: #021526;
|
||||||
padding: 24px;
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
margin: 0 0 8px 0;
|
margin: 0 0 8px 0;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
@@ -507,27 +580,32 @@
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-inquiry-desc {
|
.yacht-inquiry-desc {
|
||||||
margin: 0 0 18px 0;
|
margin: 0 0 18px 0;
|
||||||
color: rgba(255, 255, 255, 0.7);
|
color: rgba(255, 255, 255, 0.7);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-inquiry-form {
|
.yacht-inquiry-form {
|
||||||
.form-field {
|
.form-field {
|
||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
color: rgba(255, 255, 255, 0.9);
|
color: rgba(255, 255, 255, 0.9);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
||||||
.required {
|
.required {
|
||||||
color: #ff6b6b;
|
color: #ff6b6b;
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='text'] {
|
input[type='text'] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
@@ -540,6 +618,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='email'] {
|
input[type='email'] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
@@ -552,6 +631,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='tel'] {
|
input[type='tel'] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
@@ -564,6 +644,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
@@ -577,9 +658,11 @@
|
|||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: rgba(255, 255, 255, 0.4);
|
color: rgba(255, 255, 255, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: #bc1834;
|
border-color: #bc1834;
|
||||||
@@ -587,10 +670,12 @@
|
|||||||
background: rgba(255, 255, 255, 0.15);
|
background: rgba(255, 255, 255, 0.15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: rgba(255, 255, 255, 0.4);
|
color: rgba(255, 255, 255, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: #bc1834;
|
border-color: #bc1834;
|
||||||
@@ -598,69 +683,84 @@
|
|||||||
background: rgba(255, 255, 255, 0.15);
|
background: rgba(255, 255, 255, 0.15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-actions {
|
.form-actions {
|
||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.booking-terms {
|
.booking-terms {
|
||||||
color: rgba(255, 255, 255, 0.6);
|
color: rgba(255, 255, 255, 0.6);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: rgba(255, 255, 255, 0.85);
|
color: rgba(255, 255, 255, 0.85);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-booking-submit {
|
.yacht-booking-submit {
|
||||||
background: #bc1834;
|
background: #bc1834;
|
||||||
border-color: #bc1834;
|
border-color: #bc1834;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
color: #021526;
|
color: #021526;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-inquiry-response {
|
.yacht-inquiry-response {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 992px) {
|
@media (max-width: 992px) {
|
||||||
.yacht-calendar-title {
|
.yacht-calendar-title {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar {
|
.yacht-calendar {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-inquiry-layout {
|
.yacht-inquiry-layout {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.yacht-inquiry-form-container {
|
|
||||||
padding: 18px;
|
|
||||||
}
|
|
||||||
.yacht-calendar-title {
|
.yacht-calendar-title {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar {
|
.yacht-calendar {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
.fc-toolbar {
|
.fc-toolbar {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-toolbar-chunk {
|
.fc-toolbar-chunk {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-button {
|
.fc-button {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-legend {
|
.yacht-calendar-legend {
|
||||||
gap: 8px 12px;
|
gap: 8px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-booking-form-container {
|
.yacht-booking-form-container {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-booking-form {
|
.yacht-booking-form {
|
||||||
.form-row {
|
.form-row {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
@@ -668,31 +768,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.yacht-calendar-switcher-button {
|
.yacht-calendar-switcher-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-calendar-title {
|
.yacht-calendar-title {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-booking-form-container {
|
.yacht-booking-form-container {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-booking-form {
|
.yacht-booking-form {
|
||||||
input[type='text'] {
|
input[type='text'] {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='email'] {
|
input[type='email'] {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='tel'] {
|
input[type='tel'] {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yacht-booking-submit {
|
.yacht-booking-submit {
|
||||||
padding: 12px 20px;
|
padding: 12px 20px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -715,6 +822,7 @@ body .fc .fc-day-other .fc-daygrid-day-top {
|
|||||||
.fc {
|
.fc {
|
||||||
--fc-border-color: #0e2036;
|
--fc-border-color: #0e2036;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-theme-standard td,
|
.fc-theme-standard td,
|
||||||
.fc-theme-standard th,
|
.fc-theme-standard th,
|
||||||
.fc-theme-standard .fc-scrollgrid,
|
.fc-theme-standard .fc-scrollgrid,
|
||||||
@@ -724,10 +832,12 @@ body .fc .fc-day-other .fc-daygrid-day-top {
|
|||||||
.fc-col-header-cell {
|
.fc-col-header-cell {
|
||||||
border-color: #0e2036 !important;
|
border-color: #0e2036 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-col-header-cell {
|
.fc-col-header-cell {
|
||||||
background: #0e2036;
|
background: #0e2036;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-col-header-cell .fc-col-header-cell-cushion {
|
.fc-col-header-cell .fc-col-header-cell-cushion {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
@@ -167,12 +167,6 @@ class Calendar_All_View {
|
|||||||
|
|
||||||
<?php if ( $show_legend ) : ?>
|
<?php if ( $show_legend ) : ?>
|
||||||
<div class="yacht-calendar-legend yacht-calendar-all-legend" aria-label="<?php esc_attr_e( 'Legenda kalendarza', 'yacht-booking' ); ?>">
|
<div class="yacht-calendar-legend yacht-calendar-all-legend" aria-label="<?php esc_attr_e( 'Legenda kalendarza', 'yacht-booking' ); ?>">
|
||||||
<?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' ); ?>
|
|
||||||
</span>
|
|
||||||
<?php else : ?>
|
|
||||||
<?php foreach ( $yacht_posts as $yacht ) : ?>
|
<?php foreach ( $yacht_posts as $yacht ) : ?>
|
||||||
<?php $color = isset( $color_map[ $yacht->ID ] ) ? $color_map[ $yacht->ID ] : $global_color; ?>
|
<?php $color = isset( $color_map[ $yacht->ID ] ) ? $color_map[ $yacht->ID ] : $global_color; ?>
|
||||||
<span class="yacht-legend-item">
|
<span class="yacht-legend-item">
|
||||||
@@ -180,6 +174,11 @@ class Calendar_All_View {
|
|||||||
<?php echo esc_html( $yacht->post_title ); ?>
|
<?php echo esc_html( $yacht->post_title ); ?>
|
||||||
</span>
|
</span>
|
||||||
<?php endforeach; ?>
|
<?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( 'Inne', 'yacht-booking' ); ?>
|
||||||
|
</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|||||||
@@ -218,6 +218,16 @@ class Yacht_Booking {
|
|||||||
YACHT_BOOKING_VERSION
|
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(
|
wp_enqueue_script(
|
||||||
'yacht-booking-admin',
|
'yacht-booking-admin',
|
||||||
YACHT_BOOKING_PLUGIN_URL . 'admin/assets/js/admin.js',
|
YACHT_BOOKING_PLUGIN_URL . 'admin/assets/js/admin.js',
|
||||||
|
|||||||
@@ -152,4 +152,37 @@ class Yacht {
|
|||||||
public static function update_features( $yacht_id, $features ) {
|
public static function update_features( $yacht_id, $features ) {
|
||||||
update_post_meta( $yacht_id, '_yacht_features', is_array( $features ) ? $features : array() );
|
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