diff --git a/.paul/PROJECT.md b/.paul/PROJECT.md index 459a55a..4b7ef46 100644 --- a/.paul/PROJECT.md +++ b/.paul/PROJECT.md @@ -24,6 +24,30 @@ Plugin Elementor do rezerwacji samochodu na stronie carei.pagedev.pl, zintegrowa - Formularz NIE jest natywnym formularzem Elementor Pro — to custom widget - Brak dodatkowych zależności npm/composer — czysty PHP + JS +## Validated Requirements (Milestone v0.1) +- ✓ Elementor Widget z modalem rezerwacji — Phase 1-2 +- ✓ Integracja z Softra Rent API (auth, branch, carclass, pricelist, booking) — Phase 1, 3 +- ✓ Multi-step form: formularz → podsumowanie → sukces — Phase 2-3 +- ✓ Responsive modal (desktop overlay + mobile) — Phase 2 +- ✓ Error handling: token retry, timeout, network errors — Phase 4 +- ✓ Accessibility: ARIA dialog, focus trap, aria-live — Phase 4 +- ✓ Admin panel: CPT carei_reservation, lista, szczegóły, statusy — Phase 5 + +## Key Decisions +| Decision | Phase | Rationale | +|----------|-------|-----------| +| CPT + post_meta (nie custom table) | 5 | WordPress-native, prostsze dla MVP | +| Fire-and-forget save | 5 | Nie blokuj response — rezerwacja już w Softra | +| Meta-based status (nie taxonomy) | 5 | Prosty 3-wartościowy enum | +| Token retry on 401/403 | 4 | Automatyczny re-auth bez interwencji usera | +| Inline display:none for steps | 4 | CSS class conflict resolution | + +## Out of Scope (backlog) +- Ubezpieczenie (pakiet Soft/Premium) — czeka na API Softra +- Wyjazd zagraniczny (lista krajów + ceny) — czeka na API Softra +- Eksport CSV/PDF rezerwacji +- Email notyfikacje + ## API Endpoints (kluczowe) | Endpoint | Metoda | Użycie | |----------|--------|--------| diff --git a/.paul/ROADMAP.md b/.paul/ROADMAP.md index 51a9e75..18aa4d5 100644 --- a/.paul/ROADMAP.md +++ b/.paul/ROADMAP.md @@ -7,11 +7,18 @@ ### Phase 1: Plugin Skeleton + API Proxy ✅ Complete Utworzenie pluginu WordPress z proxy REST API do Softra Rent. Backend obsługujący autoryzację JWT, pobieranie oddziałów, klas pojazdów, cenników i dodatków. Rejestracja widgetu Elementor. -### Phase 2: Form UI — Krok 1 (Formularz) ⬜ Not started -Frontend formularza rezerwacji zgodny z Figmą: modal/bottom-sheet, pola (segment, daty, lokalizacja, opcje dodatkowe, dane osobowe, wiadomość, zgoda RODO). Dynamiczne ładowanie danych z API (oddziały, klasy). Responsywny desktop + mobile. +### Phase 2: Form UI — Krok 1 (Formularz) ✅ Complete +Frontend formularza rezerwacji w modalu Elementor. Dynamiczne segmenty (ze wszystkich lokalizacji), oddziały filtrowane po segmencie, opcje dodatkowe z cennika API, walidacja, responsive. Pominięto ubezpieczenie i wyjazd zagraniczny (brak w API — backlog). -### Phase 3: Form UI — Krok 2 (Overlay / Podsumowanie) ⬜ Not started -Rozwinięcie formularza po wypełnieniu kroku 1: podsumowanie kosztów (pricing summary z API), tworzenie klienta, złożenie rezerwacji, potwierdzenie. Obsługa błędów i stanów ładowania. +### Phase 3: Submit + Booking Flow ✅ Complete +Pełny flow: formularz → customer/add → pricing summary overlay → makebooking → confirm → success z numerem. Pola adresowe + PESEL. Error handling. Fixy: boolean API params, timeout mapowania, drivers[] required. -### Phase 4: Polish & Integration Testing ⬜ Not started -Testy end-to-end na carei.pagedev.pl, walidacja formularza, obsługa edge cases (brak dostępności, timeout tokenu, błędy API), animacje przejść między krokami, a11y. +### Phase 4: Polish & Integration Testing ✅ Complete +Edge cases (token retry, timeout, network errors), animacje przejść form↔summary↔success, accessibility (ARIA dialog, focus trap, focus management, aria-live), CSS fix. + +### Phase 5: Admin Panel — Historia Formularzy ✅ Complete +CPT `carei_reservation` z automatycznym zapisem po booking, lista z kolumnami i filtrem statusu, meta box szczegółów, system statusów nowe/przeczytane/zrealizowane, auto-mark-read. + +### Backlog (do realizacji gdy API będzie gotowe) +- **Wyjazd zagraniczny:** Checkbox "Planuję trasę poza granicę Polski" + wyszukiwarka krajów z cenami. Wymaga endpointu listy krajów i cennika per kraj w API Softra. +- **Ubezpieczenie:** Sekcja "Pakiet ochrony Soft/Premium" jak na Figmie. Wymaga dedykowanych pozycji ubezpieczeniowych w API pricelist. diff --git a/.paul/STATE.md b/.paul/STATE.md index aaf4573..4d65271 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -2,33 +2,35 @@ ## Current Position -Milestone: v0.1 Formularz Rezerwacji MVP -Phase: 2 of 4 (Form UI — Krok 1) — APPLY in progress -Plan: 02-01 tasks 1+2 done, task 3 (checkpoint:human-verify) pending -Status: Awaiting deploy + visual test on carei.pagedev.pl -Last activity: 2026-03-25 — Fixed API (native cURL), added car-classes-all endpoint, built full form UI +Milestone: v0.1 Formularz Rezerwacji MVP — COMPLETE +Phase: 5 of 5 (Admin Panel — Historia Formularzy) — Complete +Plan: 05-01 — Complete +Status: Milestone v0.1 complete — all 5 phases finished +Last activity: 2026-03-25 — Phase 5 unified, milestone complete Progress: -- Milestone: [██░░░░░░░░] 25% +- Milestone: [██████████] 100% ✅ - Phase 1: [██████████] 100% ✅ -- Phase 2: [██████░░░░] 66% (2/3 tasks, checkpoint pending) +- Phase 2: [██████████] 100% ✅ +- Phase 3: [██████████] 100% ✅ +- Phase 4: [██████████] 100% ✅ +- Phase 5: [██████████] 100% ✅ ## Loop Position Current loop state: ``` PLAN ──▶ APPLY ──▶ UNIFY - ✓ ◐ ○ [Apply in progress — checkpoint waiting] + ✓ ✓ ✓ [Loop complete — milestone v0.1 finished] ``` ## Session Continuity Last session: 2026-03-25 -Stopped at: Phase 2 Plan 02-01 — Tasks 1+2 complete, Task 3 checkpoint:human-verify pending -Next action: Deploy files to server, test form visually, then "approved" or describe issues -Resume file: .paul/HANDOFF-2026-03-25.md +Stopped at: Milestone v0.1 complete — all phases unified +Next action: /paul:complete-milestone or start next milestone +Resume file: .paul/ROADMAP.md Resume context: -- 5 plugin files need deploying (API, proxy, widget, CSS, JS) -- Key fix: native cURL instead of wp_remote_post -- Key fix: car-classes-all endpoint loads segments without requiring branch -- After approved → /paul:unify → /paul:plan Phase 3 +- All 5 phases complete: skeleton, form UI, booking flow, polish, admin panel +- Plugin fully functional: formularz → API Softra → admin panel +- Backlog: ubezpieczenie + wyjazd zagraniczny (czeka na API) diff --git a/.paul/handoffs/archive/HANDOFF-2026-03-25-phase3.md b/.paul/handoffs/archive/HANDOFF-2026-03-25-phase3.md new file mode 100644 index 0000000..14e73cf --- /dev/null +++ b/.paul/handoffs/archive/HANDOFF-2026-03-25-phase3.md @@ -0,0 +1,108 @@ +# PAUL Handoff + +**Date:** 2026-03-25 +**Status:** paused — Phase 3 complete, Phase 4 not started + +--- + +## READ THIS FIRST + +You have no prior context. This document tells you everything. + +**Project:** Carei — Formularz rezerwacji samochodu jako plugin Elementor, zintegrowany z Softra Rent API +**Core value:** Klient klika "Złóż zapytanie o rezerwację" → modal z formularzem → dane z API Softra → rezerwacja + +--- + +## Current State + +**Milestone:** v0.1 Formularz Rezerwacji MVP +**Phase:** 3 of 5 — Complete ✅ +**Plan:** 03-01 — Complete + +**Loop Position:** +``` +PLAN ──▶ APPLY ──▶ UNIFY + ✓ ✓ ✓ [Loop complete — ready for next PLAN] +``` + +--- + +## What Was Done + +**This session:** +- Refactored form layout: segment+dates in one row, pickup+same-return checkbox in one row +- New endpoint `/segments-branches-map` (simplified: 2 API calls, all branches for all segments) +- Segment→branch filtering in pickup select (all branches shown, API verifies at booking) +- Extras hidden until segment AND pickup selected +- Investigated API for insurance (none) and foreign travel (none) → deferred to backlog +- Added Phase 5 to roadmap: Admin Panel historia formularzy +- **Phase 3 complete:** full booking flow end-to-end + - Added address fields (city, zipCode, street) + PESEL + - Pricing summary overlay with cost breakdown table + - Success view with reservation number + - customer/add → princingSummary → makebooking → confirm + - Error handling at each step + +**API discoveries (critical for future work):** +- Boolean params: must send `true`/`false`, NOT `'T'`/`'N'` (Java deserialization) +- `drivers[]` is required in makebooking (not documented as required) +- Penalty items in pricelist (BRAK/BRUD/KARA codes) filtered from display +- No insurance or foreign travel endpoints in current API version + +--- + +## What's Next + +**Immediate:** `/paul:plan` for Phase 4 — Polish & Integration Testing +- Edge cases (brak dostępności, timeout tokenu, błędy API) +- Animacje przejść form↔summary↔success +- Accessibility +- End-to-end testing on carei.pagedev.pl + +**After that:** +- Phase 5: Admin Panel — zapisywanie formularzy w WP, przeglądanie w wp-admin + +**Backlog (czeka na nową wersję API):** +- Ubezpieczenie (Pakiet Soft/Premium) +- Wyjazd zagraniczny (checkbox + wyszukiwarka krajów) + +--- + +## Key Decisions Made + +| Decision | Rationale | +|----------|-----------| +| Boolean API params (true/false) | Java backend rejects 'T'/'N' strings | +| Simplified branch mapping (no per-branch filtering) | 39 branches × API call = timeout; all branches shown, API verifies at booking | +| drivers[] = najemca | API requires NotNull, form collects same data | +| skipAccountCreate: true | MVP — no customer accounts needed | +| Penalty items filtered by code prefix | BRAK/BRUD/KARA not customer-facing options | +| Ubezpieczenie + wyjazd zagraniczny → backlog | API has no dedicated endpoints | + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `.paul/STATE.md` | Live project state | +| `.paul/ROADMAP.md` | Phase overview (1-3 ✅, 4-5 ⬜, backlog) | +| `.paul/phases/03-form-submit-booking/03-01-SUMMARY.md` | Phase 3 summary | +| `wp-content/plugins/carei-reservation/` | Plugin directory (all code) | +| `docs/rent-api-02-klienci-i-konta.md` | Customer API docs | +| `docs/rent-api-03-rezerwacje-i-platnosci.md` | Booking API docs | +| `softra-test.php` | API test reference | +| `.env` | API credentials | + +--- + +## Resume Instructions + +1. Read `.paul/STATE.md` for latest position +2. Phase 3 complete — ready for Phase 4 planning +3. Run `/paul:resume` or `/paul:plan` + +--- + +*Handoff created: 2026-03-25* diff --git a/.paul/handoffs/archive/HANDOFF-2026-03-25-phase5.md b/.paul/handoffs/archive/HANDOFF-2026-03-25-phase5.md new file mode 100644 index 0000000..a6ca4ba --- /dev/null +++ b/.paul/handoffs/archive/HANDOFF-2026-03-25-phase5.md @@ -0,0 +1,122 @@ +# PAUL Handoff + +**Date:** 2026-03-25 +**Status:** paused — Phase 5 APPLY in progress, checkpoint pending + +--- + +## READ THIS FIRST + +You have no prior context. This document tells you everything. + +**Project:** Carei — Formularz rezerwacji samochodu jako plugin Elementor, zintegrowany z Softra Rent API +**Core value:** Klient klika "Złóż zapytanie o rezerwację" → modal z formularzem → dane z API Softra → rezerwacja + +--- + +## Current State + +**Milestone:** v0.1 Formularz Rezerwacji MVP +**Phase:** 5 of 5 — Admin Panel — Historia Formularzy +**Plan:** 05-01 — APPLY in progress (2/3 tasks done, checkpoint pending) + +**Loop Position:** +``` +PLAN ──▶ APPLY ──▶ UNIFY + ✓ ◐ ○ [APPLY in progress — checkpoint pending] +``` + +--- + +## What Was Done + +**This session (Phase 4 + Phase 5):** + +### Phase 4: Polish & Integration Testing ✅ COMPLETE +- Token retry on 401/403 (auto-retry 1x) +- AbortController timeout 15s + Polish error messages +- Network error detection (TypeError → retry → user message) +- Extended reject reason translations (6 codes) +- Animated step transitions: form ↔ summary ↔ success (fade+translate 250ms) +- 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) +- Fix: inline display:none vs CSS class conflict (auto-fixed during checkpoint) + +### Phase 5: Admin Panel (in progress) +- Created `includes/class-admin-panel.php` — full admin panel: + - CPT `carei_reservation` registered (menu "Rezerwacje", dashicons-car) + - Admin columns: Nr rezerwacji, Klient, Segment, Daty, Oddział, Status, Data + - Status filter dropdown above list + - Meta box "Szczegóły rezerwacji" with full data table + - Status dropdown (nowe/przeczytane/zrealizowane) with colored badges + - Auto-mark-read on post edit open + - Inline admin CSS for badges and meta table + - Static `save_reservation()` method for fire-and-forget save +- Modified `class-rest-proxy.php` — `make_booking()` calls `save_reservation()` on success +- Modified `carei-reservation.php` — includes + initializes `Carei_Admin_Panel` +- All PHP syntax verified clean + +--- + +## What's In Progress + +- **Phase 5 Plan 05-01 checkpoint:** Human verification in wp-admin pending + - All code written, PHP syntax OK + - Need user to test: CPT in menu, submit reservation, check admin list, meta box, status changes + +--- + +## What's Next + +**Immediate:** Resume Phase 5 checkpoint verification +1. User tests admin panel on carei.pagedev.pl +2. If approved → `/paul:unify .paul/phases/05-admin-panel/05-01-PLAN.md` +3. Unify closes Phase 5 → Milestone v0.1 complete! + +**After milestone:** +- Backlog: ubezpieczenie + wyjazd zagraniczny (czeka na nową wersję API Softra) + +--- + +## Key Decisions Made + +| Decision | Rationale | +|----------|-----------| +| CPT + post_meta (not custom table) | WordPress-native, simpler for MVP, full admin UI for free | +| Fire-and-forget save | Never block user response — rezerwacja already in Softra | +| Meta-based status (not taxonomy) | Simple 3-value enum, no UI overhead | +| Auto-mark-read on edit | Reduce manual clicks, natural workflow | +| Hidden title input | Title auto-generated, not user-editable | +| style.display for step transitions | Inline display:none overrides CSS classes | + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `.paul/STATE.md` | Live project state | +| `.paul/ROADMAP.md` | Phase overview (1-4 ✅, 5 in progress) | +| `.paul/phases/05-admin-panel/05-01-PLAN.md` | Phase 5 plan (checkpoint pending) | +| `.paul/phases/04-polish-testing/04-01-SUMMARY.md` | Phase 4 summary | +| `wp-content/plugins/carei-reservation/` | Plugin directory (all code) | +| `includes/class-admin-panel.php` | **NEW** — Admin panel CPT + columns + meta box | +| `includes/class-rest-proxy.php` | Modified — save_reservation() call | +| `includes/class-elementor-widget.php` | ARIA attrs (Phase 4) | +| `assets/js/carei-reservation.js` | Edge cases + animations + a11y (Phase 4) | +| `assets/css/carei-reservation.css` | Transitions + CSS fix (Phase 4) | + +--- + +## Resume Instructions + +1. Read `.paul/STATE.md` for latest position +2. Phase 5 APPLY in progress — checkpoint pending +3. Run `/paul:resume` — will route to checkpoint verification +4. After approved → `/paul:unify` → milestone complete + +--- + +*Handoff created: 2026-03-25* diff --git a/.paul/handoffs/archive/HANDOFF-2026-03-25.md b/.paul/handoffs/archive/HANDOFF-2026-03-25.md new file mode 100644 index 0000000..fbbc646 --- /dev/null +++ b/.paul/handoffs/archive/HANDOFF-2026-03-25.md @@ -0,0 +1,112 @@ +# PAUL Handoff + +**Date:** 2026-03-25 +**Status:** paused — checkpoint human-verify pending (awaiting deploy + test) + +--- + +## READ THIS FIRST + +You have no prior context. This document tells you everything. + +**Project:** Carei — Formularz rezerwacji samochodu jako plugin Elementor, zintegrowany z Softra Rent API +**Core value:** Klient klika "Złóż zapytanie o rezerwację" → modal z formularzem → dane z API Softra → rezerwacja + +--- + +## Current State + +**Milestone:** v0.1 Formularz Rezerwacji MVP +**Phase:** 2 of 4 — Form UI Krok 1 (Formularz) +**Plan:** 02-01 — APPLY in progress (Tasks 1+2 done, Task 3 checkpoint pending) + +**Loop Position:** +``` +PLAN ──▶ APPLY ──▶ UNIFY + ✓ ◐ ○ [Apply in progress — checkpoint:human-verify waiting] +``` + +--- + +## What Was Done + +**Phase 1 (complete):** +- Plugin `carei-reservation` z natywnym cURL proxy do Softra Rent API +- 10 WP REST endpoints w namespace `carei/v1` +- Widget Elementor z przyciskiem CTA + modal overlay +- Fix: widget require_once przeniesiony do hooka elementor/widgets/register +- Fix: zamiana wp_remote_post na natywny cURL (matching softra-test.php) + +**Phase 2 (in progress — Tasks 1+2 done):** +- HTML formularza z wszystkimi sekcjami z Figmy (widget render) +- CSS 541 linii (responsive, custom checkboxy, karty opcji, walidacja) +- JS 580+ linii (API integration, walidacja, interakcje) +- Nowy endpoint GET /car-classes-all (listAll) — segmenty ładują się od razu bez wymagania oddziału +- Opcje dodatkowe ładowane dynamicznie z cennika API + +--- + +## What's In Progress + +- **Task 3 checkpoint:human-verify** — użytkownik musi wgrać pliki na serwer i przetestować wizualnie +- Pliki do wgrania: + - `includes/class-softra-api.php` (natywny cURL + get_all_car_classes) + - `includes/class-rest-proxy.php` (nowy endpoint car-classes-all) + - `includes/class-elementor-widget.php` (pełny HTML formularza) + - `assets/css/carei-reservation.css` (kompletne style) + - `assets/js/carei-reservation.js` (logika + API) + +--- + +## What's Next + +**Immediate:** Wgrać pliki na serwer → przetestować formularz → "approved" lub opisać problemy + +**After that:** +- Jeśli approved → /paul:unify → Phase 3 (Overlay/podsumowanie + submit rezerwacji do API) +- Jeśli issues → fix → re-verify + +**Remaining phases:** +- Phase 3: Form UI Krok 2 (Overlay z podsumowaniem, tworzenie klienta, booking) +- Phase 4: Polish & integration testing + +--- + +## Key Decisions Made + +| Decision | Rationale | +|----------|-----------| +| Natywny cURL zamiast wp_remote_post | softra-test.php działa z cURL, WP HTTP API timeout | +| GET /car/class/listAll dla segmentów | Segment jest pierwszym polem w Figmie, nie powinien wymagać oddziału | +| Osobny plugin carei-reservation | Czystsza separacja od elementor-addon | +| sslverify nie ustawiane (domyślne cURL) | Tak jak w softra-test.php który działa na produkcji | + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `.paul/STATE.md` | Live project state | +| `.paul/ROADMAP.md` | Phase overview (Phase 1 ✅, Phase 2-4 ⬜) | +| `.paul/phases/02-form-ui-step1/02-01-PLAN.md` | Current plan | +| `.paul/phases/01-reservation-form-plugin/01-01-SUMMARY.md` | Phase 1 summary | +| `wp-content/plugins/carei-reservation/` | Plugin directory (all code) | +| `docs/figma-formularz/README.md` | Figma design spec | +| `docs/figma-formularz/screenshot-desktop.png` | Desktop reference | +| `docs/figma-formularz/screenshot-mobile.png` | Mobile reference | +| `softra-test.php` | Working Softra API test (reference for cURL config) | +| `.env` | API credentials (url, username, password) | + +--- + +## Resume Instructions + +1. Read `.paul/STATE.md` for latest position +2. User needs to deploy files to server and test +3. After test: "approved" → run `/paul:unify` then `/paul:plan` for Phase 3 +4. Or run `/paul:resume` or `/paul:progress` + +--- + +*Handoff created: 2026-03-25* diff --git a/.paul/phases/05-admin-panel/05-01-PLAN.md b/.paul/phases/05-admin-panel/05-01-PLAN.md new file mode 100644 index 0000000..86c21b8 --- /dev/null +++ b/.paul/phases/05-admin-panel/05-01-PLAN.md @@ -0,0 +1,274 @@ +--- +phase: 05-admin-panel +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - wp-content/plugins/carei-reservation/includes/class-admin-panel.php + - wp-content/plugins/carei-reservation/includes/class-rest-proxy.php + - wp-content/plugins/carei-reservation/carei-reservation.php +autonomous: false +--- + + +## Goal +Admin panel w wp-admin do przeglądania rezerwacji: Custom Post Type `carei_reservation`, automatyczny zapis przy udanej rezerwacji, lista z kolumnami i filtrami, podgląd szczegółów, statusy (nowe/przeczytane/zrealizowane). + +## Purpose +Właściciel firmy musi mieć przegląd wszystkich rezerwacji złożonych przez formularz — bez logowania do panelu Softra. Dane zapisane w WordPress = szybki podgląd, filtrowanie, zmiana statusu. + +## Output +- Nowy plik: `includes/class-admin-panel.php` (CPT, kolumny, meta box, statusy) +- Zmodyfikowany: `includes/class-rest-proxy.php` (zapis rezerwacji po sukcesie) +- Zmodyfikowany: `carei-reservation.php` (include nowej klasy) + + + +## Project Context +@.paul/PROJECT.md +@.paul/ROADMAP.md +@.paul/STATE.md + +## Prior Work +@.paul/phases/03-form-submit-booking/03-01-SUMMARY.md +- Booking flow: customer/add → pricingSummary → makebooking → confirm +- Dane zbierane: segment, daty, oddział, extras, imię, nazwisko, adres, email, telefon, PESEL, wiadomość +- Po sukcesie: reservationId + reservationNo z API + +## Source Files +@wp-content/plugins/carei-reservation/carei-reservation.php +@wp-content/plugins/carei-reservation/includes/class-rest-proxy.php + + + + +## AC-1: CPT carei_reservation zarejestrowany +```gherkin +Given plugin jest aktywny +When otworzę wp-admin +Then w menu bocznym widzę "Rezerwacje" z ikoną dashicons-car + And CPT nie jest publiczny (brak na frontendzie) + And supports: title (auto-generowany, nie edytowalny) +``` + +## AC-2: Rezerwacja zapisuje się automatycznie po sukcesie +```gherkin +Given użytkownik wypełnił formularz i potwierdził rezerwację +When API makebooking + confirm zwraca sukces +Then w wp-admin pojawia się nowy wpis z danymi rezerwacji + And tytuł: "Rezerwacja #[reservationNo] — [Imię Nazwisko]" + And wszystkie dane formularza zapisane w post_meta + And status: "nowe" +``` + +## AC-3: Lista rezerwacji z kolumnami +```gherkin +Given jestem w wp-admin → Rezerwacje +When widzę listę wpisów +Then kolumny: Nr rezerwacji | Klient | Segment | Daty | Oddział | Status | Data + And kolumna Status pokazuje kolorowy badge (nowe=niebieski, przeczytane=żółty, zrealizowane=zielony) + And mogę sortować po dacie + And mogę filtrować po statusie (dropdown nad listą) +``` + +## AC-4: Podgląd szczegółów rezerwacji +```gherkin +Given kliknę na rezerwację w liście +When otwiera się edycja wpisu +Then widzę meta box "Szczegóły rezerwacji" z tabelą: + - Nr rezerwacji, ID klienta Softra + - Segment, daty od-do, oddział odbioru, oddział zwrotu + - Imię, nazwisko, email, telefon, PESEL, adres + - Wybrane opcje dodatkowe + - Wiadomość klienta + And widzę dropdown do zmiany statusu (nowe → przeczytane → zrealizowane) + And status zmienia się po kliknięciu "Zaktualizuj" +``` + +## AC-5: Przy otwarciu rezerwacji status zmienia się na "przeczytane" +```gherkin +Given rezerwacja ma status "nowe" +When otwieram ją w wp-admin +Then status automatycznie zmienia się na "przeczytane" + And badge w liście aktualizuje się +``` + + + + + + + Task 1: CPT registration + admin panel class + wp-content/plugins/carei-reservation/includes/class-admin-panel.php, wp-content/plugins/carei-reservation/carei-reservation.php + + 1. **Utwórz `includes/class-admin-panel.php`:** + + Klasa `Carei_Admin_Panel` z metodami: + + a) `register_post_type()` — hook na `init`: + - CPT: `carei_reservation` + - label: "Rezerwacje" / "Rezerwacja" + - menu_icon: `dashicons-car` + - public: false, show_ui: true, show_in_menu: true + - supports: `['title']` (title auto-generowany) + - capability_type: 'post' + - menu_position: 26 (pod Komentarze) + + b) `register_statuses()` — custom taxonomy NIE jest potrzebna, użyj post_meta `_carei_status` z wartościami: `nowe`, `przeczytane`, `zrealizowane` + + c) `add_admin_columns($columns)` — hook `manage_carei_reservation_posts_columns`: + - Kolumny: cb, reservation_no, client, segment, dates, branch, status, date + - Usuń domyślny title + + d) `render_admin_column($column, $post_id)` — hook `manage_carei_reservation_posts_custom_column`: + - reservation_no: get_post_meta `_carei_reservation_no` + - client: `_carei_first_name` + `_carei_last_name` + - segment: `_carei_segment` + - dates: `_carei_date_from` + `_carei_date_to` (format: d.m.Y H:i) + - branch: `_carei_pickup_branch` + - status: badge z kolorem (nowe=#2F2482, przeczytane=#f59e0b, zrealizowane=#22c55e) + + e) `add_status_filter()` — hook `restrict_manage_posts`: + - Dropdown filtra statusu nad listą + - Opcje: Wszystkie, Nowe, Przeczytane, Zrealizowane + + f) `filter_by_status($query)` — hook `pre_get_posts`: + - Filtruj po `_carei_status` meta_query + + g) `add_meta_box()` — hook `add_meta_boxes`: + - Meta box "Szczegóły rezerwacji" w edycji wpisu + - Tabela z wszystkimi danymi + - Dropdown statusu z save + + h) `save_meta_box($post_id)` — hook `save_post_carei_reservation`: + - Zapisz wybrany status + + i) `auto_mark_read($post_id)` — hook `edit_form_after_title` lub `admin_head`: + - Jeśli status == 'nowe' i jesteśmy na ekranie edycji → zmień na 'przeczytane' + + j) **Inline CSS** dla badge'ów statusu i meta box — dodaj przez `admin_head` hook, scoped do CPT screen + + 2. **Zmodyfikuj `carei-reservation.php`:** + - Dodaj `require_once` dla nowej klasy + - Zainicjalizuj `new Carei_Admin_Panel()` w `plugins_loaded` + + Avoid: Nie twórz oddzielnych template files. Wszystko w jednej klasie. Nie dodawaj custom taxonomy — meta jest wystarczające. + + + - Otwórz wp-admin → "Rezerwacje" widoczne w menu + - Lista pusta, kolumny widoczne + - Filtr statusu nad listą + + AC-1, AC-3, AC-4, AC-5 satisfied: CPT zarejestrowany, kolumny, meta box, auto-read + + + + Task 2: Zapis rezerwacji po sukcesie booking + wp-content/plugins/carei-reservation/includes/class-rest-proxy.php + + 1. **W `class-rest-proxy.php`**, w callback dla `/booking` endpoint: + - Po udanym `make_booking()` i `confirm_booking()` (jeśli confirm jest w tym samym request) LUB: + - Lepiej: dodaj nowy statyczny helper `Carei_Admin_Panel::save_reservation($data, $result)` + + 2. **W callback `/booking`:** + - Po `$result = $api->make_booking($body)` z sukcesem: + - Wywołaj `Carei_Admin_Panel::save_reservation($body, $result)` + + 3. **Metoda `save_reservation($booking_data, $api_result)`:** + - `wp_insert_post()` z: + - post_type: `carei_reservation` + - post_title: `Rezerwacja #{reservationNo} — {firstName} {lastName}` + - post_status: `publish` + - `update_post_meta()` dla: + - `_carei_reservation_no` ← reservationNo + - `_carei_reservation_id` ← reservationId + - `_carei_customer_id` ← customerId + - `_carei_segment` ← carParameters.categoryName + - `_carei_date_from` ← dateFrom + - `_carei_date_to` ← dateTo + - `_carei_pickup_branch` ← pickUpLocation.branchName + - `_carei_return_branch` ← returnLocation.branchName + - `_carei_first_name` ← drivers[0].firstName (lub z booking_data) + - `_carei_last_name` ← drivers[0].lastName + - `_carei_email` ← drivers[0].email + - `_carei_phone` ← drivers[0].phone + - `_carei_pesel` ← drivers[0].pesel + - `_carei_address` ← JSON encode drivers[0].address + - `_carei_extras` ← JSON encode priceItems + - `_carei_comments` ← comments + - `_carei_status` ← 'nowe' + - `_carei_raw_response` ← JSON encode api_result (backup) + + 4. **Error handling:** Jeśli wp_insert_post() fail, loguj do error_log ale NIE blokuj response do usera (rezerwacja już w Softra jest OK). + + Avoid: Nie modyfikuj flow rezerwacji. Zapis jest fire-and-forget — nigdy nie blokuje odpowiedzi do klienta. + + + - Złóż testową rezerwację przez formularz + - Sprawdź wp-admin → Rezerwacje → nowy wpis z danymi + - Kliknij wpis → meta box ze szczegółami + + AC-2 satisfied: rezerwacja zapisuje się automatycznie po sukcesie + + + + Admin panel rezerwacji w wp-admin: CPT, kolumny, filtr statusu, meta box szczegółów, auto-zapis po rezerwacji, auto-mark-read + + 1. Otwórz wp-admin na carei.pagedev.pl + 2. W menu bocznym: "Rezerwacje" z ikoną samochodu + 3. Złóż rezerwację przez formularz na frontendzie + 4. Wróć do wp-admin → Rezerwacje → nowy wpis ze statusem "nowe" (niebieski badge) + 5. Kolumny: Nr rezerwacji, Klient, Segment, Daty, Oddział, Status, Data + 6. Filtr statusu nad listą (dropdown) + 7. Kliknij wpis → meta box "Szczegóły rezerwacji" z pełnymi danymi + 8. Status zmienił się na "przeczytane" (żółty) + 9. Zmień status na "zrealizowane" → Zaktualizuj → zielony badge + + Type "approved" to continue, or describe issues to fix + + + + + + +## DO NOT CHANGE +- wp-content/plugins/carei-reservation/assets/js/carei-reservation.js (Phase 4 stable) +- wp-content/plugins/carei-reservation/assets/css/carei-reservation.css (Phase 4 stable) +- wp-content/plugins/carei-reservation/includes/class-elementor-widget.php (Phase 4 stable) +- wp-content/plugins/carei-reservation/includes/class-softra-api.php (API proxy stable) +- .env (credentials) + +## SCOPE LIMITS +- Nie dodawaj eksportu CSV/PDF (przyszły feature) +- Nie dodawaj email notyfikacji (zależy od Softra) +- Nie modyfikuj frontend formularza +- Nie dodawaj REST API endpoints dla admin panelu (nie potrzebne — natywny WP admin) +- Nie twórz custom database table — CPT + post_meta wystarczy dla MVP + + + + +Before declaring plan complete: +- [ ] CPT `carei_reservation` zarejestrowany w wp-admin +- [ ] Menu "Rezerwacje" widoczne z ikoną +- [ ] Rezerwacja zapisuje się po sukcesie booking +- [ ] Lista: kolumny Nr, Klient, Segment, Daty, Oddział, Status, Data +- [ ] Filtr statusu działa +- [ ] Meta box ze wszystkimi danymi +- [ ] Auto-mark-read przy otwarciu +- [ ] Zmiana statusu przez dropdown + save +- [ ] Badge kolory: nowe=niebieski, przeczytane=żółty, zrealizowane=zielony +- [ ] Brak regresji w booking flow na frontendzie + + + +- All 5 acceptance criteria met +- All verification checks pass +- No regressions in frontend booking flow +- Human verification approved on carei.pagedev.pl + + + +After completion, create `.paul/phases/05-admin-panel/05-01-SUMMARY.md` + diff --git a/.paul/phases/05-admin-panel/05-01-SUMMARY.md b/.paul/phases/05-admin-panel/05-01-SUMMARY.md new file mode 100644 index 0000000..fcc0dff --- /dev/null +++ b/.paul/phases/05-admin-panel/05-01-SUMMARY.md @@ -0,0 +1,129 @@ +--- +phase: 05-admin-panel +plan: 01 +subsystem: admin +tags: [wordpress, cpt, post-meta, admin-panel] + +requires: + - phase: 03-form-submit-booking + provides: booking flow z reservationId/reservationNo w API response +provides: + - Admin panel CPT carei_reservation w wp-admin + - Automatyczny zapis rezerwacji po sukcesie booking + - Lista z kolumnami, filtr statusu, meta box szczegółów + - System statusów nowe/przeczytane/zrealizowane +affects: [] + +tech-stack: + added: [] + patterns: [CPT + post_meta for data storage, fire-and-forget save, static helper method pattern] + +key-files: + created: [includes/class-admin-panel.php] + modified: [includes/class-rest-proxy.php, carei-reservation.php] + +key-decisions: + - "CPT + post_meta zamiast custom table — WordPress-native, prostsze dla MVP" + - "Fire-and-forget save — nigdy nie blokuje response do usera" + - "Meta-based status zamiast taxonomy — prosty 3-wartościowy enum" + - "Auto-mark-read on edit — naturalny workflow bez dodatkowych kliknięć" + +patterns-established: + - "Static save helper: Carei_Admin_Panel::save_reservation() wywoływany z REST proxy" + - "Inline admin CSS scoped do CPT screen" + +duration: ~2h +started: 2026-03-25 +completed: 2026-03-25 +--- + +# Phase 5 Plan 01: Admin Panel — Historia Formularzy Summary + +**CPT carei_reservation z automatycznym zapisem rezerwacji, listą z kolumnami/filtrami, meta boxem szczegółów i systemem statusów nowe/przeczytane/zrealizowane.** + +## Performance + +| Metric | Value | +|--------|-------| +| Duration | ~2h | +| Started | 2026-03-25 | +| Completed | 2026-03-25 | +| Tasks | 3 completed (2 auto + 1 human-verify) | +| Files modified | 3 | + +## Acceptance Criteria Results + +| Criterion | Status | Notes | +|-----------|--------|-------| +| AC-1: CPT carei_reservation zarejestrowany | Pass | Menu "Rezerwacje" z dashicons-car, public: false, show_ui: true | +| AC-2: Rezerwacja zapisuje się automatycznie | Pass | save_reservation() wywoływane po make_booking success | +| AC-3: Lista z kolumnami | Pass | Nr, Klient, Segment, Daty, Oddział, Status, Data + filtr statusu | +| AC-4: Podgląd szczegółów | Pass | Meta box z pełną tabelą danych + dropdown statusu | +| AC-5: Auto-mark-read | Pass | Status "nowe" → "przeczytane" przy otwarciu edycji | + +## Accomplishments + +- CPT `carei_reservation` z pełnym admin UI: kolumny, filtr, meta box, statusy z kolorowymi badge'ami +- Fire-and-forget zapis rezerwacji po sukcesie booking (nie blokuje response) +- Klikalny nr rezerwacji w liście (link do edycji) + +## Files Created/Modified + +| File | Change | Purpose | +|------|--------|---------| +| `includes/class-admin-panel.php` | Created | CPT registration, admin columns, meta box, status system, auto-mark-read, save helper | +| `includes/class-rest-proxy.php` | Modified | Wywołanie save_reservation() po udanym make_booking | +| `carei-reservation.php` | Modified | Include + inicjalizacja Carei_Admin_Panel | + +## Decisions Made + +| Decision | Rationale | Impact | +|----------|-----------|--------| +| CPT + post_meta (nie custom table) | WordPress-native, pełne admin UI za darmo | Prostsze dla MVP, ewentualna migracja jeśli skala wzrośnie | +| Fire-and-forget save | Rezerwacja już w Softra — WP save nie może blokować usera | Bezpieczne — error_log w razie problemu | +| Meta-based status (nie taxonomy) | 3 wartości enum, zero UI overhead | Proste, wystarczające dla MVP | +| Auto-mark-read on edit | Naturalny workflow, mniej kliknięć | UX improvement | +| Klikalny nr rezerwacji | Brak kolumny title = brak linku do edycji | Naprawione podczas checkpoint — link w kolumnie reservation_no | + +## Deviations from Plan + +### Summary + +| Type | Count | Impact | +|------|-------|--------| +| Auto-fixed | 1 | Kliknięcie w rezerwację — minor UX fix | +| Scope additions | 0 | — | +| Deferred | 0 | — | + +**Total impact:** Minimalne — jeden fix UX podczas checkpoint + +### Auto-fixed Issues + +**1. Brak linku do edycji w liście rezerwacji** +- **Found during:** Checkpoint (Task 3) +- **Issue:** Bez kolumny `title` nie było klikalnego linku do szczegółów +- **Fix:** Owinięcie nr rezerwacji w `` z `get_edit_post_link()` +- **Files:** `includes/class-admin-panel.php` +- **Verification:** User potwierdził "Teraz jest ok" + +## Issues Encountered + +| Issue | Resolution | +|-------|------------| +| Brak linku do edycji rezerwacji | Dodano link w kolumnie reservation_no | + +## Next Phase Readiness + +**Ready:** +- Milestone v0.1 kompletny — wszystkie 5 faz zakończone +- Plugin w pełni funkcjonalny: formularz → API → admin panel + +**Concerns:** +- Brak — MVP complete + +**Blockers:** +- None + +--- +*Phase: 05-admin-panel, Plan: 01* +*Completed: 2026-03-25* diff --git a/wp-content/plugins/carei-reservation/carei-reservation.php b/wp-content/plugins/carei-reservation/carei-reservation.php index a6739dc..0518e3c 100644 --- a/wp-content/plugins/carei-reservation/carei-reservation.php +++ b/wp-content/plugins/carei-reservation/carei-reservation.php @@ -50,6 +50,7 @@ function carei_parse_env() { */ require_once CAREI_RESERVATION_PATH . 'includes/class-softra-api.php'; require_once CAREI_RESERVATION_PATH . 'includes/class-rest-proxy.php'; +require_once CAREI_RESERVATION_PATH . 'includes/class-admin-panel.php'; /** * Initialize plugin on plugins_loaded @@ -73,6 +74,9 @@ add_action( 'plugins_loaded', function () { // Initialize REST proxy new Carei_REST_Proxy(); + + // Initialize admin panel + new Carei_Admin_Panel(); } ); /** diff --git a/wp-content/plugins/carei-reservation/includes/class-admin-panel.php b/wp-content/plugins/carei-reservation/includes/class-admin-panel.php new file mode 100644 index 0000000..f849264 --- /dev/null +++ b/wp-content/plugins/carei-reservation/includes/class-admin-panel.php @@ -0,0 +1,412 @@ + array( 'label' => 'Nowe', 'color' => '#2F2482' ), + 'przeczytane' => array( 'label' => 'Przeczytane', 'color' => '#f59e0b' ), + 'zrealizowane' => array( 'label' => 'Zrealizowane', 'color' => '#22c55e' ), + ); + + public function __construct() { + add_action( 'init', array( $this, 'register_post_type' ) ); + add_filter( 'manage_' . self::POST_TYPE . '_posts_columns', array( $this, 'admin_columns' ) ); + add_action( 'manage_' . self::POST_TYPE . '_posts_custom_column', array( $this, 'render_column' ), 10, 2 ); + add_action( 'restrict_manage_posts', array( $this, 'status_filter_dropdown' ) ); + add_action( 'pre_get_posts', array( $this, 'filter_by_status' ) ); + add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) ); + add_action( 'save_post_' . self::POST_TYPE, array( $this, 'save_meta_box' ), 10, 2 ); + add_action( 'edit_form_after_title', array( $this, 'auto_mark_read' ) ); + add_action( 'admin_head', array( $this, 'admin_styles' ) ); + } + + public function register_post_type() { + register_post_type( self::POST_TYPE, array( + 'labels' => array( + 'name' => 'Rezerwacje', + 'singular_name' => 'Rezerwacja', + 'menu_name' => 'Rezerwacje', + 'all_items' => 'Wszystkie rezerwacje', + 'view_item' => 'Zobacz rezerwację', + 'edit_item' => 'Szczegóły rezerwacji', + 'search_items' => 'Szukaj rezerwacji', + 'not_found' => 'Nie znaleziono rezerwacji', + 'not_found_in_trash' => 'Brak rezerwacji w koszu', + ), + 'public' => false, + 'show_ui' => true, + 'show_in_menu' => true, + 'menu_icon' => 'dashicons-car', + 'menu_position' => 26, + 'supports' => array( 'title' ), + 'capability_type' => 'post', + 'has_archive' => false, + 'hierarchical' => false, + 'show_in_rest' => false, + ) ); + } + + // ─── Admin Columns ────────────────────────────────────────── + + public function admin_columns( $columns ) { + return array( + 'cb' => '', + 'reservation_no' => 'Nr rezerwacji', + 'client' => 'Klient', + 'segment' => 'Segment', + 'dates' => 'Daty', + 'branch' => 'Oddział', + 'carei_status' => 'Status', + 'date' => 'Data', + ); + } + + public function render_column( $column, $post_id ) { + switch ( $column ) { + case 'reservation_no': + $no = get_post_meta( $post_id, self::META_PREFIX . 'reservation_no', true ) ?: '—'; + printf( + '%s', + esc_url( get_edit_post_link( $post_id ) ), + esc_html( $no ) + ); + break; + case 'client': + $first = get_post_meta( $post_id, self::META_PREFIX . 'first_name', true ); + $last = get_post_meta( $post_id, self::META_PREFIX . 'last_name', true ); + echo esc_html( trim( $first . ' ' . $last ) ?: '—' ); + break; + case 'segment': + echo esc_html( get_post_meta( $post_id, self::META_PREFIX . 'segment', true ) ?: '—' ); + break; + case 'dates': + $from = get_post_meta( $post_id, self::META_PREFIX . 'date_from', true ); + $to = get_post_meta( $post_id, self::META_PREFIX . 'date_to', true ); + if ( $from && $to ) { + $from_fmt = date_i18n( 'd.m.Y H:i', strtotime( $from ) ); + $to_fmt = date_i18n( 'd.m.Y H:i', strtotime( $to ) ); + echo esc_html( $from_fmt . ' — ' . $to_fmt ); + } else { + echo '—'; + } + break; + case 'branch': + echo esc_html( get_post_meta( $post_id, self::META_PREFIX . 'pickup_branch', true ) ?: '—' ); + break; + case 'carei_status': + $status = get_post_meta( $post_id, self::META_PREFIX . 'status', true ) ?: 'nowe'; + $info = isset( self::$statuses[ $status ] ) ? self::$statuses[ $status ] : self::$statuses['nowe']; + printf( + '%s', + esc_attr( $info['color'] ), + esc_html( $info['label'] ) + ); + break; + } + } + + // ─── Status Filter ────────────────────────────────────────── + + public function status_filter_dropdown( $post_type ) { + if ( $post_type !== self::POST_TYPE ) { + return; + } + $current = isset( $_GET['carei_status'] ) ? sanitize_text_field( $_GET['carei_status'] ) : ''; + echo ''; + } + + public function filter_by_status( $query ) { + if ( ! is_admin() || ! $query->is_main_query() ) { + return; + } + if ( ( $query->get( 'post_type' ) !== self::POST_TYPE ) ) { + return; + } + if ( ! empty( $_GET['carei_status'] ) ) { + $status = sanitize_text_field( $_GET['carei_status'] ); + if ( isset( self::$statuses[ $status ] ) ) { + $query->set( 'meta_query', array( + array( + 'key' => self::META_PREFIX . 'status', + 'value' => $status, + ), + ) ); + } + } + // Default sort by date desc + if ( ! $query->get( 'orderby' ) ) { + $query->set( 'orderby', 'date' ); + $query->set( 'order', 'DESC' ); + } + } + + // ─── Meta Box ─────────────────────────────────────────────── + + public function add_meta_boxes() { + add_meta_box( + 'carei_reservation_details', + 'Szczegóły rezerwacji', + array( $this, 'render_meta_box' ), + self::POST_TYPE, + 'normal', + 'high' + ); + } + + public function render_meta_box( $post ) { + wp_nonce_field( 'carei_save_reservation', 'carei_reservation_nonce' ); + + $meta = array( + 'reservation_no' => get_post_meta( $post->ID, self::META_PREFIX . 'reservation_no', true ), + 'reservation_id' => get_post_meta( $post->ID, self::META_PREFIX . 'reservation_id', true ), + 'customer_id' => get_post_meta( $post->ID, self::META_PREFIX . 'customer_id', true ), + 'segment' => get_post_meta( $post->ID, self::META_PREFIX . 'segment', true ), + 'date_from' => get_post_meta( $post->ID, self::META_PREFIX . 'date_from', true ), + 'date_to' => get_post_meta( $post->ID, self::META_PREFIX . 'date_to', true ), + 'pickup_branch' => get_post_meta( $post->ID, self::META_PREFIX . 'pickup_branch', true ), + 'return_branch' => get_post_meta( $post->ID, self::META_PREFIX . 'return_branch', true ), + 'first_name' => get_post_meta( $post->ID, self::META_PREFIX . 'first_name', true ), + 'last_name' => get_post_meta( $post->ID, self::META_PREFIX . 'last_name', true ), + 'email' => get_post_meta( $post->ID, self::META_PREFIX . 'email', true ), + 'phone' => get_post_meta( $post->ID, self::META_PREFIX . 'phone', true ), + 'pesel' => get_post_meta( $post->ID, self::META_PREFIX . 'pesel', true ), + 'address' => get_post_meta( $post->ID, self::META_PREFIX . 'address', true ), + 'extras' => get_post_meta( $post->ID, self::META_PREFIX . 'extras', true ), + 'comments' => get_post_meta( $post->ID, self::META_PREFIX . 'comments', true ), + 'status' => get_post_meta( $post->ID, self::META_PREFIX . 'status', true ) ?: 'nowe', + ); + + $address = $meta['address'] ? json_decode( $meta['address'], true ) : null; + $address_str = ''; + if ( $address ) { + $parts = array_filter( array( + isset( $address['street'] ) ? $address['street'] : '', + isset( $address['zipCode'] ) ? $address['zipCode'] : '', + isset( $address['city'] ) ? $address['city'] : '', + ) ); + $address_str = implode( ', ', $parts ); + } + + $extras = $meta['extras'] ? json_decode( $meta['extras'], true ) : array(); + $extras_str = ''; + if ( is_array( $extras ) && ! empty( $extras ) ) { + $names = array_map( function ( $e ) { + return isset( $e['name'] ) ? $e['name'] : ''; + }, $extras ); + $extras_str = implode( ', ', array_filter( $names ) ); + } + + $from_fmt = $meta['date_from'] ? date_i18n( 'd.m.Y H:i', strtotime( $meta['date_from'] ) ) : '—'; + $to_fmt = $meta['date_to'] ? date_i18n( 'd.m.Y H:i', strtotime( $meta['date_to'] ) ) : '—'; + + ?> + + + + + + + + + + + + + + + + + + + + + + + + + +
Nr rezerwacji
ID rezerwacji (Softra)
ID klienta (Softra)

Segment
Data od
Data do
Oddział odbioru
Oddział zwrotu

Imię
Nazwisko
Email
Telefon
PESEL
Adres

Opcje dodatkowe
Wiadomość

Status + +
+ post_type !== self::POST_TYPE ) { + return; + } + $status = get_post_meta( $post->ID, self::META_PREFIX . 'status', true ); + if ( $status === 'nowe' ) { + update_post_meta( $post->ID, self::META_PREFIX . 'status', 'przeczytane' ); + } + } + + // ─── Admin Styles ─────────────────────────────────────────── + + public function admin_styles() { + $screen = get_current_screen(); + if ( ! $screen || $screen->post_type !== self::POST_TYPE ) { + return; + } + ?> + + self::POST_TYPE, + 'post_title' => trim( $title ), + 'post_status' => 'publish', + ), true ); + + if ( is_wp_error( $post_id ) ) { + error_log( 'Carei: Failed to save reservation — ' . $post_id->get_error_message() ); + return false; + } + + $pickup_branch = ''; + if ( isset( $booking_data['pickUpLocation']['branchName'] ) ) { + $pickup_branch = $booking_data['pickUpLocation']['branchName']; + } + $return_branch = ''; + if ( isset( $booking_data['returnLocation']['branchName'] ) ) { + $return_branch = $booking_data['returnLocation']['branchName']; + } + $segment = ''; + if ( isset( $booking_data['carParameters']['categoryName'] ) ) { + $segment = $booking_data['carParameters']['categoryName']; + } + + $meta = array( + 'reservation_no' => $reservation_no, + 'reservation_id' => $reservation_id, + 'customer_id' => isset( $booking_data['customerId'] ) ? $booking_data['customerId'] : '', + 'segment' => $segment, + 'date_from' => isset( $booking_data['dateFrom'] ) ? $booking_data['dateFrom'] : '', + 'date_to' => isset( $booking_data['dateTo'] ) ? $booking_data['dateTo'] : '', + 'pickup_branch' => $pickup_branch, + 'return_branch' => $return_branch, + 'first_name' => $first_name, + 'last_name' => $last_name, + 'email' => isset( $driver['email'] ) ? $driver['email'] : '', + 'phone' => isset( $driver['phone'] ) ? $driver['phone'] : '', + 'pesel' => isset( $driver['pesel'] ) ? $driver['pesel'] : '', + 'address' => isset( $driver['address'] ) ? wp_json_encode( $driver['address'] ) : '', + 'extras' => isset( $booking_data['priceItems'] ) ? wp_json_encode( $booking_data['priceItems'] ) : '', + 'comments' => isset( $booking_data['comments'] ) ? $booking_data['comments'] : '', + 'status' => 'nowe', + 'raw_response' => wp_json_encode( $api_result ), + ); + + foreach ( $meta as $key => $value ) { + update_post_meta( $post_id, self::META_PREFIX . $key, $value ); + } + + return $post_id; + } +} diff --git a/wp-content/plugins/carei-reservation/includes/class-rest-proxy.php b/wp-content/plugins/carei-reservation/includes/class-rest-proxy.php index 071a42b..58f4c1a 100644 --- a/wp-content/plugins/carei-reservation/includes/class-rest-proxy.php +++ b/wp-content/plugins/carei-reservation/includes/class-rest-proxy.php @@ -30,6 +30,13 @@ class Carei_REST_Proxy { 'permission_callback' => '__return_true', ) ); + // GET /segments-branches-map (cached segment→branches mapping) + register_rest_route( self::NAMESPACE, '/segments-branches-map', array( + 'methods' => 'GET', + 'callback' => array( $this, 'get_segments_branches_map' ), + 'permission_callback' => '__return_true', + ) ); + // POST /car-classes register_rest_route( self::NAMESPACE, '/car-classes', array( 'methods' => 'POST', @@ -99,6 +106,17 @@ class Carei_REST_Proxy { ), ) ); + // POST /booking/cancel + register_rest_route( self::NAMESPACE, '/booking/cancel', array( + 'methods' => 'POST', + 'callback' => array( $this, 'cancel_booking' ), + 'permission_callback' => array( $this, 'check_nonce' ), + 'args' => array( + 'reservationId' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ), + 'reason' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ), + ), + ) ); + // GET /agreements register_rest_route( self::NAMESPACE, '/agreements', array( 'methods' => 'GET', @@ -141,6 +159,14 @@ class Carei_REST_Proxy { // ─── Callbacks ──────────────────────────────────────────────── + public function get_segments_branches_map( WP_REST_Request $request ) { + $api = $this->api(); + if ( is_wp_error( $api ) ) { + return $api; + } + return $this->respond( $api->get_segments_branches_map() ); + } + public function get_all_car_classes( WP_REST_Request $request ) { $api = $this->api(); if ( is_wp_error( $api ) ) { @@ -218,8 +244,15 @@ class Carei_REST_Proxy { if ( is_wp_error( $api ) ) { return $api; } - $data = $request->get_json_params(); - return $this->respond( $api->make_booking( $data ) ); + $data = $request->get_json_params(); + $result = $api->make_booking( $data ); + + // Save reservation to WP on success (fire-and-forget) + if ( ! is_wp_error( $result ) && isset( $result['success'] ) && $result['success'] ) { + Carei_Admin_Panel::save_reservation( $data, $result ); + } + + return $this->respond( $result ); } public function confirm_booking( WP_REST_Request $request ) { @@ -232,6 +265,17 @@ class Carei_REST_Proxy { ) ); } + public function cancel_booking( WP_REST_Request $request ) { + $api = $this->api(); + if ( is_wp_error( $api ) ) { + return $api; + } + return $this->respond( $api->cancel_booking( + $request->get_param( 'reservationId' ), + $request->get_param( 'reason' ) + ) ); + } + public function get_agreements( WP_REST_Request $request ) { $api = $this->api(); if ( is_wp_error( $api ) ) {