Files
carei.pagedev.pl/.paul/phases/03-form-submit-booking/03-01-PLAN.md
2026-03-25 17:45:13 +01:00

17 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous
phase plan type wave depends_on files_modified autonomous
03-form-submit-booking 01 execute 1
02-01
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
false
## 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.

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

<acceptance_criteria>

AC-1: Formularz zbiera dodatkowe dane wymagane przez API

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

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

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>

Task 1: Dodatkowe pola formularza + overlay podsumowania (HTML/CSS) wp-content/plugins/carei-reservation/includes/class-elementor-widget.php, wp-content/plugins/carei-reservation/assets/css/carei-reservation.css **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
`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`. AC-1 satisfied: dodatkowe pola adresowe i PESEL widoczne, overlay HTML gotowy Task 2: JS — submit flow (customer → pricing → booking → confirm) 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 **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")
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. AC-2 (submit tworzy klienta i pokazuje podsumowanie), AC-3 (potwierdzenie kończy flow) Kompletny flow rezerwacji: formularz → tworzenie klienta → podsumowanie kosztów → rezerwacja → potwierdzenie 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 Type "approved" to continue, or describe issues to fix

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

<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>
After completion, create `.paul/phases/03-form-submit-booking/03-01-SUMMARY.md`