279 lines
12 KiB
Markdown
279 lines
12 KiB
Markdown
---
|
|
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>
|