diff --git a/.paul/STATE.md b/.paul/STATE.md index 80479de..810b7c5 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -2,15 +2,15 @@ ## Current Position -Milestone: (ad-hoc) Supplemental feed — custom_label_1 -Phase: 2 of 2 (Supplemental Feed CL1) — Completed -Plan: 02-01 unified (loop closed) +Milestone: (ad-hoc) Products — widok "wszystkie kampanie" +Phase: 3 of 3 (Products All Campaigns View) — Completed +Plan: 03-01 unified (loop closed) Status: UNIFY complete -Last activity: 2026-04-22 — Zamknięto pętlę planu 02-01 +Last activity: 2026-04-24 — Zamknięto pętlę planu 03-01 Progress: - Milestone: [██████████] 100% -- Phase 2: [██████████] 100% +- Phase 3: [██████████] 100% ## Loop Position @@ -21,10 +21,10 @@ PLAN ──▶ APPLY ──▶ UNIFY ## Session Continuity -Last session: 2026-04-22 +Last session: 2026-04-24 Stopped at: Loop closed, pętla gotowa do nowego /paul:plan -Next action: Deploy na produkcję już wykonany; w razie kolejnego zadania — `/paul:plan` z opisem -Resume file: .paul/phases/02-supplemental-feed-cl1/02-01-SUMMARY.md +Next action: Wdróż `templates/products/main_view.php` na produkcję (FTP sync); w razie kolejnego zadania — `/paul:plan` z opisem +Resume file: .paul/phases/03-products-all-campaigns-view/03-01-SUMMARY.md ## Historia zrealizowanych planów @@ -36,9 +36,21 @@ Resume file: .paul/phases/02-supplemental-feed-cl1/02-01-SUMMARY.md - 1 plik zmodyfikowany: autoload/services/class.SupplementalFeed.php (+6/-4) - 4 AC spełnione - 0 odchyleń — plan 1:1 +- `03-01-PLAN.md` — Powrót do widoku "wszystkie kampanie" w /products (completed 2026-04-24) + - 1 plik zmodyfikowany: templates/products/main_view.php (+1/-3) + - Zmiana: usunięto `placeholder: '- wybierz -'` i `allowClear: true` z Select2 dla `#products_campaign_id` / `#products_ad_group_id` + - 4 AC spełnione + - 1 kosmetyczna różnica w verify (allowClear na linii 2097 to niezwiązany Google taxonomy picker, poza zakresem) + +## Decisions + +| Date | Decision | Phase | Impact | +|------|----------|-------|--------| +| 2026-04-24 | Usunięcie placeholdera zamiast wprowadzania wartości sentinel `0`/`all` — minimalny blast radius | 3 | 1-linijkowa zmiana w JS, brak zmian w kontrolerze/factory | ## Notes - PAUL framework działa w trybie ad-hoc (bez pełnej roadmapy/PROJECT.md). -- Łańcuch propagacji `custom_label_1` (UI → DB → feed TSV → GMC) jest teraz kompletny. -- Następne zmiany: uruchom `/paul:plan` z opisem kolejnego zadania. +- Backend `factory\Products::build_scope_filters` już dziś traktuje `campaign_id ≤ 0` jako "bez filtra" — fix był wyłącznie po stronie JS. +- Ustalony wzorzec: **Select2 + `` ⇒ NIE konfigurować `placeholder` ani `allowClear`**, żeby opcja pozostała widoczna w dropdownie po selekcji. +- Deploy: wymaga FTP sync szablonu + hard reload (Ctrl+Shift+R) w przeglądarce klientów. diff --git a/.paul/changelog/2026-04-24.md b/.paul/changelog/2026-04-24.md new file mode 100644 index 0000000..65809f3 --- /dev/null +++ b/.paul/changelog/2026-04-24.md @@ -0,0 +1,12 @@ +# 2026-04-24 + +## Co zrobiono + +- [Phase 3, Plan 01] Przywrócono widok "wszystkie kampanie" w filtrze na `/products` +- Usunięto `placeholder: '- wybierz -'` oraz `allowClear: true` z konfiguracji Select2 w `init_products_scope_select_search()` — opcja `` (oraz analogiczna dla grup reklam) znów jest widoczna w dropdownie po dowolnej selekcji +- Zweryfikowano 4 AC manualnie w przeglądarce (powrót do agregatu dla kampanii, dla grup reklam, persist po F5, brak regresji filtrów/sortowania/edycji) +- Żadnych zmian w backendzie — `factory\Products::build_scope_filters` już dziś obsługuje `campaign_id ≤ 0` jako "bez filtra" + +## Zmienione pliki + +- `templates/products/main_view.php` diff --git a/.paul/governance/governance_2026-04-24.jsonl b/.paul/governance/governance_2026-04-24.jsonl new file mode 100644 index 0000000..6aceb4d --- /dev/null +++ b/.paul/governance/governance_2026-04-24.jsonl @@ -0,0 +1,8 @@ +{"ts":"2026-04-24T20:59:59Z","tool":"Bash","cmd":"mkdir -p \".paul/phases/03-products-all-campaigns-view\"\",\"description\":\"Create phase directory\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"noOu","cwd":"/c/visual studio code/projekty/adsPRO"} +{"ts":"2026-04-24T21:01:21Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\phases\\\\03-products-all-campaigns-view\\\\03-01-PLAN.md","cwd":"/c/visual studio code/projekty/adsPRO"} +{"ts":"2026-04-24T21:01:39Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/adsPRO"} +{"ts":"2026-04-24T21:02:27Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\templates\\\\products\\\\main_view.php","cwd":"/c/visual studio code/projekty/adsPRO"} +{"ts":"2026-04-24T21:03:50Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/adsPRO"} +{"ts":"2026-04-24T21:05:08Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\phases\\\\03-products-all-campaigns-view\\\\03-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/adsPRO"} +{"ts":"2026-04-24T21:05:21Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\changelog\\\\2026-04-24.md","cwd":"/c/visual studio code/projekty/adsPRO"} +{"ts":"2026-04-24T21:05:43Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\adsPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/adsPRO"} diff --git a/.paul/phases/03-products-all-campaigns-view/03-01-PLAN.md b/.paul/phases/03-products-all-campaigns-view/03-01-PLAN.md new file mode 100644 index 0000000..a5e6505 --- /dev/null +++ b/.paul/phases/03-products-all-campaigns-view/03-01-PLAN.md @@ -0,0 +1,183 @@ +--- +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 +--- + + +## 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. + + + +## 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 `` (linia ~19) oraz `` (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. + + + + +## 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" +``` + + + + + + + Task 1: Naprawa konfiguracji Select2 w init_products_scope_select_search() + templates/products/main_view.php + + 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 `` (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 `')` przy każdym przeładowaniu. + + + 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. + + AC-1, AC-2 i AC-4 satysfakcjonowalne: opcja "wszystkie kampanie/grupy" widoczna w dropdownie po każdej selekcji; reszta przepływu bez regresji. + + + + + 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. + + + 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. + + Wpisz "approved" aby zamknąć plan, albo opisz problem do naprawienia (nowe odchylenie). + + + + + + +## 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 ``) — istniejące opcje `` 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`). + + + + +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) + + + +- 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. + + + +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`. + diff --git a/.paul/phases/03-products-all-campaigns-view/03-01-SUMMARY.md b/.paul/phases/03-products-all-campaigns-view/03-01-SUMMARY.md new file mode 100644 index 0000000..9bc8cb5 --- /dev/null +++ b/.paul/phases/03-products-all-campaigns-view/03-01-SUMMARY.md @@ -0,0 +1,141 @@ +--- +phase: 03-products-all-campaigns-view +plan: 01 +subsystem: ui +tags: [select2, jquery, products, filter, frontend] + +requires: + - phase: (none) + provides: istniejący szablon /products oraz backend z `build_scope_filters` obsługującym już campaign_id ≤ 0 + +provides: + - Widoczna opcja "- wszystkie kampanie -" w filtrze kampanii na /products po każdej selekcji + - Widoczna opcja "- wszystkie grupy -" w filtrze grup reklam na /products po każdej selekcji + - Przywrócony widok zagregowany bez konieczności czyszczenia localStorage / zmiany klienta + +affects: + - Każda przyszła zmiana w filtrach Select2 na /products (np. dodanie kolejnych filtrów ze scope) + - Ewentualne rozszerzenia filtrów analogicznych na innych stronach (sugerowane: /campaigns, jeśli używa podobnego wzorca Select2 z placeholderem + allowClear) + +tech-stack: + added: [] + patterns: + - "Select2 z pustą opcją default: nie używać `placeholder` + `allowClear` jednocześnie, jeśli pierwsza