Files
adsPRO/.paul/phases/10-select2-global-search/10-01-PLAN.md
Jacek Pyziak a1fcbbd0d2 UI: globalny auto-init Select2 z wyszukiwarka dla selectow >4 opcji
- Nowy libraries/adspro-select2-autoinit.js (auto-init na document.ready + ajaxComplete debounce 150ms)
- Wyszukiwarka odblokowana dla "Grupa reklam", "Kampania", "Klient" na /campaign_terms (data-adspro-select2="true")
- Globalne style Select2 w layout/style.scss i style.css (uogolnione z .products-page)
- Usuniety duplikat: blok CSS .products-page .select2-* i funkcja init_products_scope_select_search() w products

PAUL: phase 10-select2-global-search complete (plan 10-01)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 16:13:06 +02:00

15 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, delegation
phase plan type wave depends_on files_modified autonomous delegation
10-select2-global-search 01 execute 1
libraries/adspro-select2-autoinit.js
templates/site/layout-logged.php
layout/style.scss
layout/style.css
templates/products/main_view.php
templates/campaign_terms/main_view.php
false off
## Goal Globalny auto-init Select2 dla wszystkich `` z >4 opcjami w aplikacji, ze spójnym stylem (jak na `/products`) i obsługą `allowClear` dla selectów z pustą opcją "- wszystkie / wybierz -". Usunięcie indywidualnych inicjalizacji Select2 tam, gdzie auto-init je zastąpi. Konkretny wyzwalacz: select "Grupa reklam" na `/campaign_terms`. Purpose Spójny UX wyszukiwania w długich listach (kampanie, grupy reklam, klienci) bez konieczności pamiętania o ręcznym $.fn.select2() w każdym module. Eliminuje duplikację CSS i kodu init w poszczególnych szablonach. Output Nowy plik libraries/adspro-select2-autoinit.js (auto-init + helper window.adsproSelect2Init) Załączenie go w templates/site/layout-logged.php po select2.full.min.js Globalne style Select2 w layout/style.scss (uogólnione z .products-page) Usunięcie redundantnego init i CSS z templates/products/main_view.php Wywołanie helpera po AJAX w templates/campaign_terms/main_view.php (jako fallback dla dynamicznie ładowanych opcji) ## Project Context @.paul/PROJECT.md @.paul/ROADMAP.md @.paul/STATE.md @.paul/codebase/architecture.md Source Files @templates/site/layout-logged.php @templates/products/main_view.php @templates/campaign_terms/main_view.php @layout/style.scss - **Zakres** — Globalny czy lokalny? → Odpowiedź: Globalny auto-init w layout; usunąć dotychczasowe indywidualne inicjalizacje gdzie auto-init je pokryje (zostawiamy specjalne przypadki jak googleCategory z `data` API i własnym placeholderem). - **Styl/UX** — allowClear i jednolity styl jak na /products? → Odpowiedź: Tak — allowClear włączony dla selectów z pustą opcją (value="") oraz styl uogólniony z `.products-page` na cały layout. - **Dynamika** — sposób obsługi dynamicznie ładowanych opcji? → Odpowiedź: Mniej problemów = minimalna zmiana. Auto-init oferuje helper `window.adsproSelect2Init($root)` wywoływany po AJAX-budowanym `` (campaign_terms). Dla statycznych selectów wystarcza `$(document).ready`.

<acceptance_criteria>

AC-1: Select "Grupa reklam" na /campaign_terms ma wyszukiwarkę

Given użytkownik jest na /campaign_terms i wybrał klienta + kampanię (załadowane >4 grupy reklam)
When kliknie w select "Grupa reklam"
Then otwiera się dropdown Select2 z polem wyszukiwania
And wpisanie fragmentu nazwy filtruje listę
And wybór grupy działa identycznie jak wcześniej (load_search_terms / load_negative)

AC-2: Globalny auto-init pokrywa wszystkie selecty z >4 opcjami

Given dowolna podstrona w layout-logged ma `<select>` z więcej niż 4 opcjami w `<option>`
When DOM jest gotowy (lub po AJAX dla dynamicznych)
Then ten select jest zainicjalizowany jako Select2 z polem wyszukiwania
And selecty z `≤ 4` opcjami pozostają natywne (chyba że mają atrybut `data-adspro-select2="true"`)
And selecty z klasą `no-select2` lub atrybutem `data-adspro-select2="false"` są pomijane

AC-3: AllowClear działa dla selectów z pustą opcją

Given Select2 jest zainicjalizowany na `<select>` którego pierwsza opcja ma value=""
When użytkownik wybrał wartość niepustą
Then widoczny jest przycisk X (clear)
When użytkownik kliknie X
Then wartość selecta wraca do "" i wyzwalany jest `change`

AC-4: Styl spójny z dotychczasowym wyglądem /products

Given Select2 jest aktywny na dowolnej stronie layout-logged
Then wysokość selecta = 38px, border-radius 6px, kolor focusa #6690f4
And styl dropdown, search input, highlighted option = jak obecnie na /products
And nie ma duplikacji CSS w `templates/products/main_view.php` (block `.products-page .select2-*` usunięty)

AC-5: Brak regresji indywidualnych Select2

Given dotychczasowe miejsca z ręcznym `$el.select2(...)` z własną konfiguracją (np. googleCategory z `data: cats`, placeholder, allowClear)
When strona się ładuje
Then nadal działają poprawnie (auto-init pomija już zainicjalizowane instancje przez sprawdzenie `$el.data('select2')`)
And `init_products_scope_select_search()` jest usunięte (zastąpione auto-initem), `update_delete_ad_group_button_state()` oraz `.trigger('change.select2')` nadal działają

</acceptance_criteria>

Task 1: Utworzyć moduł auto-init Select2 libraries/adspro-select2-autoinit.js Utworzyć nowy plik z modułem auto-init. Wymagania:
1. Sprawdza `typeof $.fn.select2 !== 'undefined'` przed działaniem.
2. Eksportuje `window.adsproSelect2Init($root)`:
   - `$root` opcjonalny (domyślnie `$(document.body)`)
   - Wyszukuje `select` w `$root` filtrując:
     * pomija `select[multiple]` z natywną logiką jeśli ma `data-adspro-select2="false"` (multiple jest OK domyślnie)
     * pomija `.no-select2` lub `[data-adspro-select2="false"]`
     * pomija jeśli `$el.data('select2')` już istnieje (już zainicjalizowane)
     * pomija jeśli select jest w `.select2-container` (wewnątrz Select2 wyświetlacza)
     * pomija jeśli rodzic ma klasę `.dataTables_length` (długość strony DataTables — krótka lista)
     * Inicjalizuje gdy: `select.options.length > 4` LUB ma atrybut `data-adspro-select2="true"`
   - Dla każdego pasującego selecta:
     * options.width = '100%'
     * options.allowClear = true jeśli pierwsza `<option>` ma value === ""
     * options.placeholder = tekst pierwszej pustej opcji (jeśli istnieje) lub atrybut `placeholder`
     * options.language = 'pl' jeśli dostępne
     * Wywołuje `$el.select2(options)`
     * Ustawia `$el.data('adspro-select2-ready', true)`
3. Na `$(document).ready` wywołuje `window.adsproSelect2Init()`.
4. Podłącza globalny `$(document).ajaxComplete` z debounce ~150ms wywołującym `window.adsproSelect2Init()` (żeby pokryć selecty budowane po AJAX, np. terms_ad_group_id, products_campaign_id).

Avoid:
- Nie inicjalizować na DataTables length-select (`.dataTables_length select`).
- Nie nadpisywać istniejących instancji Select2.
- Nie używać MutationObserver (overkill — `ajaxComplete` wystarczy w tej aplikacji).
Plik istnieje, `node -c` lub `php -r "echo file_exists('libraries/adspro-select2-autoinit.js') ? 'OK' : 'FAIL';"` zwraca OK; w przeglądarce DevTools → `typeof window.adsproSelect2Init === 'function'`. AC-2, AC-3 częściowo (silnik gotowy) Task 2: Załadować auto-init w layout i uogólnić CSS Select2 templates/site/layout-logged.php, layout/style.scss, layout/style.css 1. W `templates/site/layout-logged.php` po linii `<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>` (linia ~35) dodać: ```html <script src="/libraries/adspro-select2-autoinit.js"></script> ``` (Uwaga: w pliku są dwa źródła select2 — local i CDN. Auto-init dorzucamy raz, po obu.)
2. W `layout/style.scss` dodać nową sekcję `// === Select2 global overrides ===` z regułami zaadaptowanymi z `templates/products/main_view.php` linie 136-189. Selektory globalne (BEZ prefiksu `.products-page`):
   - `.select2-container` width 100%
   - `.select2-container--default .select2-selection--single` (height 38, border #e2e8f0, radius 6, bg #fff)
   - `.select2-selection__rendered` (line-height 36, padding 12/36, color #2d3748)
   - `.select2-selection__placeholder` (#6c757d)
   - `.select2-selection__arrow` (height 36, right 8)
   - `.select2-container--focus .select2-selection--single` (border #6690f4, shadow rgba(102,144,244,.1))
   - `.select2-dropdown` (border #e2e8f0, radius 6)
   - `.select2-search--dropdown .select2-search__field` (border, radius, height 34, font 14)
   - `.select2-results__option` (font 14)
   - `.select2-container--default .select2-results__option--highlighted[aria-selected]` (bg #6690f4, color #fff)

3. Skompilować SCSS do `layout/style.css` (lub dopisać te same reguły bezpośrednio do `layout/style.css` jeśli SCSS nie jest auto-budowane w projekcie — sprawdzić nagłówek pliku style.css czy ma marker auto-generated). Jeżeli style.css jest commitowane ręcznie, dopisać identyczny blok CSS na końcu pliku style.css.

Avoid:
- Nie usuwać żadnych innych istniejących reguł z style.scss/style.css.
- Nie zmieniać kolejności ładowania Select2 CSS (local + CDN — obecny stan zachować).
Po odświeżeniu dowolnej strony layout-logged: w DevTools `$('.select2-container').length > 0` na stronach z natywnymi selectami z >4 opcjami; computed style selecta = height 38px. AC-2, AC-4 Task 3: Usunąć redundantne Select2 w products i podłączyć helper w campaign_terms templates/products/main_view.php, templates/campaign_terms/main_view.php **W `templates/products/main_view.php`:** 1. Usunąć blok CSS `.products-page .select2-*` (linie ~136-189) — zastąpiony przez globalne reguły z Task 2. 2. Usunąć funkcję `init_products_scope_select_search()` (linie ~610-631) ORAZ jej wywołanie w linii ~723. - Jeśli wywołanie jest częścią innego bloku, zostawić sąsiednie linie nietknięte; tylko skreślić wywołanie funkcji. 3. NIE ruszać `$googleCategory.select2({...})` w linii ~2414 (ma własny `data:`, `placeholder`, `allowClear` — auto-init go pominie bo będzie już zainicjalizowany przed/po; dodać przed tym wywołaniem klasę `no-select2` na `$googleCategory` LUB zostawić — auto-init nie nadpisuje istniejących instancji, więc bezpiecznie). 4. Zachować wszystkie `$x.trigger('change.select2')` (linie ~1112, 1133, 1159, 1178) — nadal aktualne.
**W `templates/campaign_terms/main_view.php`:**
1. W funkcji `load_campaigns()` po linii `$campaign_select.val( restore_campaign_id );` (~1791) i ogólnie po wszystkich append() w success: dodać:
   ```js
   if ( typeof window.adsproSelect2Init === 'function' ) window.adsproSelect2Init( $campaign_select.parent() );
   $campaign_select.trigger( 'change.select2' );
   ```
2. W funkcji `load_ad_groups()` po pętli `ad_groups.forEach` (po ~1738): analogicznie dla `$ad_group_select`.
3. Dla `#terms_client_id` (jeśli ma >4 opcji od razu po PHP render) auto-init z `$(document).ready` go pokryje — nie trzeba zmian; ale dla pewności dodać atrybut `data-adspro-select2="true"` w `<select id="terms_client_id" ...>` (~linia 9) żeby gwarantować init niezależnie od liczby opcji.
4. Dla `#terms_campaign_id` i `#terms_ad_group_id` w HTML (~linie 18, 24) dodać `data-adspro-select2="true"` — jawnie wymuszają Select2 nawet gdy chwilowo mają tylko placeholder.

Avoid:
- Nie zmieniać logiki `terms_storage_set/get`.
- Nie zmieniać struktury DataTables ani handlerów `change` na selectach.
- Nie usuwać innych stylów `.products-page *` poza blokiem Select2.
1. Strona `/products` — wybór kampanii i grupy reklam działa, wyszukiwarka obecna, wygląd niezmieniony. 2. Strona `/campaign_terms` — po wyborze klienta + kampanii select "Grupa reklam" otwiera dropdown z wyszukiwarką; filtrowanie po wpisaniu fragmentu działa; wybór wczytuje termy. 3. `git diff templates/products/main_view.php` pokazuje usunięcie bloków CSS i funkcji init. AC-1, AC-5 Globalny auto-init Select2 + jednolity styl + wyszukiwarka w "Grupa reklam" na /campaign_terms. 1. Otwórz `https://adspro.projectpro.pl/campaign_terms`. 2. Wybierz klienta, potem kampanię — poczekaj na załadowanie grup reklam. 3. Kliknij select "Grupa reklam" → powinien pokazać się dropdown Select2 z polem wyszukiwania na górze. 4. Wpisz fragment nazwy grupy — lista filtruje się na żywo. 5. Wybierz grupę → tabela "Search terms" / "Negative" przeładowuje się jak wcześniej. 6. Kliknij X (jeśli pierwsza opcja "- wszystkie grupy -") → wraca do "- wszystkie grupy -". 7. Przejdź na `/products` — sprawdź że wybór kampanii i grupy reklam nadal działa (regresja). 8. Przejdź na kilka innych podstron (/campaigns, /clients, /feeds) — sprawdź czy żaden select nie wygląda zepsuto. 9. Sprawdź w DevTools console: brak błędów JS po załadowaniu strony i po zmianach w selectach. Wpisz "approved" aby kontynuować lub opisz problemy do naprawy

DO NOT CHANGE

  • libraries/select2/** — biblioteka Select2 (nie modyfikujemy źródeł).
  • Logika AJAX endpointów /campaign_terms/* i /products/* (tylko frontend).
  • $googleCategory.select2(...) w products/main_view.php (~2414) — działa z własną konfiguracją.
  • Inne miejsca używające .trigger('change.select2') — zachować.
  • Backend (controls/factory/services) — bez zmian.

SCOPE LIMITS

  • Tylko layout-logged (panel zalogowanego użytkownika). Nie ruszamy layout-unlogged (/login itp.).
  • Nie wprowadzamy nowych bibliotek ani nie aktualizujemy wersji Select2.
  • Nie zmieniamy multi-select (<select multiple>) — auto-init obsługuje je tak samo (Select2 ma natywne wsparcie), ale nie dodajemy zaawansowanych opcji typu tagi.
  • Brak nowych migracji DB, brak zmian w PHP poza ewentualnymi atrybutami HTML w templatach.
Before declaring plan complete: - [ ] Plik `libraries/adspro-select2-autoinit.js` istnieje i jest podłączony w layout-logged.php - [ ] Globalne style Select2 w `layout/style.css` - [ ] Blok `.products-page .select2-*` usunięty z `templates/products/main_view.php` - [ ] Funkcja `init_products_scope_select_search()` i jej wywołanie usunięte - [ ] `data-adspro-select2="true"` na 3 selectach w `templates/campaign_terms/main_view.php` - [ ] `window.adsproSelect2Init(...)` wywoływane po load_campaigns/load_ad_groups w campaign_terms - [ ] Brak błędów JS w konsoli na /campaign_terms i /products - [ ] Checkpoint human-verify zaakceptowany

<success_criteria>

  • Wszystkie zadania ukończone
  • Wszystkie kryteria akceptacji spełnione
  • Brak regresji wizualnej/funkcjonalnej na /products
  • Wyszukiwarka działa na /campaign_terms → Grupa reklam (i na każdym innym selecie z >4 opcjami w layout-logged) </success_criteria>
After completion, create `.paul/phases/10-select2-global-search/10-01-SUMMARY.md`