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
+
+
+
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'] ) ) : '—';
+
+ ?>
+
+ 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 ) ) {