update
This commit is contained in:
395
.paul/phases/03-form-submit-booking/03-01-PLAN.md
Normal file
395
.paul/phases/03-form-submit-booking/03-01-PLAN.md
Normal file
@@ -0,0 +1,395 @@
|
||||
---
|
||||
phase: 03-form-submit-booking
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["02-01"]
|
||||
files_modified:
|
||||
- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||
- 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-softra-api.php
|
||||
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
autonomous: false
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Zbudować kompletny flow wysyłki formularza: tworzenie klienta w API Softra → podsumowanie kosztów w overlay → złożenie rezerwacji → potwierdzenie. Formularz staje się w pełni funkcjonalny.
|
||||
|
||||
## Purpose
|
||||
Bez tego flow formularz tylko zbiera dane do console.log. To jest core value — klient składa rezerwację która trafia do systemu Softra Rent.
|
||||
|
||||
## Output
|
||||
Działający submit: formularz → API customer/add → pricing summary overlay → makebooking → confirm → komunikat sukcesu.
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
|
||||
## Prior Work
|
||||
@.paul/phases/02-form-ui-step1/02-01-SUMMARY.md
|
||||
- Formularz Krok 1 w modalu z dynamicznym ładowaniem segmentów/oddziałów/extras
|
||||
- Walidacja + zbieranie danych (console.log)
|
||||
- Pricelist ID dostępny z odpowiedzi /pricelist endpoint
|
||||
|
||||
## API Documentation
|
||||
@docs/rent-api-02-klienci-i-konta.md — customer/add wymaga: name, address, paymentMethod, isCompany, account|skipAccountCreate, pesel|passportNo|idCard (dla osób fizycznych)
|
||||
@docs/rent-api-03-rezerwacje-i-platnosci.md — makebooking wymaga: dateFrom, dateTo, customerId, pickUpLocation, returnLocation, carParameters, priceListId, validTime
|
||||
|
||||
## Source Files
|
||||
@wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
|
||||
@wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
|
||||
@wp-content/plugins/carei-reservation/assets/css/carei-reservation.css
|
||||
@wp-content/plugins/carei-reservation/includes/class-softra-api.php
|
||||
@wp-content/plugins/carei-reservation/includes/class-rest-proxy.php
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Formularz zbiera dodatkowe dane wymagane przez API
|
||||
```gherkin
|
||||
Given formularz rezerwacji jest otwarty
|
||||
When użytkownik przewija do sekcji "Dane najemcy"
|
||||
Then widoczne są dodatkowe pola: Miejscowość, Kod pocztowy, Ulica, PESEL
|
||||
And pola adresowe i PESEL są wymagane przy walidacji
|
||||
```
|
||||
|
||||
## AC-2: Submit formularza tworzy klienta i pokazuje podsumowanie kosztów
|
||||
```gherkin
|
||||
Given użytkownik wypełnił wszystkie wymagane pola formularza
|
||||
When klika "Wyślij"
|
||||
Then przycisk zmienia się na "Przetwarzanie..." (disabled)
|
||||
And w tle: tworzony jest klient via /customer/add
|
||||
And w tle: pobierane jest podsumowanie kosztów via /rent/princingSummary
|
||||
And wyświetla się overlay z podsumowaniem: lista opłat, wartość netto/brutto
|
||||
And overlay ma przycisk "Potwierdź rezerwację" i "Wróć do formularza"
|
||||
```
|
||||
|
||||
## AC-3: Potwierdzenie rezerwacji kończy flow
|
||||
```gherkin
|
||||
Given overlay z podsumowaniem jest wyświetlony
|
||||
When użytkownik klika "Potwierdź rezerwację"
|
||||
Then w tle: tworzona jest rezerwacja via /rent/makebooking
|
||||
And w tle: rezerwacja jest potwierdzana via /rent/confirm
|
||||
And overlay zamienia się na komunikat sukcesu z numerem rezerwacji
|
||||
And jest przycisk "Zamknij" który zamyka modal
|
||||
|
||||
When rezerwacja się nie powiedzie (API error)
|
||||
Then wyświetla się komunikat błędu z przyczyną (rejectReason)
|
||||
And użytkownik może wrócić do formularza i spróbować ponownie
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Dodatkowe pola formularza + overlay podsumowania (HTML/CSS)</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 — dodaj do sekcji "Dane najemcy":**
|
||||
|
||||
Po wierszu Imię/Nazwisko, przed wierszem Email/Telefon, dodaj wiersz adresowy:
|
||||
```
|
||||
.carei-form__row (3 kolumny: Miejscowość | Kod pocztowy | Ulica)
|
||||
├─ input#carei-city (Miejscowość, required)
|
||||
├─ input#carei-zipcode (Kod pocztowy, placeholder "00-000", required)
|
||||
└─ input#carei-street (Ulica i nr domu, required)
|
||||
```
|
||||
|
||||
Po wierszu Email/Telefon dodaj wiersz PESEL:
|
||||
```
|
||||
.carei-form__field.carei-form__field--half
|
||||
└─ input#carei-pesel (PESEL, type="text", maxlength="11", required)
|
||||
```
|
||||
|
||||
**Overlay podsumowania — dodaj PO form, ale wewnątrz .carei-modal:**
|
||||
|
||||
```html
|
||||
<div id="carei-summary-overlay" class="carei-summary" style="display:none;">
|
||||
<h3 class="carei-summary__title">Podsumowanie rezerwacji</h3>
|
||||
<div class="carei-summary__details" id="carei-summary-details">
|
||||
<!-- JS wypełni: segment, daty, oddział -->
|
||||
</div>
|
||||
<div class="carei-summary__table" id="carei-summary-table">
|
||||
<!-- JS wypełni: tabela opłat z princingSummary -->
|
||||
</div>
|
||||
<div class="carei-summary__total" id="carei-summary-total">
|
||||
<!-- JS wypełni: netto, VAT, brutto -->
|
||||
</div>
|
||||
<div class="carei-summary__actions">
|
||||
<button type="button" class="carei-summary__btn carei-summary__btn--back" id="carei-summary-back">
|
||||
Wróć do formularza
|
||||
</button>
|
||||
<button type="button" class="carei-summary__btn carei-summary__btn--confirm" id="carei-summary-confirm">
|
||||
Potwierdź rezerwację
|
||||
</button>
|
||||
</div>
|
||||
<div class="carei-summary__error" id="carei-summary-error" style="display:none;"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Widok sukcesu — dodaj PO overlay podsumowania:**
|
||||
|
||||
```html
|
||||
<div id="carei-success-view" class="carei-success" style="display:none;">
|
||||
<div class="carei-success__icon">✓</div>
|
||||
<h3 class="carei-success__title">Rezerwacja złożona!</h3>
|
||||
<p class="carei-success__number" id="carei-success-number"></p>
|
||||
<p class="carei-success__message">Potwierdzenie zostało wysłane na podany adres e-mail.</p>
|
||||
<button type="button" class="carei-success__close" id="carei-success-close">Zamknij</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
**carei-reservation.css — dodaj style:**
|
||||
|
||||
- `.carei-form__row--address`: grid 3 kolumny (1fr auto 1fr), mobile: 1 kolumna
|
||||
- `.carei-form__field--half`: max-width 50%, mobile: 100%
|
||||
- `.carei-summary`: pełna wysokość modala, flex column, gap 24px, padding jak modal
|
||||
- `.carei-summary__title`: jak .carei-modal-title
|
||||
- `.carei-summary__table`: tabela z opłatami — wiersz: nazwa | ilość | cena | wartość
|
||||
- `.carei-summary__table table`: width 100%, border-collapse, alternating rows
|
||||
- `.carei-summary__total`: pogrubiony, wyrównany do prawej, netto/VAT/brutto
|
||||
- `.carei-summary__actions`: flex, gap 16px, justify-content space-between
|
||||
- `.carei-summary__btn--back`: border 1px solid carei-blue, bg transparent, color carei-blue
|
||||
- `.carei-summary__btn--confirm`: bg carei-red, color white (jak submit)
|
||||
- `.carei-summary__error`: bg rgba(255,0,0,0.05), color red, padding 12px, border-radius
|
||||
- `.carei-success`: flex column, align-items center, text-align center, gap 16px
|
||||
- `.carei-success__icon`: 64px circle, bg green, color white, font-size 32px
|
||||
- `.carei-success__close`: jak .carei-form__submit
|
||||
</action>
|
||||
<verify>
|
||||
`php -l wp-content/plugins/carei-reservation/includes/class-elementor-widget.php` — brak syntax errors.
|
||||
CSS plik zawiera style dla `.carei-summary` i `.carei-success`.
|
||||
</verify>
|
||||
<done>AC-1 satisfied: dodatkowe pola adresowe i PESEL widoczne, overlay HTML gotowy</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: JS — submit flow (customer → pricing → booking → confirm)</name>
|
||||
<files>wp-content/plugins/carei-reservation/assets/js/carei-reservation.js, wp-content/plugins/carei-reservation/includes/class-softra-api.php, wp-content/plugins/carei-reservation/includes/class-rest-proxy.php</files>
|
||||
<action>
|
||||
**JS — przepisz initSubmit() i dodaj nowe funkcje:**
|
||||
|
||||
**Stan globalny — dodaj zmienne:**
|
||||
```
|
||||
var currentCustomerId = null;
|
||||
var currentPriceListId = null; // z odpowiedzi pricelist (już ładowany w loadExtras)
|
||||
var currentPricingSummary = null;
|
||||
var currentReservationId = null;
|
||||
var summaryOverlay, successView, summaryBack, summaryConfirm;
|
||||
```
|
||||
|
||||
**initRefs() — dodaj referencje:**
|
||||
- summaryOverlay = getElementById('carei-summary-overlay')
|
||||
- successView = getElementById('carei-success-view')
|
||||
- summaryBack = getElementById('carei-summary-back')
|
||||
- summaryConfirm = getElementById('carei-summary-confirm')
|
||||
- summaryError = getElementById('carei-summary-error')
|
||||
|
||||
**loadExtras() — zapisz priceListId:**
|
||||
- Przy ładowaniu pricelist, zapisz `currentPriceListId = pricelist.id` z pierwszego elementu odpowiedzi
|
||||
|
||||
**Nowy flow submit:**
|
||||
|
||||
1. `handleSubmit()`:
|
||||
- Walidacja (jak teraz)
|
||||
- Disable submit btn → "Przetwarzanie..."
|
||||
- Wywołaj `createCustomerAndShowSummary()`
|
||||
|
||||
2. `createCustomerAndShowSummary()`:
|
||||
- Zbierz dane formularza
|
||||
- POST /customer z danymi:
|
||||
```json
|
||||
{
|
||||
"firstName": formData.firstName,
|
||||
"lastName": formData.lastName,
|
||||
"name": firstName + " " + lastName,
|
||||
"isCompany": "N",
|
||||
"address": {
|
||||
"city": formData.city,
|
||||
"zipCode": formData.zipCode,
|
||||
"street": formData.street,
|
||||
"homeNo": "-"
|
||||
},
|
||||
"pesel": formData.pesel,
|
||||
"email": formData.email,
|
||||
"phoneMobile": formData.phone,
|
||||
"paymentMethod": "GOTÓWKA",
|
||||
"skipAccountCreate": "T",
|
||||
"emailVerified": true
|
||||
}
|
||||
```
|
||||
- On success: `currentCustomerId = response.customerId`
|
||||
- Wywołaj `loadPricingSummary()`
|
||||
- On error: pokaż błąd, re-enable submit btn
|
||||
|
||||
3. `loadPricingSummary()`:
|
||||
- POST /pricing-summary z danymi:
|
||||
```json
|
||||
{
|
||||
"dateFrom": formData.dateFrom + ":00",
|
||||
"dateTo": formData.dateTo + ":00",
|
||||
"customerId": currentCustomerId,
|
||||
"pickUpLocation": { "branchName": formData.pickupBranch, "outOfBranch": "N" },
|
||||
"returnLocation": { "branchName": returnBranch, "outOfBranch": "N" },
|
||||
"carParameters": { "categoryName": formData.segment },
|
||||
"priceListId": currentPriceListId,
|
||||
"priceItems": selectedExtrasAsBookingPriceItems()
|
||||
}
|
||||
```
|
||||
- On success: `currentPricingSummary = response`
|
||||
- Wywołaj `showSummaryOverlay(response)`
|
||||
- On error: pokaż błąd, re-enable submit btn
|
||||
|
||||
4. `selectedExtrasAsBookingPriceItems()`:
|
||||
- Dla każdego zaznaczonego checkbox extras[]:
|
||||
```json
|
||||
{
|
||||
"id": checkbox.value,
|
||||
"name": checkbox label text,
|
||||
"unit": "szt.",
|
||||
"amount": 1,
|
||||
"priceBeforeDiscount": checkbox.dataset.price,
|
||||
"discount": 0,
|
||||
"priceAfterDiscount": checkbox.dataset.price
|
||||
}
|
||||
```
|
||||
|
||||
5. `showSummaryOverlay(summary)`:
|
||||
- Ukryj form, pokaż summaryOverlay
|
||||
- Wypełnij #carei-summary-details: segment, daty, oddział
|
||||
- Wypełnij #carei-summary-table: tabela z summary.pricelist items
|
||||
- Kolumny: Nazwa | Ilość | Cena netto | Wartość brutto
|
||||
- Oznacz wiersze addedBySystem=true (np. kursywą + "doliczone automatycznie")
|
||||
- Wypełnij #carei-summary-total: totalNetValue, totalVatValue, totalGrossValue
|
||||
|
||||
6. `handleSummaryBack()`:
|
||||
- Ukryj summaryOverlay, pokaż form
|
||||
- Re-enable submit btn
|
||||
|
||||
7. `handleSummaryConfirm()`:
|
||||
- Disable confirm btn → "Rezerwuję..."
|
||||
- POST /booking z pełnymi danymi makebooking:
|
||||
```json
|
||||
{
|
||||
"dateFrom": ..., "dateTo": ...,
|
||||
"customerId": currentCustomerId,
|
||||
"pickUpLocation": { "branchName": ..., "outOfBranch": "N" },
|
||||
"returnLocation": { "branchName": ..., "outOfBranch": "N" },
|
||||
"carParameters": { "categoryName": segment },
|
||||
"priceListId": currentPriceListId,
|
||||
"validTime": 30,
|
||||
"priceItems": selectedExtrasAsBookingPriceItems(),
|
||||
"agreementItems": agreementItemsFromForm(),
|
||||
"comments": formData.message
|
||||
}
|
||||
```
|
||||
- On success (response.success=true):
|
||||
- `currentReservationId = response.reservationId`
|
||||
- POST /booking/confirm z { reservationId }
|
||||
- Pokaż success view z response.reservationNo
|
||||
- On error: pokaż błąd w #carei-summary-error, re-enable btn
|
||||
|
||||
8. `agreementItemsFromForm()`:
|
||||
- Privacy checkbox → return [{ id: agreementId, value: true }]
|
||||
- AgreementId: załaduj raz z GET /agreements na modal open, zapisz w zmiennej
|
||||
|
||||
9. `showSuccessView(reservationNo)`:
|
||||
- Ukryj summaryOverlay, pokaż successView
|
||||
- Wypełnij #carei-success-number: "Nr rezerwacji: " + reservationNo
|
||||
|
||||
10. `handleSuccessClose()`:
|
||||
- Zamknij modal (closeModal())
|
||||
- Reset formularza
|
||||
|
||||
**Walidacja — dodaj nowe wymagane pola do requiredFields:**
|
||||
- { id: 'carei-city', type: 'input', msg: 'Podaj miejscowość' }
|
||||
- { id: 'carei-zipcode', type: 'input', msg: 'Podaj kod pocztowy' }
|
||||
- { id: 'carei-street', type: 'input', msg: 'Podaj ulicę' }
|
||||
- { id: 'carei-pesel', type: 'pesel', msg: 'Podaj poprawny PESEL (11 cyfr)' }
|
||||
- Walidacja PESEL: dokładnie 11 cyfr
|
||||
|
||||
**REST proxy — dodaj brakujący endpoint /cancel:**
|
||||
W class-rest-proxy.php dodaj route POST /booking/cancel.
|
||||
W class-softra-api.php metoda cancel_booking już może istnieć, jeśli nie — dodaj.
|
||||
|
||||
**WAŻNE — obsługa błędów:**
|
||||
- Każdy krok API (customer, pricing, booking, confirm) może failować
|
||||
- Przy każdym błędzie: wyświetl czytelny komunikat, pozwól wrócić do formularza
|
||||
- Timeout: jeśli API nie odpowie w 30s → "Serwer nie odpowiada, spróbuj ponownie"
|
||||
- rejectReason z API: wyświetl użytkownikowi (np. "CAR_NOT_FOUND" → "Brak dostępnego pojazdu w wybranym terminie")
|
||||
</action>
|
||||
<verify>
|
||||
JS plik nie zawiera syntax errors.
|
||||
PHP pliki: `php -l class-softra-api.php`, `php -l class-rest-proxy.php` — brak błędów.
|
||||
Flow: form submit → customer → pricing summary → display overlay.
|
||||
</verify>
|
||||
<done>AC-2 (submit tworzy klienta i pokazuje podsumowanie), AC-3 (potwierdzenie kończy flow)</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>Kompletny flow rezerwacji: formularz → tworzenie klienta → podsumowanie kosztów → rezerwacja → potwierdzenie</what-built>
|
||||
<how-to-verify>
|
||||
1. Wgraj pliki na serwer (carei.pagedev.pl)
|
||||
2. Otwórz formularz, wypełnij wszystkie pola (w tym adres, PESEL)
|
||||
3. Kliknij "Wyślij" — sprawdź:
|
||||
- Przycisk zmienia się na "Przetwarzanie..."
|
||||
- Po chwili pojawia się overlay z podsumowaniem kosztów
|
||||
- Tabela pokazuje opłaty (wynajem + ewentualne dodatki)
|
||||
- Widoczne wartości netto/VAT/brutto
|
||||
4. Kliknij "Wróć do formularza" — formularz wraca
|
||||
5. Kliknij "Wyślij" ponownie → overlay → "Potwierdź rezerwację"
|
||||
- Sprawdź czy pojawia się komunikat sukcesu z numerem rezerwacji
|
||||
6. Test błędów:
|
||||
- Wyślij z niepoprawnym PESEL → walidacja łapie
|
||||
- Wyślij z datami w przeszłości → sprawdź reakcję API
|
||||
7. Na mobile: overlay i success view powinny być responsywne
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- wp-content/plugins/carei-reservation/carei-reservation.php (main plugin file)
|
||||
- wp-content/plugins/elementor-addon/* (istniejący plugin)
|
||||
- wp-content/themes/hello-elementor/* (theme)
|
||||
- .env, docs/*
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Ten plan implementuje flow: customer → pricing summary → booking → confirm
|
||||
- NIE implementuje płatności online (preautoryzacja karty itp.)
|
||||
- NIE implementuje konta klienta (skipAccountCreate='T')
|
||||
- NIE buduje panelu admina (Phase 5)
|
||||
- Wyjazd zagraniczny i ubezpieczenie pominięte (backlog)
|
||||
- Anulowanie rezerwacji: endpoint dodany ale UI nie (future)
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] `php -l` na wszystkich zmienionych plikach PHP — brak błędów
|
||||
- [ ] Formularz zawiera pola: adres (city, zip, street), PESEL
|
||||
- [ ] Submit flow: customer/add → princingSummary → overlay z tabelą kosztów
|
||||
- [ ] Confirm flow: makebooking → confirm → success view z numerem rezerwacji
|
||||
- [ ] Error handling: API errors wyświetlane użytkownikowi
|
||||
- [ ] Responsive: overlay i success view działają na mobile
|
||||
- [ ] Human verify: pełny test flow na żywym serwerze
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Formularz składa rezerwację w systemie Softra Rent
|
||||
- Użytkownik widzi podsumowanie kosztów przed potwierdzeniem
|
||||
- Użytkownik dostaje numer rezerwacji po potwierdzeniu
|
||||
- Błędy API wyświetlane czytelnie
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/03-form-submit-booking/03-01-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user