This commit is contained in:
2026-03-25 17:45:13 +01:00
parent 2af73782f2
commit df13b3613c
11 changed files with 2341 additions and 524 deletions

View File

@@ -0,0 +1,273 @@
---
phase: 04-polish-testing
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- 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-elementor-widget.php
autonomous: false
---
<objective>
## Goal
Polish formularza rezerwacji: obsługa edge cases (wygasły token, brak dostępności, timeouty), animacje przejść form↔summary↔success, poprawki a11y (ARIA, focus management, keyboard nav), naprawa bugów CSS.
## Purpose
Formularz działa end-to-end (Phase 3), ale brak obsługi błędów brzegowych, brak animacji, a11y nie jest wdrożone. Phase 4 doprowadza formularz do jakości produkcyjnej.
## Output
Zmodyfikowane: JS (error handling + animacje + a11y), CSS (transitions + fix bugs), PHP widget (ARIA attrs).
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
## Prior Work
@.paul/phases/03-form-submit-booking/03-01-SUMMARY.md
- Phase 3: pełny booking flow działa (customer → pricing → makebooking → confirm → success)
- API discoveries: boolean true/false, drivers[] required, penalty items filtered
## Source Files
@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-elementor-widget.php
</context>
<acceptance_criteria>
## AC-1: Obsługa wygasłego tokenu JWT
```gherkin
Given formularz jest otwarty i token JWT serwera wygasł (>1h)
When użytkownik wysyła formularz lub zmienia segment/extras
Then system automatycznie odświeża token i ponawia request (retry 1x)
And użytkownik nie widzi błędu tokenu flow kontynuuje normalnie
```
## AC-2: Obsługa braku dostępności pojazdu
```gherkin
Given użytkownik wypełnił formularz i jest na ekranie podsumowania
When API makebooking zwraca rejectReason CAR_NOT_FOUND
Then wyświetla się czytelny komunikat "Brak dostępnego pojazdu w wybranym terminie. Zmień daty lub segment."
And przycisk "Wróć do formularza" jest aktywny
And użytkownik może wrócić i zmienić dane
```
## AC-3: Obsługa timeoutów i błędów sieciowych
```gherkin
Given dowolny request API trwa >15s lub sieć jest niedostępna
When fetch rzuca timeout lub network error
Then wyświetla się komunikat "Wystąpił problem z połączeniem. Spróbuj ponownie."
And przycisk submit/confirm wraca do stanu aktywnego
And formularz nie jest zablokowany
```
## AC-4: Animacje przejść między krokami
```gherkin
Given użytkownik jest na formularzu
When przechodzi do podsumowania (submit) lub do success (confirm)
Then przejście jest animowane (fade out fade in, ~300ms)
And przy powrocie z podsumowania do formularza również jest animacja
```
## AC-5: Accessibility — ARIA i focus management
```gherkin
Given użytkownik otwiera modal
When modal się otwiera
Then focus przenosi się na pierwszy interaktywny element modalu
And modal ma role="dialog" i aria-modal="true"
And focus jest trapowany w modalu (Tab nie wychodzi poza modal)
And po zamknięciu modalu focus wraca na przycisk trigger
And przejście formsummarysuccess przenosi focus na odpowiedni heading
```
## AC-6: Fix CSS — orphaned styles w media query
```gherkin
Given plik CSS ma orphaned styles poza media query (linie ~560-570)
When CSS jest naprawiony
Then style .carei-form__row--address i .carei-summary__actions są wewnątrz @media (max-width: 768px)
And nie ma podwójnego zamknięcia } na końcu media query
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Edge cases — token retry, timeout, lepsze error messages</name>
<files>wp-content/plugins/carei-reservation/assets/js/carei-reservation.js</files>
<action>
1. **Token retry:** Wrap apiGet/apiPost — jeśli response HTTP 401 lub 403, wywołaj retry 1x (token odświeża się automatycznie po stronie PHP proxy). Dodaj flagę `isRetry` żeby nie zapętlić.
2. **Timeout:** Dodaj AbortController z timeout 15s do fetch w apiGet/apiPost. Na timeout rzuć Error z komunikatem "Przekroczono czas oczekiwania. Spróbuj ponownie."
3. **Network error:** W handleResponse/catch, rozróżnij TypeError (network) od Error (API). Na TypeError pokaż: "Brak połączenia z serwerem. Sprawdź internet i spróbuj ponownie."
4. **Rozszerz translateRejectReason:** Dodaj więcej kodów z API:
- CUSTOMER_ALREADY_EXISTS → "Klient o tych danych już istnieje w systemie"
- INVALID_PESEL → "Nieprawidłowy numer PESEL"
- PRICE_LIST_EXPIRED → "Cennik wygasł. Odśwież formularz."
5. **Submit button recovery:** Upewnij się, że KAŻDY catch w createCustomerAndShowSummary i handleSummaryConfirm przywraca przyciski do stanu aktywnego.
Avoid: Nie zmieniaj endpointów API ani logiki proxy PHP. Retry tylko na 401/403, nie na inne kody.
</action>
<verify>
- Symuluj offline: w DevTools Network → Offline → submit → czytelny komunikat, przycisk aktywny
- Symuluj slow: Network → Slow 3G → submit → po 15s timeout message
- Sprawdź że po błędzie można ponownie wysłać formularz
</verify>
<done>AC-1, AC-2, AC-3 satisfied: token retry, timeout handling, network errors, rozszerzone komunikaty</done>
</task>
<task type="auto">
<name>Task 2: Animacje przejść + fix CSS bug</name>
<files>wp-content/plugins/carei-reservation/assets/css/carei-reservation.css, wp-content/plugins/carei-reservation/assets/js/carei-reservation.js</files>
<action>
1. **CSS transitions:** Dodaj klasy animacji:
```css
.carei-step-enter { opacity: 0; transform: translateY(10px); }
.carei-step-active { opacity: 1; transform: translateY(0); transition: opacity 0.3s ease, transform 0.3s ease; }
.carei-step-exit { opacity: 0; transform: translateY(-10px); transition: opacity 0.2s ease, transform 0.2s ease; }
```
2. **JS transitions:** W showSummaryOverlay(), handleSummaryBack(), showSuccessView():
- Dodaj klasę `carei-step-exit` do wychodzącego elementu
- Po 200ms (transitionend lub setTimeout): hide wychodzący, show wchodzący z `carei-step-enter`
- Po 1 frame (requestAnimationFrame): zamień `carei-step-enter` na `carei-step-active`
3. **Modal open animation:** Dodaj fade-in na overlay (.carei-modal-overlay) i scale na .carei-modal:
```css
.carei-modal-overlay { opacity: 0; transition: opacity 0.3s ease; }
.carei-modal-overlay.is-open { opacity: 1; }
.carei-modal { transform: scale(0.95); transition: transform 0.3s ease; }
.carei-modal-overlay.is-open .carei-modal { transform: scale(1); }
```
Zmień display:none/flex na visibility+opacity pattern (display:flex zawsze, visibility:hidden gdy nie is-open).
4. **Fix CSS bug:** Linie ~558-570 — orphaned styles po zamknięciu media query 768px. Przenieś `.carei-form__row--address`, `.carei-summary__actions`, `.carei-summary__btn` do wnętrza `@media (max-width: 768px)` i usuń dodatkowy `}`.
Avoid: Nie dodawaj żadnych bibliotek animacji. Czyste CSS transitions + JS class toggle.
</action>
<verify>
- Otwórz modal: smooth fade-in + scale
- Submit formularz: form fade out → summary fade in
- "Wróć": summary fade out → form fade in
- "Potwierdź": summary fade out → success fade in
- Sprawdź w DevTools że nie ma CSS parse errors
</verify>
<done>AC-4 satisfied: animacje przejść. AC-6 satisfied: CSS bug naprawiony.</done>
</task>
<task type="auto">
<name>Task 3: Accessibility — ARIA, focus trap, focus management</name>
<files>wp-content/plugins/carei-reservation/includes/class-elementor-widget.php, wp-content/plugins/carei-reservation/assets/js/carei-reservation.js</files>
<action>
1. **PHP widget — ARIA attrs:**
- Modal overlay div: dodaj `role="dialog"` `aria-modal="true"` `aria-labelledby="carei-modal-title"`
- Close button: dodaj `aria-label="Zamknij formularz"`
- Form sections: dodaj `role="group"` z `aria-label` na sekcjach (dane rezerwacji, dane osobowe, opcje)
- Submit button: dodaj `aria-busy="false"` (JS zmieni na true podczas loading)
- Summary confirm: dodaj `aria-busy="false"`
2. **JS — Focus management:**
- openModal(): po otwarciu, focus na pierwszy select (segmentSelect) lub na modal-title
- closeModal(): zapisz `lastFocusedElement` przed open, przywróć focus po close
- showSummaryOverlay(): focus na .carei-summary__title
- showSuccessView(): focus na .carei-success__title
- handleSummaryBack(): focus na segmentSelect
3. **JS — Focus trap:**
- W openModal(), dodaj keydown listener na modal:
- Zbierz wszystkie focusable elements w modalu
- Na Tab z ostatniego → focus na pierwszy
- Na Shift+Tab z pierwszego → focus na ostatni
- Usuń trap w closeModal()
4. **JS — aria-busy:**
- setSubmitState('loading'): dodaj aria-busy="true" na submit button
- setSubmitState('ready'): dodaj aria-busy="false"
- Analogicznie dla summaryConfirm
5. **Announcements:** Dodaj aria-live="polite" region (sr-only) do modalu. Announce:
- "Ładowanie podsumowania..." przy submit
- "Rezerwacja potwierdzona" przy success
- Błędy API
Avoid: Nie dodawaj aria-label do elementów które mają visible text. Nie rób z formularza role="form" (natywny <form> wystarczy).
</action>
<verify>
- Tab przez formularz: focus nie wychodzi poza modal
- Submit → focus na heading podsumowania
- Confirm → focus na heading success
- Close modal → focus wraca na trigger button
- Screen reader: ogłasza "Ładowanie...", "Rezerwacja potwierdzona", błędy
</verify>
<done>AC-5 satisfied: ARIA attrs, focus trap, focus management, aria-live announcements</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>Edge cases, animacje przejść, accessibility — pełny polish formularza</what-built>
<how-to-verify>
1. Otwórz https://carei.pagedev.pl
2. Kliknij "Złóż zapytanie o rezerwację" — modal fade-in smooth
3. Tab przez formularz — focus nie wychodzi poza modal
4. Wypełnij formularz i wyślij — form animuje się do summary
5. Kliknij "Wróć do formularza" — animacja powrotu
6. Wyślij ponownie → Potwierdź → success z animacją
7. Zamknij modal → focus wraca na przycisk trigger
8. Test offline: DevTools → Network → Offline → submit → komunikat błędu
9. Test mobile: responsive 375px — form działa, animacje smooth
</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/includes/class-softra-api.php (API proxy stable)
- wp-content/plugins/carei-reservation/includes/class-rest-proxy.php (REST endpoints stable)
- .env (credentials)
- Logika API endpoints (segments-branches-map, pricelist, customer, booking)
## SCOPE LIMITS
- Nie dodawaj nowych endpointów API
- Nie zmieniaj flow rezerwacji (customer → pricing → booking → confirm)
- Nie dodawaj bibliotek zewnętrznych
- Nie implementuj e2e test framework — weryfikacja manualna na produkcji
- Nie dodawaj ubezpieczenia ani wyjazdu zagranicznego (backlog)
</boundaries>
<verification>
Before declaring plan complete:
- [ ] Formularz działa end-to-end bez regresji
- [ ] Token retry: 401 → retry → success
- [ ] Timeout 15s: komunikat, przycisk aktywny
- [ ] Network error: komunikat, przycisk aktywny
- [ ] CAR_NOT_FOUND: czytelny komunikat PL
- [ ] Animacje: modal open, form→summary, summary→form, summary→success
- [ ] Focus: modal open → focus w modalu, close → focus na trigger
- [ ] Focus trap: Tab nie wychodzi poza modal
- [ ] ARIA: role=dialog, aria-modal, aria-busy, aria-live
- [ ] CSS: brak orphaned styles, media queries poprawne
- [ ] Mobile 375px: responsive OK
</verification>
<success_criteria>
- All 6 acceptance criteria met
- All verification checks pass
- No regressions in booking flow
- Human verification approved on carei.pagedev.pl
</success_criteria>
<output>
After completion, create `.paul/phases/04-polish-testing/04-01-SUMMARY.md`
</output>

View File

@@ -0,0 +1,126 @@
---
phase: 04-polish-testing
plan: 01
subsystem: ui, api
tags: [a11y, animations, error-handling, focus-trap, aria, css-transitions]
requires:
- phase: 03-form-submit-booking
provides: Complete booking flow end-to-end (customer → pricing → booking → confirm → success)
provides:
- Token retry on 401/403 with auto-recovery
- AbortController timeout 15s with Polish error messages
- Network error detection and user-friendly messaging
- Extended reject reason translations (6 codes)
- Animated step transitions (form ↔ summary ↔ success)
- Modal open/close animations (fade + scale)
- ARIA dialog with focus trap and focus management
- aria-live announcements for screen readers
- CSS bug fix (orphaned media query styles)
affects: [05-admin-panel]
tech-stack:
added: []
patterns: [AbortController timeout, CSS class-based step transitions, focus trap pattern, aria-live announcements]
key-files:
modified:
- 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-elementor-widget.php
key-decisions:
- "Inline style.display for step visibility instead of CSS-only classes — HTML inline display:none overrides classes"
- "Focus trap via keydown listener on overlay, filtering visible focusable elements"
- "aria-live region appended to modal overlay for scoped announcements"
patterns-established:
- "transitionStep(outEl, inEl, callback) pattern for animated view switching"
- "hideStep/showStep helpers using style.display for reliable visibility control"
- "announce() via aria-live polite region with 100ms delay for screen reader pickup"
duration: ~45min
started: 2026-03-25T14:00:00Z
completed: 2026-03-25T14:45:00Z
---
# Phase 4 Plan 01: Polish & Integration Testing Summary
**Edge cases (token retry, timeout, network errors), animated step transitions, ARIA dialog with focus trap — formularz gotowy produkcyjnie.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~45min |
| Tasks | 3 auto + 1 checkpoint |
| Files modified | 3 |
| Deviations | 1 (auto-fixed) |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Wygasły token JWT | Pass | 401/403 → auto-retry 1x, user nie widzi błędu |
| AC-2: Brak dostępności pojazdu | Pass | CAR_NOT_FOUND → czytelny komunikat PL z sugestią |
| AC-3: Timeouty i błędy sieciowe | Pass | AbortController 15s, TypeError → retry → komunikat PL |
| AC-4: Animacje przejść | Pass | fade+translate form↔summary↔success, modal scale |
| AC-5: Accessibility | Pass | role=dialog, aria-modal, focus trap, focus management, aria-live |
| AC-6: Fix CSS orphaned styles | Pass | Styles przeniesione do @media (max-width: 768px) |
## Accomplishments
- Pełny error recovery: token retry, timeout 15s, network errors — formularz nigdy nie blokuje się na stałe
- Animowane przejścia między krokami: smooth fade+translate (250ms) z callback focus management
- Focus trap w modalu: Tab nie wychodzi poza modal, Escape zamyka, focus wraca na trigger
- aria-live announcements: "Ładowanie podsumowania...", "Rezerwacja potwierdzona", błędy API
## Files Modified
| File | Change | Purpose |
|------|--------|---------|
| `assets/js/carei-reservation.js` | Modified | Token retry, timeout, network errors, animacje, focus trap, aria-live |
| `assets/css/carei-reservation.css` | Modified | Modal animations, step transitions, sr-only class, CSS bug fix |
| `includes/class-elementor-widget.php` | Modified | ARIA: role=dialog, aria-modal, aria-labelledby, aria-busy, tabindex=-1 |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| style.display zamiast CSS class dla visibility | HTML inline display:none nadpisuje klasy CSS | hideStep/showStep helpers operują na style.display |
| Focus na segment select po open | Pierwszy interaktywny element, najlogiczniejszy start | 350ms delay po animacji otwarcia |
| aria-live region w overlay | Scoped do modalu, nie zanieczyszcza strony | announce() z 100ms delay dla pickup przez screen readery |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 1 | Konieczna zmiana podejścia do visibility |
**Total impact:** Essential fix, no scope creep
### Auto-fixed Issues
**1. Inline display:none vs CSS class conflict**
- **Found during:** Checkpoint (user reported form title instead of summary)
- **Issue:** HTML elements miały `style="display:none;"` inline, a transitionStep używał klas CSS (`carei-step--hidden`) — inline style nadpisywał klasę
- **Fix:** Zmieniono na hideStep()/showStep() operujące na `el.style.display`
- **Verification:** User potwierdził poprawne działanie po fix
## Next Phase Readiness
**Ready:**
- Formularz produkcyjnie gotowy: error handling, animacje, a11y
- Booking flow end-to-end przetestowany na carei.pagedev.pl
**Concerns:**
- Penalty items filtrowane po kodzie (BRAK/BRUD/KARA) — może nie pokryć przyszłych kodów
- Brak email notification po rezerwacji (zależy od konfiguracji Softra)
**Blockers:** None
---
*Phase: 04-polish-testing, Plan: 01*
*Completed: 2026-03-25*