---
phase: 03-products-all-campaigns-view
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - templates/products/main_view.php
autonomous: false
delegation: off
---

<objective>
## Goal
Przywrócić na stronie `/products` możliwość wyboru widoku "wszystkie kampanie" (agregacji) po wcześniejszym wybraniu konkretnej kampanii w filtrze `#products_campaign_id`. Analogicznie dla filtru grup reklam `#products_ad_group_id`.

## Purpose
Obecnie po wybraniu dowolnej kampanii użytkownik nie widzi już opcji "- wszystkie kampanie -" w rozwijanej liście i nie może wrócić do widoku zbiorczego (ani zrestartować filtra grupy reklam). Jedynym sposobem jest zmiana klienta albo ręczne wyczyszczenie `localStorage`. To psuje podstawowy przepływ pracy na liście produktów.

## Output
Zmodyfikowany plik `templates/products/main_view.php` — zmiana konfiguracji Select2 w `init_products_scope_select_search()` tak, aby opcja z pustą wartością ("- wszystkie kampanie -" / "- wszystkie grupy -") była stale widoczna w rozwijanej liście i dała się kliknąć w dowolnym momencie.
</objective>

<context>
## Project Context
@.paul/STATE.md
@CLAUDE.md

## Source Files
@templates/products/main_view.php
@autoload/controls/class.Products.php
@autoload/factory/class.Products.php

## Prior Work
Nie wymaga wczytywania poprzednich SUMMARY — ten plan nie jest zależny od milestone'a CL1. Dotyczy innej warstwy (UI filtra) tej samej strony `/products`, co plany 01-01 i 02-01.

## Kluczowe fakty z analizy
- Szablon `templates/products/main_view.php` już zawiera `<option value="">- wszystkie kampanie -</option>` (linia ~19) oraz `<option value="">- wszystkie grupy -</option>` (linia ~27).
- `init_products_scope_select_search()` (linia ~510) inicjuje Select2 z `placeholder: '- wybierz -'` i `allowClear: true`. Zgodnie z zachowaniem Select2, opcja z pustą wartością jest traktowana jako placeholder i ukrywana z dropdownu, co uniemożliwia jej ponowne wybranie po zmianie selekcji.
- Backend (`factory\Products::build_scope_filters`, linia 470) filtruje po kampanii / grupie reklam TYLKO gdy `campaign_id > 0` / `ad_group_id > 0`. Pusty `campaign_id` już teraz daje widok zbiorczy — zmiana wyłącznie po stronie JS.
- AJAX DataTable (`/products/get_products/`, linia ~537) wysyła `d.campaign_id = $('#products_campaign_id').val() || ''` — pusty string trafia do kontrolera, `(int)''` = 0, więc zagregowany widok działa bez zmian w kontrolerze.
- Pozostałe funkcje (`load_scope_alerts`, `load_zero_impressions_products`, `update_delete_ad_group_button_state`) poprawnie krótko-spięte dla pustego `campaign_id` — wyłączają swoje panele, co jest pożądanym zachowaniem w widoku zbiorczym.
</context>

<acceptance_criteria>

## AC-1: Powrót do widoku "wszystkie kampanie" po wyborze kampanii
```gherkin
Given użytkownik jest na /products i wybrał klienta z co najmniej jedną kampanią
  And wybrał konkretną kampanię w filtrze "Kampania"
  And tabela produktów pokazuje wyniki przefiltrowane do tej kampanii
When użytkownik rozwija dropdown "Kampania"
Then w liście wyboru widzi pozycję "- wszystkie kampanie -"
  And po jej kliknięciu wartość `#products_campaign_id` staje się pusta
  And tabela `#products` przeładowuje się i pokazuje produkty ze wszystkich kampanii klienta (zagregowane po `products_aggregate` dla całego `client_id`)
  And panele boczne "Produkty do sprawdzenia" oraz alerty zakresu (scope alerts) są ukryte (bo wymagają konkretnej kampanii)
```

## AC-2: Powrót do widoku "wszystkie grupy" po wyborze grupy reklam
```gherkin
Given użytkownik ma wybraną kampanię oraz konkretną grupę reklam w filtrze "Grupa reklam"
When użytkownik rozwija dropdown "Grupa reklam"
Then w liście wyboru widzi pozycję "- wszystkie grupy -"
  And po jej kliknięciu wartość `#products_ad_group_id` staje się pusta
  And tabela `#products` przeładowuje się i pokazuje produkty ze wszystkich grup reklam wybranej kampanii
  And przycisk "Usuń grupę reklam" (`#delete-products-ad-group`) jest nieaktywny
```

## AC-3: Stan zachowany w localStorage po odświeżeniu strony
```gherkin
Given użytkownik wrócił do "wszystkich kampanii" (pusty `#products_campaign_id`)
When odświeży stronę (`F5`)
Then filtr "Kampania" pokazuje "- wszystkie kampanie -" jako aktualną wartość
  And filtr "Grupa reklam" pokazuje "- wszystkie grupy -"
  And tabela `#products` od razu ładuje zagregowany widok bez błędów konsoli
```

## AC-4: Brak regresji dla istniejących ścieżek
```gherkin
Given strona /products po zmianie
When użytkownik wybierze klienta → kampanię → grupę reklam → produkt
Then wszystkie istniejące przepływy działają jak dotychczas:
  - filtry tekstowe (search, CL1, CL4) aktualizują tabelę
  - sortowanie kolumn, paginacja DataTables działają
  - edycja `custom_label_1` / `custom_label_4`, `min_roas` zapisuje się
  - "Usuń grupę reklam" działa wyłącznie dla kampanii SHOPPING z wybraną grupą
  - brak regresji w alertach zakresu i panelu "Produkty do sprawdzenia"
```

</acceptance_criteria>

<tasks>

<task type="auto">
  <name>Task 1: Naprawa konfiguracji Select2 w init_products_scope_select_search()</name>
  <files>templates/products/main_view.php</files>
  <action>
    W funkcji `init_products_scope_select_search()` (około linii 510–527) zmień konfigurację Select2 dla selektorów `#products_campaign_id` i `#products_ad_group_id`, aby opcja z pustą wartością była dostępna do wyboru w dropdownie.

    Zmiana:
    - Usuń parametry `placeholder: '- wybierz -'` oraz `allowClear: true`. Select2 bez `placeholder` nie promuje pierwszej pustej opcji do roli placeholdera — dzięki czemu `<option value="">- wszystkie kampanie -</option>` (i odpowiednio "- wszystkie grupy -") pozostaje normalną pozycją listy, klikalną po selekcji innej opcji.
    - Pozostaw `width: '100%'` oraz `data( 'products-select2-ready', true )` bez zmian.
    - Nie dodawaj `minimumResultsForSearch` — domyślna wyszukiwarka wewnątrz dropdownu dalej jest pożądana przy większej liczbie kampanii.

    Dlaczego dokładnie tak:
    - Dokumentacja Select2: "If you do not specify a placeholder, a clear option will not be shown" — usunięcie placeholdera jest jednym krokiem, który przywraca widoczność pustej opcji i zarazem neutralizuje wymóg `allowClear`.
    - Nie trzeba zmieniać wartości `<option value="">` w HTML-u, ponieważ reszta JS-a (`$('#products_campaign_id').val() || ''`, `load_products_ad_groups`, `load_scope_alerts`, `localStorage.setItem`) traktuje pusty string jako "wszystkie" i działa poprawnie.

    Czego unikać:
    - Nie wprowadzaj `value="0"` dla opcji "wszystkie" — dodałoby to niepotrzebną gałąź w logice porównań w `load_products_campaigns` (`if (selected_campaign_id && ...)`) i mogłoby namieszać w `get_selected_products_campaign_channel_type`.
    - Nie zmieniaj handlerów `change` (`#client_id`, `#products_campaign_id`, `#products_ad_group_id`) — ich aktualne zachowanie przy pustej wartości jest już poprawne (czyszczą localStorage grupy, przeładowują tabelę, chowają panele boczne).
    - Nie ruszaj `load_products_campaigns()` / `load_products_ad_groups()` — już dzisiaj same dodają `.append('<option value="">…</option>')` przy każdym przeładowaniu.
  </action>
  <verify>
    1. Otwórz plik `templates/products/main_view.php`, znajdź funkcję `init_products_scope_select_search` — potwierdź, że blok `$select.select2({...})` zawiera tylko `width: '100%'`.
    2. `php -l "templates/products/main_view.php"` → "No syntax errors detected".
    3. Szybki `grep -n "placeholder: '- wybierz -'" templates/products/main_view.php` → brak wyników.
    4. Szybki `grep -n "allowClear" templates/products/main_view.php` → brak wyników.
  </verify>
  <done>AC-1, AC-2 i AC-4 satysfakcjonowalne: opcja "wszystkie kampanie/grupy" widoczna w dropdownie po każdej selekcji; reszta przepływu bez regresji.</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <what-built>
    Naprawa filtra kampanii i grup reklam na stronie /products: przywrócenie widoczności opcji "- wszystkie kampanie -" oraz "- wszystkie grupy -" w rozwijanej liście Select2 po wcześniejszej selekcji konkretnej wartości.
  </what-built>
  <how-to-verify>
    1. Wdróż zmianę na `https://adspro.projectpro.pl` (lub lokalne środowisko z tą bazą).
    2. Otwórz `https://adspro.projectpro.pl/products` (Hard Reload: Ctrl+Shift+R, aby odświeżyć cache JS).
    3. Wybierz klienta, który ma co najmniej 2 kampanie Shopping/PMax z produktami.
    4. AC-1: Rozwiń filtr "Kampania", wybierz konkretną kampanię → tabela pokazuje produkty tej kampanii. Rozwiń filtr ponownie → pierwsza pozycja listy to "- wszystkie kampanie -". Kliknij ją → tabela przeładowuje się i pokazuje zagregowane produkty wszystkich kampanii klienta; panele "Produkty do sprawdzenia" i alerty są ukryte.
    5. AC-2: Wybierz kampanię → wybierz konkretną grupę reklam → tabela ją filtruje. Rozwiń filtr "Grupa reklam" → pozycja "- wszystkie grupy -" jest widoczna i klikalna; po wybraniu tabela wraca do widoku wszystkich grup kampanii, a przycisk "Usuń grupę reklam" jest wyszarzony.
    6. AC-3: Ustaw filtr w trybie "wszystkie kampanie" → wykonaj F5. Po reloadzie dropdowny pokazują "- wszystkie kampanie -" / "- wszystkie grupy -" i tabela ładuje zagregowany widok bez błędów w konsoli (F12 → Console).
    7. AC-4 (regresje):
       - Wyszukiwarka (`#products_search`) + filtry CL1/CL4 działają w widoku "wszystkich kampanii" oraz w widoku konkretnej kampanii.
       - Sortowanie kolumn (np. ROAS, clicks_30) + paginacja DataTables nie wyrzucają błędów.
       - Edycja inline `custom_label_1`, `custom_label_4` i `min_roas` poprawnie zapisuje wartości.
       - "Usuń grupę reklam" działa dla kampanii SHOPPING z wybraną grupą.
    8. Sprawdź konsolę przeglądarki — nie powinno być błędów `Select2: The placeholder must be specified...` ani innych ostrzeżeń Select2.
  </how-to-verify>
  <resume-signal>Wpisz "approved" aby zamknąć plan, albo opisz problem do naprawienia (nowe odchylenie).</resume-signal>
</task>

</tasks>

<boundaries>

## DO NOT CHANGE
- `autoload/controls/class.Products.php` — kontroler już poprawnie obsługuje pusty `campaign_id` / `ad_group_id`.
- `autoload/factory/class.Products.php` (w tym `build_scope_filters`, `get_products`, `get_records_total_products`, `get_roas_bounds`) — logika agregacji dla `client_id` bez filtrów kampanii jest sprawdzona i nie wymaga zmian.
- HTML strony (sekcje `<select id="products_campaign_id">` i `<select id="products_ad_group_id">`) — istniejące opcje `<option value="">…</option>` pozostają.
- Handlery `change` dla `#client_id`, `#products_campaign_id`, `#products_ad_group_id` — ich logika z pustą wartością jest już poprawna.
- `load_products_campaigns()`, `load_products_ad_groups()`, `load_scope_alerts()`, `load_zero_impressions_products()` — bez zmian.
- localStorage keys (`products_campaign_id`, `products_ad_group_id`) — zachowujemy istniejący format (pusty string = "wszystkie").
- Migracje SQL — brak zmian w schemacie.

## SCOPE LIMITS
- Nie dodajemy nowych filtrów na stronie /products.
- Nie zmieniamy stylistyki Select2 (CSS `.products-page .select2-*`).
- Nie wprowadzamy sentineli `0`/`all` dla wartości "wszystkie" — świadomy wybór, żeby minimalizować blast radius.
- Nie dotykamy widoku `products/product_history.php` ani analogicznych filtrów na innych stronach (np. `/campaigns`).

</boundaries>

<verification>
Przed uznaniem planu za zamknięty:
- [ ] `php -l templates/products/main_view.php` → no syntax errors
- [ ] `grep -n "placeholder: '- wybierz -'" templates/products/main_view.php` → brak trafień
- [ ] `grep -n "allowClear" templates/products/main_view.php` → brak trafień
- [ ] Brak błędów w konsoli przeglądarki na /products po zmianie
- [ ] Checkpoint human-verify odhaczony (AC-1…AC-4 zweryfikowane ręcznie)
</verification>

<success_criteria>
- Użytkownik może z dowolnej selekcji w filtrze "Kampania" wrócić do widoku zbiorczego "- wszystkie kampanie -" jednym kliknięciem w dropdown.
- To samo dla filtra "Grupa reklam".
- Brak regresji w istniejących przepływach edycji, sortowania, usuwania grup reklam, AI suggestions, merchant sync i filtrów tekstowych.
- Zmiana ograniczona do jednego pliku (`templates/products/main_view.php`), bez nowych zależności, bez migracji DB.
</success_criteria>

<output>
Po zakończeniu utwórz `.paul/phases/03-products-all-campaigns-view/03-01-SUMMARY.md` opisujący faktyczne zmiany (diff + ew. odchylenia od planu) i wynik testów manualnych z `checkpoint:human-verify`.
</output>
