--- 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 --- ## 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 ## 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 ``` 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 ``` **Widok sukcesu — dodaj PO overlay podsumowania:** ```html ``` **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 - 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 After completion, create `.paul/phases/03-form-submit-booking/03-01-SUMMARY.md`