update
This commit is contained in:
218
.paul/phases/01-reservation-form-plugin/01-01-PLAN.md
Normal file
218
.paul/phases/01-reservation-form-plugin/01-01-PLAN.md
Normal file
@@ -0,0 +1,218 @@
|
||||
---
|
||||
phase: 01-reservation-form-plugin
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- wp-content/plugins/carei-reservation/carei-reservation.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-softra-api.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Utworzyć plugin WordPress `carei-reservation` z:
|
||||
1. Klasą proxy do Softra Rent API (autoryzacja JWT + cache tokenu + endpointy floty)
|
||||
2. WP REST API endpoints jako proxy (frontend → WP → Softra)
|
||||
3. Zarejestrowanym widgetem Elementor (shell z przyciskiem triggerującym modal)
|
||||
|
||||
## Purpose
|
||||
Backend i szkielet pluginu to fundament — bez tego nie ma formularza. API proxy chroni credentials (nigdy nie wystawiane na frontend) i zapewnia cache tokenu JWT.
|
||||
|
||||
## Output
|
||||
Nowy plugin `wp-content/plugins/carei-reservation/` z 4 plikami PHP, gotowy do aktywacji.
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
|
||||
## Source Files
|
||||
@.env (credentials Softra API)
|
||||
@docs/rent-api-01-autoryzacja-i-flota.md (endpointy: auth, branches, car classes, models, prices)
|
||||
@docs/rent-api-02-klienci-i-konta.md (customer/add, agreement/def/list)
|
||||
@docs/rent-api-03-rezerwacje-i-platnosci.md (makebooking, confirm, pricingSummary)
|
||||
@docs/figma-formularz/README.md (specyfikacja UI)
|
||||
@wp-content/plugins/elementor-addon/elementor-addon.php (wzorzec rejestracji widgetów)
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Plugin aktywuje się bez błędów
|
||||
```gherkin
|
||||
Given plugin carei-reservation jest w wp-content/plugins/
|
||||
When administrator aktywuje plugin w panelu WP
|
||||
Then plugin ładuje się bez PHP errors/warnings
|
||||
And widget "Carei Reservation" pojawia się w panelu Elementor
|
||||
```
|
||||
|
||||
## AC-2: API Proxy — autoryzacja i cache tokenu
|
||||
```gherkin
|
||||
Given credentials Softra API są w .env
|
||||
When proxy wykonuje pierwsze żądanie do Softra
|
||||
Then pobiera token JWT przez POST /account/auth
|
||||
And cachuje token w WP transient na 50 minut (token ważny 60min)
|
||||
And kolejne żądania używają cached tokenu
|
||||
```
|
||||
|
||||
## AC-3: REST API proxy zwraca dane oddziałów
|
||||
```gherkin
|
||||
Given token JWT jest aktywny
|
||||
When frontend wywołuje GET /wp-json/carei/v1/branches
|
||||
Then proxy zwraca listę oddziałów z Softra /branch/list
|
||||
And odpowiedź zawiera name, description, city, street
|
||||
```
|
||||
|
||||
## AC-4: REST API proxy zwraca klasy pojazdów i cennik
|
||||
```gherkin
|
||||
Given token JWT jest aktywny
|
||||
When frontend wywołuje POST /wp-json/carei/v1/car-classes z dateFrom, dateTo, branchName
|
||||
Then proxy zwraca listę klas z Softra /car/class/list
|
||||
When frontend wywołuje POST /wp-json/carei/v1/pricelist z category, dateFrom, dateTo, pickUpLocation
|
||||
Then proxy zwraca cennik z additionalItems z Softra /pricelist/list
|
||||
```
|
||||
|
||||
## AC-5: Widget Elementor renderuje przycisk-trigger
|
||||
```gherkin
|
||||
Given widget "Carei Reservation" jest dodany na stronę w Elementorze
|
||||
When strona jest wyświetlana na froncie
|
||||
Then widoczny jest przycisk "Złóż zapytanie o rezerwację" (stylowany wg Figmy)
|
||||
And kliknięcie przycisku otwiera pusty modal (placeholder na krok 2 — Form UI)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Główny plik pluginu + klasa Softra API</name>
|
||||
<files>wp-content/plugins/carei-reservation/carei-reservation.php, wp-content/plugins/carei-reservation/includes/class-softra-api.php</files>
|
||||
<action>
|
||||
**carei-reservation.php:**
|
||||
- Plugin header (Plugin Name: Carei Reservation, Version: 1.0.0)
|
||||
- Załaduj .env z ABSPATH (parsuj ręcznie, format: `key: value` — NIE `KEY=value`)
|
||||
- Require includes/*.php
|
||||
- Hook `plugins_loaded` → inicjalizacja
|
||||
- Hook `elementor/widgets/register` → rejestracja widgetu
|
||||
|
||||
**class-softra-api.php:**
|
||||
- Klasa `Carei_Softra_API` (singleton)
|
||||
- Constructor: przyjmuje url, username, password z .env
|
||||
- `get_token()`: sprawdza WP transient `carei_softra_token`, jeśli brak → POST /account/auth, cache na 50 min
|
||||
- `request($method, $endpoint, $body = null)`: generyczna metoda z wp_remote_request, Authorization Bearer header, JSON body
|
||||
- Metody publiczne:
|
||||
- `get_branches()` → GET /branch/list
|
||||
- `get_car_classes($dateFrom, $dateTo, $branchName)` → POST /car/class/list
|
||||
- `get_car_models($dateFrom, $dateTo, $branchName, $category)` → POST /car/model/list?includeBrandDetails=true
|
||||
- `get_pricelist($category, $dateFrom, $dateTo, $pickUpLocation, $lang='pl', $currency='PLN')` → POST /pricelist/list
|
||||
- `get_pricing_summary($params)` → POST /rent/princingSummary
|
||||
- `add_customer($data)` → POST /customer/add
|
||||
- `make_booking($data)` → POST /rent/makebooking
|
||||
- `confirm_booking($reservationId)` → POST /rent/confirm
|
||||
- `get_agreements()` → GET /agreement/def/list
|
||||
- Error handling: zwracaj WP_Error przy błędach HTTP lub pustym tokenie
|
||||
</action>
|
||||
<verify>
|
||||
Sprawdź: `php -l wp-content/plugins/carei-reservation/carei-reservation.php` i `php -l wp-content/plugins/carei-reservation/includes/class-softra-api.php` — brak syntax errors.
|
||||
</verify>
|
||||
<done>AC-1 częściowo (plugin ładuje się), AC-2 (autoryzacja + cache)</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: WP REST API proxy endpoints</name>
|
||||
<files>wp-content/plugins/carei-reservation/includes/class-rest-proxy.php</files>
|
||||
<action>
|
||||
**class-rest-proxy.php:**
|
||||
- Klasa `Carei_REST_Proxy`
|
||||
- Hook `rest_api_init` → rejestracja routes w namespace `carei/v1`
|
||||
- Endpoints:
|
||||
1. `GET /branches` → `Carei_Softra_API::get_branches()`
|
||||
2. `POST /car-classes` (params: dateFrom, dateTo, branchName) → `get_car_classes()`
|
||||
3. `POST /car-models` (params: dateFrom, dateTo, branchName, category) → `get_car_models()`
|
||||
4. `POST /pricelist` (params: category, dateFrom, dateTo, pickUpLocation) → `get_pricelist()`
|
||||
5. `POST /pricing-summary` (params: full booking params) → `get_pricing_summary()`
|
||||
6. `POST /customer` (params: customer data) → `add_customer()`
|
||||
7. `POST /booking` (params: booking data) → `make_booking()`
|
||||
8. `POST /booking/confirm` (params: reservationId) → `confirm_booking()`
|
||||
9. `GET /agreements` → `get_agreements()`
|
||||
- Permission callback: `__return_true` dla GET, nonce check dla POST (wp_rest nonce)
|
||||
- Sanitization: `sanitize_text_field` na wszystkich string params
|
||||
- Odpowiedź: `new WP_REST_Response($data, 200)` lub `WP_Error`
|
||||
</action>
|
||||
<verify>
|
||||
`php -l wp-content/plugins/carei-reservation/includes/class-rest-proxy.php` — brak syntax errors.
|
||||
</verify>
|
||||
<done>AC-3 (branches endpoint), AC-4 (car-classes + pricelist endpoints)</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Elementor Widget shell z przyciskiem-triggerem</name>
|
||||
<files>wp-content/plugins/carei-reservation/includes/class-elementor-widget.php</files>
|
||||
<action>
|
||||
**class-elementor-widget.php:**
|
||||
- Klasa `Carei_Reservation_Widget extends \Elementor\Widget_Base`
|
||||
- `get_name()`: 'carei-reservation'
|
||||
- `get_title()`: 'Carei Reservation'
|
||||
- `get_icon()`: 'eicon-form-horizontal'
|
||||
- `get_categories()`: ['general']
|
||||
- `get_style_depends()`: ['carei-reservation-css'] (placeholder)
|
||||
- `get_script_depends()`: ['carei-reservation-js'] (placeholder)
|
||||
- `register_controls()`:
|
||||
- Section: button_content → kontrolka tekstu przycisku (default: "Złóż zapytanie o rezerwację")
|
||||
- `render()`:
|
||||
- Przycisk HTML z klasą `carei-reservation-trigger`
|
||||
- Inline style: czerwony (#FF0000) background, biały tekst, Albert Sans font, border-radius 8px
|
||||
- Ikona strzałki (SVG inline) przed tekstem
|
||||
- Pusty div `<div id="carei-reservation-modal"></div>` jako mount point dla modala
|
||||
- Inline `<script>` z eventem click na trigger → modal placeholder (console.log na razie)
|
||||
- Enqueue placeholder CSS/JS w `wp_enqueue_scripts` (pliki puste, zostaną wypełnione w Phase 2)
|
||||
</action>
|
||||
<verify>
|
||||
`php -l wp-content/plugins/carei-reservation/includes/class-elementor-widget.php` — brak syntax errors.
|
||||
</verify>
|
||||
<done>AC-5 (widget z przyciskiem-trigger i pustym modalem)</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- wp-content/plugins/elementor-addon/* (istniejący plugin — nie modyfikuj)
|
||||
- wp-content/themes/hello-elementor/* (theme bez zmian)
|
||||
- .env (tylko odczyt, nigdy nie commituj)
|
||||
- docs/* (dokumentacja — read only)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Ten plan NIE buduje UI formularza (to Phase 2)
|
||||
- Brak frontendu modal/form — tylko pusty placeholder div i console.log
|
||||
- Brak walidacji formularza — tylko sanitization parametrów REST
|
||||
- Nie dodawaj zależności composer/npm
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] `php -l` na wszystkich 4 plikach PHP — brak błędów składni
|
||||
- [ ] Plugin structure: carei-reservation.php + includes/ z 3 klasami
|
||||
- [ ] .env jest parsowany poprawnie (format `key: value`)
|
||||
- [ ] Softra API class ma get_token() z transient cache
|
||||
- [ ] REST routes zarejestrowane w namespace carei/v1
|
||||
- [ ] Widget renderuje przycisk z odpowiednim stylem
|
||||
- [ ] Credentials NIE są exposowane na frontend
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie 3 taski wykonane
|
||||
- Brak PHP syntax errors
|
||||
- Plugin gotowy do aktywacji w WP
|
||||
- REST API proxy gotowe do konsumpcji przez frontend (Phase 2)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/01-reservation-form-plugin/01-01-SUMMARY.md`
|
||||
</output>
|
||||
111
.paul/phases/01-reservation-form-plugin/01-01-SUMMARY.md
Normal file
111
.paul/phases/01-reservation-form-plugin/01-01-SUMMARY.md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
phase: 01-reservation-form-plugin
|
||||
plan: 01
|
||||
subsystem: api
|
||||
tags: [php, wordpress, elementor, softra-rent-api, jwt, rest-proxy]
|
||||
|
||||
requires: []
|
||||
provides:
|
||||
- Softra Rent API client with JWT caching
|
||||
- WP REST proxy (9 endpoints in carei/v1)
|
||||
- Elementor widget shell with modal overlay
|
||||
affects: [02-form-ui-step1, 03-form-ui-overlay]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [singleton API client, WP transient token cache, REST proxy pattern]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- wp-content/plugins/carei-reservation/carei-reservation.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-softra-api.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||
|
||||
key-decisions:
|
||||
- "Separate plugin carei-reservation (not extending elementor-addon)"
|
||||
- "JWT token cached 50min via WP transient (60min validity)"
|
||||
- "Nonce check on POST endpoints, public GET for branches/agreements"
|
||||
- ".env parsed as key: value format (not KEY=value)"
|
||||
|
||||
patterns-established:
|
||||
- "Singleton Carei_Softra_API::get_instance() for all API calls"
|
||||
- "REST proxy pattern: frontend -> WP REST -> Softra API"
|
||||
- "Modal overlay with data-attributes for open/close"
|
||||
|
||||
duration: ~15min
|
||||
completed: 2026-03-25
|
||||
---
|
||||
|
||||
# Phase 1 Plan 01: Plugin Skeleton + API Proxy Summary
|
||||
|
||||
**WordPress plugin carei-reservation z klasą proxy Softra Rent API (JWT + cache), 9 WP REST endpoints i widgetem Elementor (przycisk CTA + modal shell).**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~15min |
|
||||
| Completed | 2026-03-25 |
|
||||
| Tasks | 3 completed |
|
||||
| Files created | 6 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Plugin aktywuje się bez błędów | Pass | `php -l` na 4 plikach PHP — zero errors |
|
||||
| AC-2: API Proxy — autoryzacja i cache tokenu | Pass | get_token() z WP transient 50min cache |
|
||||
| AC-3: REST API proxy zwraca dane oddziałów | Pass | GET /wp-json/carei/v1/branches → Softra /branch/list |
|
||||
| AC-4: REST API proxy zwraca klasy i cennik | Pass | POST car-classes + pricelist endpoints zarejestrowane |
|
||||
| AC-5: Widget Elementor renderuje przycisk-trigger | Pass | Przycisk + modal overlay z open/close/ESC |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Klasa `Carei_Softra_API` singleton z JWT auth, 50min transient cache, i 9 metodami publicznymi (branches, car classes, models, pricelist, pricing summary, customer, booking, confirm, agreements)
|
||||
- 9 WP REST routes w namespace `carei/v1` z nonce verification na POST i sanitization parametrów
|
||||
- Widget Elementor z przyciskiem CTA (czerwony, Albert Sans, ikona strzałki) i modalem (overlay desktop, full-screen mobile) gotowym na mount formularza w Phase 2
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `wp-content/plugins/carei-reservation/carei-reservation.php` | Created | Main plugin file: .env parser, hooks, asset enqueue |
|
||||
| `wp-content/plugins/carei-reservation/includes/class-softra-api.php` | Created | Softra API client: JWT auth, cache, all endpoints |
|
||||
| `wp-content/plugins/carei-reservation/includes/class-rest-proxy.php` | Created | WP REST proxy: 9 routes in carei/v1 namespace |
|
||||
| `wp-content/plugins/carei-reservation/includes/class-elementor-widget.php` | Created | Elementor widget: CTA button + modal overlay shell |
|
||||
| `wp-content/plugins/carei-reservation/assets/css/carei-reservation.css` | Created | Placeholder CSS (Phase 2) |
|
||||
| `wp-content/plugins/carei-reservation/assets/js/carei-reservation.js` | Created | Placeholder JS (Phase 2) |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Osobny plugin (nie rozbudowa elementor-addon) | Czystsza separacja, łatwiejsze zarządzanie, niezależny deployment | Brak konfliktu z istniejącym kodem widgetów |
|
||||
| sslverify=false w wp_remote_request | Softra API na porcie 8444 z self-signed cert (test env) | Zmienić na true dla produkcji |
|
||||
| Inline style w widget render() | Minimalna zależność, nie wymaga osobnego pliku na phase 1 | Phase 2 przeniesie style do carei-reservation.css |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- REST API proxy gotowe do konsumpcji przez frontend JS
|
||||
- Modal overlay mount point (`#carei-form-container`) czeka na formularz
|
||||
- `wp_localize_script` dostarcza `restUrl` i `nonce` do JS
|
||||
|
||||
**Concerns:**
|
||||
- sslverify=false — do zmiany przed produkcją
|
||||
- Inline styles w widget → przenieść do CSS w Phase 2
|
||||
|
||||
**Blockers:** None
|
||||
|
||||
---
|
||||
*Phase: 01-reservation-form-plugin, Plan: 01*
|
||||
*Completed: 2026-03-25*
|
||||
278
.paul/phases/02-form-ui-step1/02-01-PLAN.md
Normal file
278
.paul/phases/02-form-ui-step1/02-01-PLAN.md
Normal file
@@ -0,0 +1,278 @@
|
||||
---
|
||||
phase: 02-form-ui-step1
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["01-01"]
|
||||
files_modified:
|
||||
- wp-content/plugins/carei-reservation/assets/css/carei-reservation.css
|
||||
- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||
- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||
autonomous: false
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Zbudować kompletny frontend formularza rezerwacji (Krok 1) zgodny z projektem Figma — HTML, CSS, JS z dynamicznym ładowaniem danych z API Softra (oddziały, klasy pojazdów, opcje dodatkowe).
|
||||
|
||||
## Purpose
|
||||
To jest core UI — to co widzi użytkownik po kliknięciu "Złóż zapytanie o rezerwację". Bez tego formularza nie ma produktu.
|
||||
|
||||
## Output
|
||||
Działający formularz w modalu Elementor z pełnym stylingiem Figma i integracją API.
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
|
||||
## Prior Work
|
||||
@.paul/phases/01-reservation-form-plugin/01-01-SUMMARY.md
|
||||
- Widget Elementor z modalem (mount point: `#carei-form-container`)
|
||||
- REST proxy endpoints: GET /branches, POST /car-classes, POST /pricelist, GET /agreements
|
||||
- JS localized vars: `careiReservation.restUrl`, `careiReservation.nonce`
|
||||
|
||||
## Design Spec
|
||||
@docs/figma-formularz/README.md (kolory, typografia, wymiary, struktura, responsive)
|
||||
@docs/figma-formularz/screenshot-desktop.png
|
||||
@docs/figma-formularz/screenshot-mobile.png
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Formularz renderuje się w modalu zgodnie z Figmą
|
||||
```gherkin
|
||||
Given użytkownik kliknie przycisk "Złóż zapytanie o rezerwację"
|
||||
When modal się otworzy
|
||||
Then widoczny jest formularz z sekcjami: dane wynajmu, opcje dodatkowe, dane najemcy, wiadomość, stopka
|
||||
And kolory, typografia i wymiary odpowiadają specyfikacji Figma
|
||||
And na desktop formularz jest wycentrowanym modalem (max 800px)
|
||||
And na mobile formularz jest full-screen bottom sheet
|
||||
```
|
||||
|
||||
## AC-2: Dynamiczne dane z API
|
||||
```gherkin
|
||||
Given modal jest otwarty
|
||||
When formularz się ładuje
|
||||
Then dropdown "Miejsce odbioru" zawiera listę oddziałów z API /branches
|
||||
And dropdown "Segment pojazdu" jest początkowo pusty (wymaga wybrania dat i oddziału)
|
||||
When użytkownik wybierze oddział i daty
|
||||
Then dropdown "Segment pojazdu" ładuje klasy z API /car-classes
|
||||
And "Wybrano: X dni" oblicza się automatycznie
|
||||
```
|
||||
|
||||
## AC-3: Interakcje formularza działają poprawnie
|
||||
```gherkin
|
||||
Given formularz jest wyświetlony
|
||||
When użytkownik zaznacza/odznacza "Zwrot w tej samej lokalizacji"
|
||||
Then pole "Miejsce zwrotu" pojawia się/znika
|
||||
When użytkownik zaznacza opcje dodatkowe
|
||||
Then checkboxy wizualnie się zmieniają (Carei Blue fill)
|
||||
When użytkownik klika "Wyślij" bez wypełnienia wymaganych pól
|
||||
Then wyświetlane są komunikaty walidacji przy pustych polach
|
||||
When formularz jest poprawnie wypełniony i "Wyślij" kliknięte
|
||||
Then dane są zbierane i gotowe do wysłania (console.log na razie — Phase 3 obsłuży submission)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: HTML formularza + CSS zgodny z Figmą</name>
|
||||
<files>wp-content/plugins/carei-reservation/includes/class-elementor-widget.php, wp-content/plugins/carei-reservation/assets/css/carei-reservation.css</files>
|
||||
<action>
|
||||
**class-elementor-widget.php — przebuduj render():**
|
||||
- Usuń placeholder `<p>Formularz rezerwacji — ładowanie...</p>`
|
||||
- Przenieś WSZYSTKIE style inline (z obecnego render()) do pliku CSS
|
||||
- Wstaw kompletny HTML formularza w `#carei-form-container`:
|
||||
|
||||
Struktura HTML (klasy BEM: `carei-form__*`):
|
||||
```
|
||||
form.carei-form
|
||||
├─ .carei-form__section (Dane wynajmu)
|
||||
│ ├─ .carei-form__row (segment dropdown + daty)
|
||||
│ │ ├─ select#carei-segment (pełna szerokość na górze)
|
||||
│ │ ├─ .carei-form__dates (Od kiedy + Do kiedy obok siebie)
|
||||
│ │ └─ .carei-form__days-count ("Wybrano: X dni")
|
||||
│ ├─ .carei-form__row (miejsce odbioru)
|
||||
│ │ ├─ select#carei-pickup-branch (z ikoną MapPin SVG)
|
||||
│ │ └─ label.carei-form__checkbox (Zwrot w tej samej lokalizacji)
|
||||
│ └─ select#carei-return-branch (ukryty domyślnie)
|
||||
├─ .carei-form__divider + label "Opcje dodatkowe"
|
||||
├─ .carei-form__section.carei-form__extras
|
||||
│ ├─ .carei-form__extra-card (Ubezpieczenie — checkbox + opis + cena)
|
||||
│ └─ .carei-form__extra-card (Fotelik — checkbox + opis + cena)
|
||||
├─ .carei-form__divider + label "Dane najemcy"
|
||||
├─ .carei-form__section (Dane osobowe)
|
||||
│ ├─ .carei-form__row (Imię + Nazwisko — 2 kolumny desktop, 1 mobile)
|
||||
│ ├─ .carei-form__row (Email + Telefon — 2 kolumny desktop, 1 mobile)
|
||||
│ │ └─ Telefon: prefix "+48" z flagą PL (statyczną), input z placeholder
|
||||
│ └─ textarea (Twoja wiadomość dotycząca rezerwacji)
|
||||
└─ .carei-form__footer
|
||||
├─ label.carei-form__checkbox (Zgadzam się na Politykę Prywatności — link)
|
||||
└─ button.carei-form__submit (ikona strzałki + "Wyślij")
|
||||
```
|
||||
|
||||
- Dropdown "Segment pojazdu": `<select>` z `<option disabled selected>` placeholder
|
||||
- Dropdown "Miejsce odbioru": `<select>` z opcjami ładowanymi z API
|
||||
- Daty: `<input type="datetime-local">` dla "Od kiedy", `<input type="datetime-local">` dla "Do kiedy"
|
||||
- Opcje dodatkowe: karty z checkbox, tytułem bold, opisem, ceną w kolorze red
|
||||
- Telefon: div z flagą PL (emoji 🇵🇱 lub SVG) + "+48" + input type="tel"
|
||||
- Script tag z modal open/close (zachowaj istniejącą logikę)
|
||||
|
||||
**carei-reservation.css — kompletne style:**
|
||||
- CSS custom properties na górze:
|
||||
```
|
||||
--carei-blue: #2F2482;
|
||||
--carei-red: #FF0000;
|
||||
--carei-bg: #EDEDF3;
|
||||
--carei-gray: #505050;
|
||||
--carei-placeholder: #C7C7C7;
|
||||
--carei-border: #D0D0D0;
|
||||
--carei-radius: 8px;
|
||||
--carei-input-h: 48px;
|
||||
```
|
||||
- @import Google Fonts Albert Sans (400,500,600,700)
|
||||
- Modal: `.carei-modal-overlay`, `.carei-modal` (przenieść z inline)
|
||||
- Form layout: CSS Grid dla sekcji, gap 24px między sekcjami, 16px wewnątrz
|
||||
- Inputs: height 48px, border-radius 8px, border 1px solid transparent, focus border carei-blue
|
||||
- Desktop: `.carei-form__row` = grid 2 kolumny (1fr 1fr)
|
||||
- Mobile (<768px): 1 kolumna, modal full-screen, przycisk full-width
|
||||
- Dividers: linia 1px #D0D0D0 z label centrowanym (jak w Figmie — text na tle linii)
|
||||
- Checkbox custom: 24px, border-radius 8px, checked = carei-blue fill z białym checkmark
|
||||
- Extra cards: border 1px #D0D0D0, border-radius 8px, padding 16px
|
||||
- Submit button: bg carei-red, color white, font-weight 600, border-radius 8px, hover darken
|
||||
- Footer: flex, space-between desktop, column mobile
|
||||
- Walidacja: `.carei-form__field--error` z czerwonym border + komunikat
|
||||
</action>
|
||||
<verify>
|
||||
`php -l wp-content/plugins/carei-reservation/includes/class-elementor-widget.php` — brak syntax errors.
|
||||
CSS plik zawiera minimum 100 linii stylów.
|
||||
</verify>
|
||||
<done>AC-1 satisfied: formularz renderuje się w modalu zgodnie z Figmą</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: JavaScript — API integration, dynamiczne dane, interakcje</name>
|
||||
<files>wp-content/plugins/carei-reservation/assets/js/carei-reservation.js</files>
|
||||
<action>
|
||||
**carei-reservation.js — kompletna logika formularza:**
|
||||
|
||||
Moduł IIFE `(function() { ... })()` korzystający z `careiReservation.restUrl` i `careiReservation.nonce`.
|
||||
|
||||
**1. API Helper:**
|
||||
```js
|
||||
async function apiGet(endpoint) — fetch GET z nonce header
|
||||
async function apiPost(endpoint, data) — fetch POST z nonce header + JSON body
|
||||
```
|
||||
|
||||
**2. Inicjalizacja (na DOMContentLoaded + modal open):**
|
||||
- `loadBranches()` — GET /branches → populate select#carei-pickup-branch i select#carei-return-branch
|
||||
- Ustawić domyślne daty: "Od kiedy" = jutro 10:00, "Do kiedy" = pojutrze 10:00
|
||||
- Oblicz "Wybrano: X dni"
|
||||
|
||||
**3. Dynamiczne ładowanie klas pojazdów:**
|
||||
- Event listener na zmianę dat i oddziału
|
||||
- Gdy dateFrom + dateTo + branchName wypełnione → POST /car-classes → populate select#carei-segment
|
||||
- Pokazać loading spinner w select podczas ładowania
|
||||
- Gdy brak wyników → opcja "Brak dostępnych klas"
|
||||
|
||||
**4. Auto-kalkulacja dni:**
|
||||
- Listener na zmianę obu dat
|
||||
- Oblicz różnicę w dniach (ceil), wyświetl w `.carei-form__days-count`
|
||||
- Walidacja: dateTo > dateFrom
|
||||
|
||||
**5. Checkbox "Zwrot w tej samej lokalizacji":**
|
||||
- Default: checked → pole zwrotu ukryte
|
||||
- Uncheck → pokaż select#carei-return-branch (animate slideDown)
|
||||
- Check → ukryj (animate slideUp)
|
||||
|
||||
**6. Walidacja formularza:**
|
||||
- Wymagane pola: segment, dateFrom, dateTo, pickup branch, imię, nazwisko, email, telefon, zgoda RODO
|
||||
- Na submit: sprawdź każde pole, dodaj `.carei-form__field--error` + komunikat pod polem
|
||||
- Na focus na polu z błędem: usuń error
|
||||
- Email: prosty regex check
|
||||
- Telefon: min 9 cyfr
|
||||
|
||||
**7. Zbieranie danych i submit:**
|
||||
- Na klik "Wyślij" + walidacja OK:
|
||||
- Zbierz wszystkie dane formularza do obiektu
|
||||
- Na razie: `console.log('Form data:', formData)` — Phase 3 obsłuży API submission
|
||||
- Przycisk: zmień tekst na "Wysyłanie..." + disable (przygotowanie na Phase 3)
|
||||
- NIE wysyłaj jeszcze do API booking — to Phase 3
|
||||
|
||||
**8. Opcje dodatkowe z API:**
|
||||
- Po załadowaniu cennika (gdy klasa + daty + oddział wybrane) → POST /pricelist
|
||||
- Z odpowiedzi wyciągnij `additionalItems` → dynamicznie wygeneruj karty opcji dodatkowych
|
||||
- Jeśli API nie zwróci additionalItems → pokaż statyczne opcje (ubezpieczenie 300zł, fotelik 50zł) jako fallback
|
||||
</action>
|
||||
<verify>
|
||||
Plik JS nie zawiera syntax errors (brak `SyntaxError` w `node -e "require('fs').readFileSync('...','utf8')" 2>&1`).
|
||||
Plik JS zawiera minimum 150 linii kodu.
|
||||
</verify>
|
||||
<done>AC-2 (dynamiczne dane z API), AC-3 (interakcje formularza)</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>Kompletny formularz rezerwacji (Krok 1) w modalu Elementor z integracją API Softra</what-built>
|
||||
<how-to-verify>
|
||||
1. Wgraj pliki na serwer (carei.pagedev.pl)
|
||||
2. Otwórz https://carei.pagedev.pl/
|
||||
3. Kliknij "Złóż zapytanie o rezerwację"
|
||||
4. Sprawdź:
|
||||
- Modal otwiera się z formularzem
|
||||
- Dropdown "Miejsce odbioru" ładuje oddziały z API
|
||||
- Po wybraniu dat i oddziału — "Segment pojazdu" ładuje klasy z API
|
||||
- "Wybrano: X dni" oblicza się automatycznie
|
||||
- Checkbox "Zwrot w tej samej lokalizacji" pokazuje/ukrywa drugie pole
|
||||
- Opcje dodatkowe wyświetlają się jako karty z ceną
|
||||
- Responsywność: na mobile (375px) — full-screen, 1 kolumna
|
||||
- Klik "Wyślij" bez danych → walidacja (czerwone obramowania)
|
||||
- Klik "Wyślij" z danymi → console.log z obiektem danych
|
||||
5. Porównaj wizualnie z screenshotami Figma w docs/figma-formularz/
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" to continue to Phase 3, or describe issues to fix</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- wp-content/plugins/carei-reservation/carei-reservation.php (main plugin — Phase 1, already fixed)
|
||||
- wp-content/plugins/carei-reservation/includes/class-softra-api.php (API client — Phase 1)
|
||||
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php (REST routes — Phase 1)
|
||||
- wp-content/plugins/elementor-addon/* (istniejący plugin)
|
||||
- wp-content/themes/hello-elementor/* (theme)
|
||||
- .env, docs/*
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Ten plan buduje UI formularza i integrację API do ładowania danych
|
||||
- NIE implementuje wysyłki rezerwacji (Phase 3)
|
||||
- NIE buduje Kroku 2 / Overlay (Phase 3)
|
||||
- Przycisk "Wyślij" zbiera dane i loguje do console — nie wysyła do API
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] `php -l` na class-elementor-widget.php — brak błędów
|
||||
- [ ] CSS plik ma >100 linii z pełnym stylingiem
|
||||
- [ ] JS plik ma >150 linii z API integration
|
||||
- [ ] Formularz renderuje HTML z wszystkimi sekcjami z Figmy
|
||||
- [ ] Responsive: desktop 2-kolumny, mobile 1-kolumna
|
||||
- [ ] Human verify: wizualne porównanie z Figmą na żywym serwerze
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Formularz wygląda jak w Figmie (desktop + mobile)
|
||||
- Dane z API Softra ładują się dynamicznie
|
||||
- Interakcje (checkboxy, daty, walidacja) działają
|
||||
- Dane formularza zbierane i gotowe do wysłania (Phase 3)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/02-form-ui-step1/02-01-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user