This commit is contained in:
2026-03-25 00:41:16 +01:00
parent 1739f354d1
commit a82ec90a51
48 changed files with 4019 additions and 0 deletions

View 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>

View 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*