diff --git a/.paul/PROJECT.md b/.paul/PROJECT.md
index 7906521..b1b88e5 100644
--- a/.paul/PROJECT.md
+++ b/.paul/PROJECT.md
@@ -33,6 +33,9 @@ Plugin Elementor do rezerwacji samochodu na stronie carei.pagedev.pl, zintegrowa
- ✓ Accessibility: ARIA dialog, focus trap, aria-live — Phase 4
- ✓ Admin panel: CPT carei_reservation, lista, szczegóły, statusy — Phase 5
+## Validated Requirements (Milestone v0.2)
+- ✓ Hero Search Form: mini formularz w hero z pre-fill do modala — Phase 7
+
## Key Decisions
| Decision | Phase | Rationale |
|----------|-------|-----------|
@@ -41,6 +44,8 @@ Plugin Elementor do rezerwacji samochodu na stronie carei.pagedev.pl, zintegrowa
| 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 |
+| Calendar picker opacity:0 stretch | 7 | Ukrycie natywnej ikonki z zachowaniem kliknięcia |
+| Search form niezależne API loading | 7 | Dane dostępne od razu, niezależnie od modala |
## Out of Scope (backlog)
- Ubezpieczenie (pakiet Soft/Premium) — czeka na API Softra
diff --git a/.paul/ROADMAP.md b/.paul/ROADMAP.md
index 32d17bd..d36c855 100644
--- a/.paul/ROADMAP.md
+++ b/.paul/ROADMAP.md
@@ -30,6 +30,9 @@ CPT `carei_reservation` z automatycznym zapisem po booking, lista z kolumnami i
### Phase 6: Wyjazd zagraniczny — sekcja + wyszukiwarka krajów 🔄 Planning
Sekcja "Wyjazd zagraniczny" z checkboxem toggle, wyszukiwarką krajów z flagami/cenami, dodawanie/usuwanie krajów, integracja z API submit. Design z Figmy (node 32-397, 122:1054, 122:1091, 123:1195).
+### Phase 7: Hero Search Form — mini formularz w hero ✅ Complete
+Kompaktowy widget Elementor "Carei Search Form" osadzany w sekcji hero. Pola: segment, daty od/do, lokalizacja, checkbox zwrotu. Po kliknięciu przycisku otwiera istniejący modal rezerwacji z pre-wypełnionymi danymi. Design z Figmy (Form.svg).
+
---
### Backlog (do realizacji gdy API będzie gotowe)
diff --git a/.paul/STATE.md b/.paul/STATE.md
index 3a84a1a..dc359f6 100644
--- a/.paul/STATE.md
+++ b/.paul/STATE.md
@@ -3,32 +3,40 @@
## Current Position
Milestone: v0.2 Wyjazd Zagraniczny
-Phase: 6 of 6 (Wyjazd zagraniczny — sekcja + wyszukiwarka krajów) — Planning
-Plan: 06-01 created, awaiting approval
-Status: PLAN created, ready for APPLY
-Last activity: 2026-03-30 — Created .paul/phases/06-wyjazd-zagraniczny/06-01-PLAN.md
+Phase: 7 of 7 (Hero Search Form) — Complete
+Plan: 07-01 complete
+Status: Phase 7 complete, Phase 6 plan awaiting execution
+Last activity: 2026-04-01 — Phase 7 complete, transitioned
Progress:
- Milestone v0.1: [██████████] 100% ✅
-- Milestone v0.2: [░░░░░░░░░░] 0%
-- Phase 6: [░░░░░░░░░░] 0%
+- Milestone v0.2: [█████░░░░░] 50%
+- Phase 6 (Wyjazd zagraniczny): Plan created, not applied
+- Phase 7 (Hero Search Form): [██████████] 100% ✅
## Loop Position
Current loop state:
```
PLAN ──▶ APPLY ──▶ UNIFY
- ✓ ○ ○ [Plan created, awaiting approval]
+ ✓ ✓ ✓ [Loop complete — Phase 7 done]
```
+## Accumulated Context
+
+### Decisions
+| Decision | Phase | Impact |
+|----------|-------|--------|
+| Calendar picker opacity:0 stretch | 7 | Natywna ikonka ukryta, kliknięcie zachowane |
+| Search form niezależne API loading | 7 | Dwa requesty API na page load |
+
+### Git State
+Branch: main
+Feature branches merged: none
+
## Session Continuity
-Last session: 2026-03-30
-Stopped at: Plan 06-01 created
-Next action: Review and approve plan, then run /paul:apply .paul/phases/06-wyjazd-zagraniczny/06-01-PLAN.md
-Resume file: .paul/phases/06-wyjazd-zagraniczny/06-01-PLAN.md
-Resume context:
-- v0.1 complete (5 phases)
-- v0.2 Phase 6: wyjazd zagraniczny — wydzielenie WYJAZD ZA GRANICĘ z extras do dedykowanej sekcji z wyszukiwarką krajów
-- Dane krajów z istniejącego pricelist API (additionalItems z nazwą WYJAZD ZA GRANIC...)
-- Design z Figmy: checkbox toggle + wyszukiwarka z flagami/cenami + karty krajów
+Last session: 2026-04-01
+Stopped at: Phase 7 complete
+Next action: Execute Phase 6 (/paul:apply .paul/phases/06-wyjazd-zagraniczny/06-01-PLAN.md) or plan new work
+Resume file: .paul/ROADMAP.md
diff --git a/.paul/phases/07-hero-search-form/07-01-PLAN.md b/.paul/phases/07-hero-search-form/07-01-PLAN.md
new file mode 100644
index 0000000..36180ba
--- /dev/null
+++ b/.paul/phases/07-hero-search-form/07-01-PLAN.md
@@ -0,0 +1,263 @@
+---
+phase: 07-hero-search-form
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - wp-content/plugins/carei-reservation/includes/class-search-widget.php
+ - wp-content/plugins/carei-reservation/assets/css/carei-reservation.css
+ - wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
+ - wp-content/plugins/carei-reservation/carei-reservation.php
+autonomous: false
+---
+
+
+## Goal
+Nowy widget Elementor "Carei Search Form" — kompaktowy mini formularz rezerwacji do osadzenia w sekcji hero. Po wypełnieniu i kliknięciu przycisku otwiera istniejący popup formularza rezerwacji z automatycznie uzupełnionymi danymi (segment, daty, lokalizacja).
+
+## Purpose
+Użytkownik widzi formularz już na hero — nie musi szukać przycisku rezerwacji. Skraca ścieżkę konwersji i daje natychmiastowe CTA.
+
+## Output
+- Nowy plik: `class-search-widget.php` (widget Elementor)
+- Rozszerzony CSS: style mini formularza
+- Rozszerzony JS: logika prefill + otwarcie modala z danymi z mini formularza
+- Rejestracja widgetu w `carei-reservation.php`
+
+
+
+## Project Context
+@.paul/PROJECT.md
+@.paul/ROADMAP.md
+
+## Source Files
+@wp-content/plugins/carei-reservation/carei-reservation.php
+@wp-content/plugins/carei-reservation/includes/class-elementor-widget.php
+@wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
+@wp-content/plugins/carei-reservation/assets/css/carei-reservation.css
+
+
+
+
+## AC-1: Widget renderuje mini formularz
+```gherkin
+Given strona z osadzonym widgetem "Carei Search Form" w Elementorze
+When strona się ładuje
+Then widoczny jest kompaktowy formularz z polami: segment (select), daty od/do, miejsce odbioru (select), checkbox "Zwrot w tej samej lokalizacji", przycisk "Złóż zapytanie o rezerwację"
+```
+
+## AC-2: Selecty ładują dane z API
+```gherkin
+Given mini formularz jest widoczny na stronie
+When strona się załadowała
+Then select segmentu zawiera segmenty z API (segments-branches-map)
+And select lokalizacji filtruje się po wybranym segmencie
+```
+
+## AC-3: Przycisk otwiera modal z pre-wypełnionymi danymi
+```gherkin
+Given użytkownik wypełnił mini formularz (segment, daty, lokalizacja)
+When kliknie "Złóż zapytanie o rezerwację"
+Then otwiera się istniejący modal rezerwacji
+And pola segment, data od, data do, miejsce odbioru są automatycznie wypełnione wartościami z mini formularza
+And checkbox zwrotu jest zsynchronizowany
+And extras/pricelist ładują się automatycznie (jak po ręcznym wypełnieniu)
+```
+
+## AC-4: Design zgodny z Figmą
+```gherkin
+Given mini formularz jest renderowany
+When wyświetla się na desktop
+Then tło #EDEDF3, zaokrąglone rogi 14px, border #2F2482/10%
+And pola mają białe tło, zaokrąglone rogi
+And przycisk jest czerwony (#FF0000) z białym tekstem, pełna szerokość
+And font Albert Sans, tytuł bold fioletowy (#2F2482)
+```
+
+
+
+
+
+
+ Task 1: Widget Elementor + HTML mini formularza
+ wp-content/plugins/carei-reservation/includes/class-search-widget.php, wp-content/plugins/carei-reservation/carei-reservation.php
+
+ 1. Utworzyć `class-search-widget.php` z klasą `Carei_Search_Widget extends \Elementor\Widget_Base`:
+ - name: `carei-search-form`
+ - title: `Carei Search Form`
+ - icon: `eicon-search`
+ - categories: `['general']`
+ - style/script depends: te same co główny widget (carei-reservation-css, carei-reservation-js)
+ - Brak kontrolek Elementor (formularz jest statyczny)
+
+ 2. Metoda `render()` generuje HTML:
+ - Container `div.carei-search-form` z tłem
+ - Tytuł: ``
+ - Select segmentu: `
+ Widget "Carei Search Form" widoczny w panelu Elementor, po osadzeniu renderuje HTML mini formularza
+ AC-1 satisfied: Mini formularz renderuje się z wszystkimi polami
+
+
+
+ Task 2: JavaScript — ładowanie danych + prefill + otwarcie modala
+ wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
+
+ 1. W sekcji inicjalizacji (DOMContentLoaded) dodać wykrywanie mini formularza:
+ ```
+ var searchForm = document.querySelector('.carei-search-form');
+ ```
+ Jeśli istnieje, zainicjalizować `initSearchForm()`.
+
+ 2. Funkcja `initSearchForm()`:
+ - Pobrać referencje do elementów: `carei-search-segment`, `carei-search-date-from`, `carei-search-date-to`, `carei-search-pickup`, `carei-search-same-return`
+ - Załadować dane API (segments-branches-map) — REUŻYĆ istniejącą logikę `loadInitialData`, ale populować selecty mini formularza
+ - Podpiąć event na zmianę segmentu → filtrowanie lokalizacji (jak w głównym formularzu)
+ - Podpiąć event na `#carei-search-submit` click:
+ a) Zebrać wartości z mini formularza
+ b) Znaleźć overlay modala: `document.querySelector('[data-carei-modal]')`
+ c) Wywołać `openModal()` (istniejąca funkcja)
+ d) Po otwarciu modala (setTimeout ~100ms po openModal) ustawić wartości w głównym formularzu:
+ - `segmentSelect.value = searchSegment` + dispatch 'change' event
+ - `dateFrom.value = searchDateFrom` + dispatch 'change'
+ - `dateTo.value = searchDateTo` + dispatch 'change'
+ - `pickupSelect` — poczekać aż lokalizacje się załadują po change segmentu, potem ustawić wartość
+ - `sameReturnCheck.checked = searchSameReturn`
+ e) Triggerować `loadExtras()` jeśli wszystkie wymagane pola wypełnione
+
+ 3. Problem z timingiem: po ustawieniu segmentu, lokalizacje ładują się asynchronicznie. Rozwiązanie:
+ - Dodać callback/promise lub event `carei:branches-loaded` który dispatch-uje się po załadowaniu lokalizacji
+ - W prefill poczekać na ten event, potem ustawić pickupSelect.value
+
+ 4. Osobna inicjalizacja dat w mini formularzu — labelki "Od kiedy?"/"Do kiedy?" zachowują się jak placeholdery (widoczne gdy puste).
+
+ Avoid:
+ - Nie duplikować fetchowania API — jeśli dane już załadowane (dataLoaded=true), reużyć cache
+ - Nie łamać istniejącej logiki modala — openModal() musi działać normalnie gdy wywoływana z przycisku `data-carei-open-modal`
+
+ Po wypełnieniu mini formularza i kliknięciu przycisku: modal się otwiera, pola segment/daty/lokalizacja są wypełnione, extras się ładują
+ AC-2, AC-3 satisfied: Selecty ładują dane, przycisk otwiera modal z pre-fill
+
+
+
+ Task 3: CSS — stylowanie mini formularza zgodne z Figmą
+ wp-content/plugins/carei-reservation/assets/css/carei-reservation.css
+
+ Dodać style dla `.carei-search-form` na końcu pliku CSS:
+
+ 1. Container `.carei-search-form`:
+ - background: #EDEDF3
+ - border-radius: 14px
+ - border: 1px solid rgba(47, 36, 130, 0.1)
+ - padding: ~24px 28px
+ - max-width: 422px (z Figmy: width 422px)
+ - box-shadow: subtletny drop shadow
+
+ 2. Tytuł `.carei-search-form__title`:
+ - font-family: 'Albert Sans', sans-serif
+ - font-weight: 700
+ - color: #2F2482
+ - text-align: center
+ - span (kropka): color: #FF0000
+
+ 3. Pola formularza `.carei-search-form__field`:
+ - background: #fff
+ - border-radius: 8px
+ - border: 1px solid #E0E0E0
+ - padding: 12px 16px
+ - font-size: 14px
+ - Selecty: appearance: none + custom arrow icon
+ - Daty: dwa pola obok siebie (flex row, gap)
+
+ 4. Checkbox `.carei-search-form__checkbox`:
+ - Styl jak w głównym formularzu (custom checkbox box z SVG checkmark)
+ - Kolor checked: #2F2482
+
+ 5. Przycisk `.carei-search-form__submit`:
+ - background: #FF0000
+ - color: #fff
+ - border-radius: 8px
+ - width: 100%
+ - padding: 14px
+ - font-weight: 600
+ - ikona strzałki przed tekstem
+ - hover: lekkie przyciemnienie
+
+ 6. Responsive:
+ - Na mobile (< 768px): max-width: 100%, padding zmniejszony
+ - Pola dat: mogą być w kolumnie na bardzo wąskich ekranach (< 400px)
+
+ Avoid: Nie modyfikować istniejących styli modala/formularza — dodać TYLKO nowe reguły z prefiksem `.carei-search-form`
+
+ Mini formularz wygląda zgodnie z designem z Figmy: tło szare, pola białe, przycisk czerwony, tytuł fioletowy
+ AC-4 satisfied: Design zgodny z Figmą
+
+
+
+ Mini formularz rezerwacji w hero z pre-fill do modala
+
+ 1. Otwórz stronę w Elementorze, osadź widget "Carei Search Form" w sekcji hero
+ 2. Na tej samej stronie musi być osadzony widget "Carei Reservation" (renderuje modal)
+ 3. Podgląd strony:
+ - Mini formularz widoczny z polami segment, daty, lokalizacja, checkbox
+ - Wybierz segment → lokalizacje się filtrują
+ - Wypełnij daty i lokalizację
+ - Kliknij "Złóż zapytanie o rezerwację"
+ - Modal się otwiera z wypełnionymi danymi
+ - Extras ładują się automatycznie
+ 4. Sprawdź design: tło szare, pola białe, przycisk czerwony
+ 5. Sprawdź mobile: formularz responsywny
+
+ Type "approved" to continue, or describe issues to fix
+
+
+
+
+
+
+## DO NOT CHANGE
+- includes/class-elementor-widget.php — HTML modala pozostaje bez zmian
+- includes/class-softra-api.php — logika API bez zmian
+- includes/class-rest-proxy.php — endpointy REST bez zmian
+- includes/class-admin-panel.php — panel admina bez zmian
+
+## SCOPE LIMITS
+- Mini formularz NIE ma własnego modala — korzysta z modala renderowanego przez istniejący widget
+- Brak walidacji w mini formularzu — walidacja jest w głównym formularzu
+- Brak pól danych osobowych w mini formularzu — tylko dane wynajmu
+- Nie dodawać nowych zależności npm/composer
+
+
+
+
+Before declaring plan complete:
+- [ ] Widget "Carei Search Form" widoczny w panelu Elementor
+- [ ] Mini formularz renderuje się na stronie z prawidłowym designem
+- [ ] Selecty segmentu i lokalizacji ładują dane z API
+- [ ] Kliknięcie przycisku otwiera modal z pre-wypełnionymi danymi
+- [ ] Istniejący przycisk `data-carei-open-modal` nadal działa normalnie
+- [ ] Responsive: formularz poprawny na mobile
+- [ ] Brak błędów JS w konsoli
+
+
+
+- Widget osadzony w Elementorze renderuje mini formularz
+- Dane z mini formularza poprawnie przenoszone do modala
+- Design zgodny z Figmą
+- Istniejąca funkcjonalność modala nienaruszona
+
+
+
diff --git a/.paul/phases/07-hero-search-form/07-01-SUMMARY.md b/.paul/phases/07-hero-search-form/07-01-SUMMARY.md
new file mode 100644
index 0000000..f2988e6
--- /dev/null
+++ b/.paul/phases/07-hero-search-form/07-01-SUMMARY.md
@@ -0,0 +1,122 @@
+---
+phase: 07-hero-search-form
+plan: 01
+subsystem: ui
+tags: [elementor, widget, vanilla-js, css]
+
+requires:
+ - phase: 01-reservation-form-plugin
+ provides: plugin bootstrap, API proxy, Elementor widget infrastructure
+ - phase: 02-form-ui-step1
+ provides: modal form UI, segments/branches data loading
+provides:
+ - Carei Search Form Elementor widget (mini formularz hero)
+ - Pre-fill modal z danymi z mini formularza
+affects: []
+
+tech-stack:
+ added: []
+ patterns: [search-form-prefill-via-setTimeout-polling]
+
+key-files:
+ created:
+ - wp-content/plugins/carei-reservation/includes/class-search-widget.php
+ modified:
+ - wp-content/plugins/carei-reservation/carei-reservation.php
+ - wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
+ - wp-content/plugins/carei-reservation/assets/css/carei-reservation.css
+
+key-decisions:
+ - "Calendar picker: opacity:0 stretched over input instead of display:none — preserves native click-to-open"
+ - "Search form loads API data independently — not dependent on modal being present"
+
+patterns-established:
+ - "Mini form prefill: collect values → openModal() → setTimeout prefill → poll for async-loaded options"
+
+duration: ~30min
+started: 2026-04-01T13:00:00Z
+completed: 2026-04-01T13:45:00Z
+---
+
+# Phase 7 Plan 01: Hero Search Form Summary
+
+**Kompaktowy widget Elementor "Carei Search Form" w hero z pre-fill do istniejącego modala rezerwacji**
+
+## Performance
+
+| Metric | Value |
+|--------|-------|
+| Duration | ~30min |
+| Tasks | 4 completed (3 auto + 1 checkpoint) |
+| Files modified | 4 (1 created, 3 modified) |
+
+## Acceptance Criteria Results
+
+| Criterion | Status | Notes |
+|-----------|--------|-------|
+| AC-1: Widget renderuje mini formularz | Pass | Segment, daty, lokalizacja, checkbox, przycisk |
+| AC-2: Selecty ładują dane z API | Pass | Segmenty i lokalizacje filtrowane po segmencie |
+| AC-3: Przycisk otwiera modal z pre-fill | Pass | Segment, daty, lokalizacja przenoszone do modala |
+| AC-4: Design zgodny z Figmą | Pass | Tło #EDEDF3, pola białe, przycisk czerwony, tytuł fioletowy |
+
+## Accomplishments
+
+- Nowy widget Elementor "Carei Search Form" do osadzenia w hero
+- Pola mini formularza automatycznie przenoszone do modala rezerwacji po kliknięciu przycisku
+- Niezależne ładowanie danych API (nie wymaga wcześniejszego otwarcia modala)
+
+## Files Created/Modified
+
+| File | Change | Purpose |
+|------|--------|---------|
+| `includes/class-search-widget.php` | Created | Widget Elementor z HTML mini formularza |
+| `carei-reservation.php` | Modified | Rejestracja nowego widgetu |
+| `assets/js/carei-reservation.js` | Modified | initSearchForm() — ładowanie danych, prefill, otwarcie modala |
+| `assets/css/carei-reservation.css` | Modified | Style .carei-search-form (tło, pola, przycisk, responsive) |
+
+## Decisions Made
+
+| Decision | Rationale | Impact |
+|----------|-----------|--------|
+| Osobne ładowanie API w search form | Niezależność od modala — dane dostępne od razu po załadowaniu strony | Dwa requesty API przy page load (zamiast lazy) |
+| Polling na pickup options po prefill | Lokalizacje ładują się async po zmianie segmentu — nie ma eventa | setInterval z max 30 prób (3s timeout) |
+
+## Deviations from Plan
+
+### Summary
+
+| Type | Count | Impact |
+|------|-------|--------|
+| Auto-fixed | 1 | Niezbędny fix CSS |
+
+**Total impact:** Fix CSS kalendarza — konieczny dla UX
+
+### Auto-fixed Issues
+
+**1. CSS calendar picker indicator**
+- **Found during:** Checkpoint (human-verify)
+- **Issue:** `display:none` na ikonkach kalendarza blokowało otwarcie date picker
+- **Fix:** Zmiana na `opacity:0` + stretch na cały input (wzorzec z głównego formularza)
+- **Files:** carei-reservation.css
+- **Verification:** Kliknięcie w pole daty otwiera picker
+
+**2. CSS datetime placeholder selectable**
+- **Found during:** Checkpoint (human-verify)
+- **Issue:** Placeholder "--" w pustych polach dat był zaznaczalny
+- **Fix:** Dodanie `user-select:none` + `color:transparent` na `::-webkit-datetime-edit`
+- **Verification:** Placeholder niewidoczny i niezaznaczalny
+
+## Next Phase Readiness
+
+**Ready:**
+- Mini formularz działa i jest osadzony w hero
+- Modal rezerwacji przyjmuje pre-fill z zewnętrznego źródła
+
+**Concerns:**
+- Dwa niezależne requesty API (search form + modal) — mogą powodować podwójne obciążenie przy jednoczesnym ładowaniu
+
+**Blockers:** None
+
+---
+*Phase: 07-hero-search-form, Plan: 01*
+*Completed: 2026-04-01*
diff --git a/wp-content/plugins/carei-reservation/assets/css/carei-reservation.css b/wp-content/plugins/carei-reservation/assets/css/carei-reservation.css
index ed4fac7..1f047f7 100644
--- a/wp-content/plugins/carei-reservation/assets/css/carei-reservation.css
+++ b/wp-content/plugins/carei-reservation/assets/css/carei-reservation.css
@@ -23,13 +23,14 @@
/* ═══════════════════════════════════════════
Trigger Button
═══════════════════════════════════════════ */
-.carei-reservation-trigger {
+.carei-reservation-trigger,
+button.carei-reservation-trigger {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 14px 28px;
- background-color: var(--carei-red);
- color: var(--carei-white);
+ background-color: var(--carei-blue);
+ color: var(--carei-white) !important;
font-family: var(--carei-font);
font-weight: 600;
font-size: 14px;
@@ -40,9 +41,10 @@
text-decoration: none;
line-height: 1;
}
-.carei-reservation-trigger:hover {
- background-color: var(--carei-red-hover);
- color: var(--carei-white);
+.carei-reservation-trigger:hover,
+button.carei-reservation-trigger:hover {
+ background-color: #1e1660;
+ color: var(--carei-white) !important;
}
.carei-reservation-trigger svg {
width: 16px;
@@ -76,13 +78,20 @@
max-width: 800px;
width: 100%;
max-height: 90vh;
- overflow-y: auto;
- overflow-x: hidden;
- padding: 40px 48px;
+ overflow: hidden;
position: relative;
font-family: var(--carei-font);
transform: scale(0.95) translateY(10px);
transition: transform 0.3s ease;
+ display: flex;
+ flex-direction: column;
+}
+.carei-modal__scroll {
+ overflow-y: auto;
+ overflow-x: hidden;
+ padding: 40px 48px;
+ flex: 1;
+ min-height: 0;
}
.carei-modal-overlay.is-open .carei-modal {
transform: scale(1) translateY(0);
@@ -115,13 +124,17 @@
}
/* Scrollbar styling */
-.carei-modal::-webkit-scrollbar {
+.carei-modal__scroll {
+ scrollbar-width: thin;
+ scrollbar-color: var(--carei-border) transparent;
+}
+.carei-modal__scroll::-webkit-scrollbar {
width: 6px;
}
-.carei-modal::-webkit-scrollbar-track {
+.carei-modal__scroll::-webkit-scrollbar-track {
background: transparent;
}
-.carei-modal::-webkit-scrollbar-thumb {
+.carei-modal__scroll::-webkit-scrollbar-thumb {
background: var(--carei-border);
border-radius: 3px;
}
@@ -575,11 +588,14 @@
.carei-form__extra-price {
font-weight: 400;
font-size: 14px;
- color: var(--carei-gray);
+ color: #505050;
margin-left: auto;
white-space: nowrap;
flex-shrink: 0;
}
+.carei-form__extra-price strong {
+ color: inherit;
+}
.carei-form__checkbox-label--abroad {
align-items: center;
@@ -763,7 +779,7 @@
display: inline-flex;
align-items: center;
gap: 8px;
- padding: 16px 32px;
+ padding: 16px 48px;
background-color: var(--carei-red);
color: var(--carei-white);
font-family: var(--carei-font);
@@ -894,10 +910,11 @@
max-width: 100%;
max-height: 100vh;
height: 100%;
- border-radius: var(--carei-radius-lg) var(--carei-radius-lg) 0 0;
- padding: 32px 24px;
border-radius: 0;
}
+ .carei-modal__scroll {
+ padding: 32px 24px;
+ }
.carei-form__row {
grid-template-columns: 1fr;
}
@@ -930,7 +947,7 @@
.carei-form__row--top {
grid-template-columns: 1fr;
}
- .carei-modal {
+ .carei-modal__scroll {
padding: 24px 16px;
}
}
@@ -1156,3 +1173,233 @@
.carei-success__close:hover {
background: var(--carei-red-hover);
}
+
+/* ═══════════════════════════════════════════
+ Search Form (Hero Mini Form)
+ ═══════════════════════════════════════════ */
+.carei-search-form {
+ background: var(--carei-bg);
+ border-radius: 14px;
+ border: 1px solid rgba(47, 36, 130, 0.1);
+ padding: 28px 24px 24px;
+ max-width: 422px;
+ font-family: var(--carei-font);
+ box-shadow: 0 4px 24px rgba(47, 36, 130, 0.08);
+}
+.carei-search-form__title {
+ font-family: var(--carei-font);
+ font-weight: 700;
+ font-size: 18px;
+ color: var(--carei-blue);
+ text-align: center;
+ margin: 0 0 20px;
+ line-height: 1.3;
+}
+.carei-search-form__title span {
+ color: var(--carei-red);
+}
+.carei-search-form__fields {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ margin-bottom: 16px;
+}
+.carei-search-form__row {
+ display: flex;
+ gap: 10px;
+}
+.carei-search-form__row > .carei-search-form__field {
+ flex: 1;
+ min-width: 0;
+}
+.carei-search-form__field--full {
+ width: 100%;
+}
+
+/* Select wrap */
+.carei-search-form__select-wrap {
+ position: relative;
+ display: flex;
+ align-items: center;
+ background: var(--carei-white);
+ border-radius: var(--carei-radius);
+ border: 1px solid #E0E0E0;
+ height: var(--carei-input-h);
+ transition: border-color 0.2s;
+}
+.carei-search-form__select-wrap:focus-within {
+ border-color: var(--carei-blue);
+}
+.carei-search-form__select-wrap select {
+ appearance: none;
+ -webkit-appearance: none;
+ border: none;
+ background: transparent;
+ font-family: var(--carei-font);
+ font-size: 14px;
+ color: var(--carei-gray);
+ padding: 0 32px 0 16px;
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+ outline: none;
+}
+.carei-search-form__select-wrap--icon select {
+ padding-left: 38px;
+}
+.carei-search-form__select-arrow {
+ position: absolute;
+ right: 12px;
+ pointer-events: none;
+ color: var(--carei-blue);
+}
+.carei-search-form__pin-icon {
+ position: absolute;
+ left: 14px;
+ pointer-events: none;
+ color: var(--carei-gray);
+}
+
+/* Date wrap */
+.carei-search-form__date-wrap {
+ position: relative;
+ display: flex;
+ align-items: center;
+ background: var(--carei-white);
+ border-radius: var(--carei-radius);
+ border: 1px solid #E0E0E0;
+ height: var(--carei-input-h);
+ transition: border-color 0.2s;
+}
+.carei-search-form__date-wrap:focus-within {
+ border-color: var(--carei-blue);
+}
+.carei-search-form__date-icon {
+ position: absolute;
+ left: 14px;
+ pointer-events: none;
+ color: var(--carei-gray);
+}
+.carei-search-form__date-label {
+ position: absolute;
+ left: 38px;
+ font-family: var(--carei-font);
+ font-size: 14px;
+ color: var(--carei-placeholder);
+ pointer-events: none;
+ transition: opacity 0.2s;
+}
+.carei-search-form__date-wrap.has-value .carei-search-form__date-label {
+ opacity: 0;
+}
+.carei-search-form__input--date {
+ appearance: none;
+ -webkit-appearance: none;
+ border: none;
+ background: transparent;
+ font-family: var(--carei-font);
+ font-size: 14px;
+ color: var(--carei-gray);
+ padding: 0 12px 0 38px;
+ width: 100%;
+ height: 100%;
+ outline: none;
+}
+.carei-search-form__date-wrap:not(.has-value) .carei-search-form__input--date {
+ color: transparent;
+}
+.carei-search-form__date-wrap:not(.has-value) .carei-search-form__input--date::-webkit-datetime-edit {
+ color: transparent;
+ -webkit-user-select: none;
+ user-select: none;
+}
+.carei-search-form__input--date::-webkit-calendar-picker-indicator {
+ opacity: 0;
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+}
+
+/* Checkbox */
+.carei-search-form__checkbox-label {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ cursor: pointer;
+ font-family: var(--carei-font);
+ font-size: 14px;
+ color: var(--carei-gray);
+ padding: 4px 0;
+}
+.carei-search-form__checkbox-label input[type="checkbox"] {
+ position: absolute;
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+.carei-search-form__checkbox-box {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 22px;
+ height: 22px;
+ border-radius: 4px;
+ border: 2px solid #D0D0D0;
+ background: var(--carei-white);
+ flex-shrink: 0;
+ transition: background-color 0.2s, border-color 0.2s;
+}
+.carei-search-form__checkbox-box svg {
+ display: none;
+}
+.carei-search-form__checkbox-label input[type="checkbox"]:checked + .carei-search-form__checkbox-box {
+ background: var(--carei-blue);
+ border-color: var(--carei-blue);
+}
+.carei-search-form__checkbox-label input[type="checkbox"]:checked + .carei-search-form__checkbox-box svg {
+ display: block;
+}
+
+/* Submit button */
+.carei-search-form__submit {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ width: 100%;
+ padding: 14px 20px;
+ background: var(--carei-red);
+ color: var(--carei-white);
+ font-family: var(--carei-font);
+ font-weight: 600;
+ font-size: 15px;
+ border: none;
+ border-radius: var(--carei-radius);
+ cursor: pointer;
+ transition: background-color 0.2s;
+ line-height: 1;
+}
+.carei-search-form__submit:hover {
+ background: var(--carei-red-hover);
+}
+.carei-search-form__submit svg {
+ width: 16px;
+ height: 16px;
+ flex-shrink: 0;
+}
+
+/* Responsive */
+@media (max-width: 767px) {
+ .carei-search-form {
+ max-width: 100%;
+ padding: 20px 16px 16px;
+ }
+}
+@media (max-width: 400px) {
+ .carei-search-form__row {
+ flex-direction: column;
+ }
+}
diff --git a/wp-content/plugins/carei-reservation/assets/js/carei-reservation.js b/wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
index 385ee83..791e121 100644
--- a/wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
+++ b/wp-content/plugins/carei-reservation/assets/js/carei-reservation.js
@@ -388,11 +388,11 @@
card.className = 'carei-form__extra-card';
card.innerHTML =
'';
+ '';
return card;
}
@@ -881,7 +881,11 @@
if (selectedExtras.length > 0) {
html += '
Wybrane opcje:
';
selectedExtras.forEach(function (ex) {
- html += '- ' + escHtml(ex.name) + ' — ' + fmtPrice(ex.priceAfterDiscount) + ' zł
';
+ var totalPrice = ex.priceAfterDiscount * (ex.amount || 1);
+ var priceInfo = ex.unit === 'doba' && ex.amount > 1
+ ? fmtPrice(ex.priceAfterDiscount) + ' zł/doba × ' + ex.amount + ' = ' + fmtPrice(totalPrice) + ' zł'
+ : fmtPrice(totalPrice) + ' zł';
+ html += '- ' + escHtml(toSentenceCase(ex.name)) + ' — ' + priceInfo + '
';
});
html += '
';
}
@@ -899,7 +903,7 @@
summary.pricelist.forEach(function (item) {
var rowClass = item.addedBySystem ? ' class="carei-summary__auto-item"' : '';
html += '' +
- '| ' + escHtml(item.name) + (item.addedBySystem ? ' (auto)' : '') + ' | ' +
+ '' + escHtml(toSentenceCase(item.name)) + (item.addedBySystem ? ' (auto)' : '') + ' | ' +
'' + (item.amount || 1) + ' ' + escHtml(item.unit || '') + ' | ' +
'' + fmtPrice(item.netValue) + ' | ' +
'' + fmtPrice(item.grossValue) + ' |
';
@@ -1078,11 +1082,160 @@
function escHtml(str) { var d = document.createElement('div'); d.textContent = str || ''; return d.innerHTML; }
function escAttr(str) { return (str || '').replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(//g, '>'); }
+ function toSentenceCase(str) { if (!str) return ''; var s = str.toLowerCase(); return s.charAt(0).toUpperCase() + s.slice(1); }
+
+ // ─── Search Form (Hero Mini Form) ──────────────────────────────
+
+ function initSearchForm() {
+ var searchForm = document.querySelector('.carei-search-form');
+ if (!searchForm) return;
+
+ var searchSegment = document.getElementById('carei-search-segment');
+ var searchDateFrom = document.getElementById('carei-search-date-from');
+ var searchDateTo = document.getElementById('carei-search-date-to');
+ var searchPickup = document.getElementById('carei-search-pickup');
+ var searchSameReturn = document.getElementById('carei-search-same-return');
+ var searchSubmit = document.getElementById('carei-search-submit');
+
+ var searchMapData = null;
+
+ // Ładowanie danych do mini formularza
+ function loadSearchData() {
+ Promise.all([
+ apiGet('car-classes-all'),
+ apiGet('segments-branches-map')
+ ]).then(function (results) {
+ var classes = results[0];
+ searchMapData = results[1];
+
+ if (Array.isArray(classes) && classes.length > 0) {
+ var segments = classes.map(function (c) {
+ var val = typeof c === 'string' ? c : (c.name || c);
+ var label = typeof c === 'string' ? ('Segment ' + c) : (c.description || c.name || c);
+ return { value: val, label: label };
+ });
+ populateSelect(searchSegment, segments, 'Wybierz segment');
+ }
+ if (searchPickup) {
+ populateSelect(searchPickup, [], 'Najpierw wybierz segment');
+ searchPickup.disabled = true;
+ }
+ }).catch(function (err) {
+ console.error('Search form: failed to load data:', err);
+ });
+ }
+
+ // Zmiana segmentu → filtr lokalizacji
+ if (searchSegment) {
+ searchSegment.addEventListener('change', function () {
+ var sel = searchSegment.value;
+ if (!sel || !searchMapData || !searchPickup) return;
+ var segBranches = searchMapData.segmentToBranches[sel] || [];
+ var allBranches = searchMapData.branches || [];
+ var opts = [];
+ allBranches.forEach(function (b) {
+ if (segBranches.indexOf(b.name || '') !== -1) {
+ var label = b.description || b.name;
+ if (b.city) label += ' — ' + b.city;
+ opts.push({ value: b.name, label: label });
+ }
+ });
+ if (opts.length > 0) {
+ populateSelect(searchPickup, opts, 'Miejsce odbioru');
+ searchPickup.disabled = false;
+ } else {
+ populateSelect(searchPickup, [], 'Brak lokalizacji');
+ searchPickup.disabled = true;
+ }
+ });
+ }
+
+ // Date label behavior
+ [searchDateFrom, searchDateTo].forEach(function (input) {
+ if (!input) return;
+ var wrap = input.closest('.carei-search-form__date-wrap');
+ function updateLabel() {
+ if (wrap) wrap.classList.toggle('has-value', !!input.value);
+ }
+ updateLabel();
+ input.addEventListener('change', updateLabel);
+ input.addEventListener('input', updateLabel);
+ });
+
+ // Submit → otwórz modal z pre-fill
+ if (searchSubmit) {
+ searchSubmit.addEventListener('click', function () {
+ if (!overlay) return;
+
+ var valSegment = searchSegment ? searchSegment.value : '';
+ var valDateFrom = searchDateFrom ? searchDateFrom.value : '';
+ var valDateTo = searchDateTo ? searchDateTo.value : '';
+ var valPickup = searchPickup ? searchPickup.value : '';
+ var valSameReturn = searchSameReturn ? searchSameReturn.checked : true;
+
+ // Otwórz modal
+ openModal(searchSubmit);
+
+ // Pre-fill po załadowaniu danych modala
+ function prefillModal() {
+ // Segment
+ if (segmentSelect && valSegment) {
+ segmentSelect.value = valSegment;
+ segmentSelect.dispatchEvent(new Event('change'));
+ }
+
+ // Daty
+ if (dateFrom && valDateFrom) {
+ dateFrom.value = valDateFrom;
+ dateFrom.dispatchEvent(new Event('change'));
+ }
+ if (dateTo && valDateTo) {
+ dateTo.value = valDateTo;
+ dateTo.dispatchEvent(new Event('change'));
+ }
+
+ // Checkbox zwrotu
+ if (sameReturnCheck) {
+ sameReturnCheck.checked = valSameReturn;
+ sameReturnCheck.dispatchEvent(new Event('change'));
+ }
+
+ // Pickup — poczekaj aż lokalizacje się załadują po change segmentu
+ if (valPickup && pickupSelect) {
+ var attempts = 0;
+ var pickupInterval = setInterval(function () {
+ attempts++;
+ // Sprawdź czy opcja jest dostępna
+ var optExists = Array.prototype.slice.call(pickupSelect.options).some(function (o) {
+ return o.value === valPickup;
+ });
+ if (optExists) {
+ clearInterval(pickupInterval);
+ pickupSelect.value = valPickup;
+ pickupSelect.dispatchEvent(new Event('change'));
+ } else if (attempts > 30) {
+ clearInterval(pickupInterval);
+ }
+ }, 100);
+ }
+ }
+
+ // Daj czas na loadInitialData w openModal
+ setTimeout(prefillModal, 400);
+ });
+ }
+
+ loadSearchData();
+ }
// ─── Init ─────────────────────────────────────────────────────
function init() {
initRefs();
+
+ // Inicjalizuj search form niezależnie od modala
+ initSearchForm();
+
if (!overlay || !form) return;
initModal();
diff --git a/wp-content/plugins/carei-reservation/carei-reservation.php b/wp-content/plugins/carei-reservation/carei-reservation.php
index 0518e3c..ab037db 100644
--- a/wp-content/plugins/carei-reservation/carei-reservation.php
+++ b/wp-content/plugins/carei-reservation/carei-reservation.php
@@ -85,6 +85,9 @@ add_action( 'plugins_loaded', function () {
add_action( 'elementor/widgets/register', function ( $widgets_manager ) {
require_once CAREI_RESERVATION_PATH . 'includes/class-elementor-widget.php';
$widgets_manager->register( new Carei_Reservation_Widget() );
+
+ require_once CAREI_RESERVATION_PATH . 'includes/class-search-widget.php';
+ $widgets_manager->register( new Carei_Search_Widget() );
} );
/**
diff --git a/wp-content/plugins/carei-reservation/includes/class-search-widget.php b/wp-content/plugins/carei-reservation/includes/class-search-widget.php
new file mode 100644
index 0000000..04f3c8b
--- /dev/null
+++ b/wp-content/plugins/carei-reservation/includes/class-search-widget.php
@@ -0,0 +1,102 @@
+
+
+