This commit is contained in:
2026-04-25 17:31:15 +02:00
parent 25fb0364ac
commit 973c98723f
8 changed files with 780 additions and 164 deletions

View File

@@ -0,0 +1,206 @@
---
phase: 04-products-aggregate-breakdown
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- autoload/factory/class.Products.php
- autoload/controls/class.Products.php
- templates/products/main_view.php
autonomous: false
delegation: off
---
<objective>
## Goal
Na stronie `/products` zmienic tryb listy tak, aby przy braku wybranej grupy reklam (`ad_group_id` puste/0) tabela pokazywala 1 wiersz glowny na produkt z metrykami zagregowanymi, oraz rozwijane podwiersze z pelnym rozbiciem na kampanie i grupy reklam.
## Purpose
Obecny widok miesza poziomy agregacji i utrudnia szybka analize produktu jako calosci. Wiersz glowny ma pokazywac wynik laczny produktu, a szczegoly (kampania/grupa) maja byc dostepne na zadanie przez rozwiniecie.
## Output
- Backend zwracajacy dane zagregowane per produkt oraz szczegoly per scope (kampania/grupa) dla aktualnej strony.
- Frontend DataTable z przyciskiem rozwin/zwin i podwierszem z pelnymi metrykami.
- Brak mozliwosci edycji w podwierszach (edycja tylko w wierszu glownym).
</objective>
<context>
## Project Context
@.paul/STATE.md
@.paul/phases/03-products-all-campaigns-view/03-01-SUMMARY.md
## Source Files
@autoload/factory/class.Products.php
@autoload/controls/class.Products.php
@templates/products/main_view.php
## Constraints from user
- Podwiersze maja byc zwijane/rozwijane przyciskiem.
- Podwiersze maja pokazywac pelny zestaw metryk.
- Edycja (`min_roas`, `CL1`, `CL4`, akcje) tylko w wierszu glownym produktu.
## Notes
Repo dziala w trybie ad-hoc PAUL (bez ROADMAP.md / PROJECT.md). Plan tworzony jako kolejna faza ad-hoc.
</context>
<acceptance_criteria>
## AC-1: Agregacja produktu przy braku wybranej grupy reklam
```gherkin
Given uzytkownik jest na `/products` i ma wybranego klienta
And filtr grupy reklam jest pusty (`#products_ad_group_id` = "")
When tabela laduje dane
Then kazdy produkt wystepuje tylko raz jako wiersz glowny
And metryki wiersza glownego (wyswietlenia, klikniecia, koszt, konwersje, wartosc konwersji, ROAS itd.) sa suma ze wszystkich pasujacych scope
```
## AC-2: Rozwijane podwiersze z pelnym rozbiciem
```gherkin
Given tabela produktow jest zaladowana w trybie z AC-1
When uzytkownik kliknie przycisk rozwin przy produkcie
Then pod wierszem glownym pojawia sie podwiersz(e) z rozbiciem per kampania+grupa reklam
And kazdy podwiersz pokazuje pelen zestaw kolumn metryk jak w widoku glownym (w formie tylko-do-odczytu)
And ponowne klikniecie przycisku zwija szczegoly
```
## AC-3: Edycja tylko w wierszu glownym
```gherkin
Given produkt ma rozwiniete podwiersze
When uzytkownik przeglada podwiersze
Then nie ma tam inputow ani przyciskow edycji/usuwania
And inputy `min_roas`, `custom_label_1`, `custom_label_4` oraz akcje pozostaja dostepne tylko w wierszu glownym produktu
```
## AC-4: Brak regresji dla filtrowania i wydajnosci listy
```gherkin
Given nowy tryb agregacji i podwierszy
When uzytkownik zmienia filtry (kampania, grupa, search, CL1, CL4), sortowanie i paginacje
Then tabela zachowuje poprawna liczbe rekordow i sortowanie
And przy wybranej konkretnej grupie reklam zachowanie pozostaje logicznie spojne (bez duplikacji i bez bledow JS/PHP)
And endpoint DataTables nie zwraca bledow SQL ani warningow
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Przebudowa warstwy danych na agregat produktu + szczegoly scope</name>
<files>autoload/factory/class.Products.php</files>
<action>
Rozszerzyc factory tak, aby `get_products()` zwracalo dane glownych wierszy agregowane per `p.id` przy `ad_group_id <= 0`, oraz by mozna bylo pobrac szczegoly per kampania/grupa dla zestawu `product_id` z biezacej strony.
Zakres:
- Utrzymac medoo/prepared statements.
- Dostosowac `GROUP BY` i logike licznikow tak, aby `recordsTotal` liczyl produkty, a nie scope, gdy nie ma wybranej grupy.
- Dodac/metody pomocnicze do pobrania breakdownu dla listy produktow bez N+1 (jedno zapytanie z `IN (...)` + grupowanie po stronie PHP).
- Utrzymac zgodnosc sortowania kolumn DataTables dla wiersza glownego.
Unikac:
- SQL sklejanych stringiem bez parametrow.
- Dublowania tej samej logiki obliczen metryk w wielu metodach bez wspolnego helpera.
</action>
<verify>
- `php -l autoload/factory/class.Products.php`
- Szybki test endpointu `/products/get_products/` dla: (a) ad_group_id="", (b) ad_group_id>0 i porownanie liczby rekordow.
- Brak warningow SQL / PHP w logu.
</verify>
<done>AC-1 i czesc AC-4 spelnione: poprawna agregacja i poprawne liczenie rekordow.</done>
</task>
<task type="auto">
<name>Task 2: Adaptacja kontrolera odpowiedzi DataTables pod dane glowne + breakdown</name>
<files>autoload/controls/class.Products.php</files>
<action>
W `get_products()` przebudowac format odpowiedzi JSON tak, aby kazdy wiersz glowny produktu zawieral identyfikator i dane potrzebne do renderu rozwijanych podwierszy (np. `row_meta` / `breakdown_rows`).
Zakres:
- Zachowac obecne formatowanie komorek glownych (ROAS bar, warningi, linki, akcje).
- Dla podwierszy zwracac pelne metryki, ale bez kontrolnych elementow edycji.
- Zachowac kompatybilnosc z istniejacymi handlerami JS, ktore operuja na kolumnach glownych.
Unikac:
- Wykonywania dodatkowego zapytania DB na kazdy pojedynczy wiersz.
- Mieszania HTML edycyjnego do danych podwierszy.
</action>
<verify>
- `php -l autoload/controls/class.Products.php`
- Odpowiedz endpointu zawiera dane breakdown tylko dla produktow z biezacej strony i nie psuje `recordsTotal`, `recordsFiltered`, `data`.
</verify>
<done>AC-2 i AC-3 backend-ready: API dostarcza dane do podwierszy i oddziela je od edycji.</done>
</task>
<task type="auto">
<name>Task 3: UI DataTable - przycisk rozwin i render podwierszy</name>
<files>templates/products/main_view.php</files>
<action>
Dodac w tabeli produktow obsluge rozwijanych podwierszy:
- Przycisk w wierszu glownym (rozwin/zwin) widoczny tylko gdy sa szczegoly.
- Render child-row (DataTables child row) z pelnym zestawem metryk dla kampania+grupa.
- Podwiersze readonly: bez inputow i bez akcji edycyjnych/usuwania.
- Zachowac obecne akcje edycyjne tylko dla glownego wiersza.
Dodatkowo:
- Dodac czytelne style dla child-row (wizualnie odroznione od glownej tabeli).
- Nie przekraczac 3 poziomow zagniezdzenia - wydzielic helpery JS do budowy HTML podwierszy i toggla.
</action>
<verify>
- `php -l templates/products/main_view.php`
- Manual: rozwin/zwin dziala, brak bledow w konsoli, sortowanie/paginacja dalej dziala.
</verify>
<done>AC-2, AC-3 i pozostala czesc AC-4 spelnione po stronie UI.</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>
Nowy widok listy produktow: glowny agregat per produkt + rozwijane podwiersze kampania/grupa z pelnymi metrykami, bez edycji w podwierszach.
</what-built>
<how-to-verify>
1. Otworz `/products`, wybierz klienta.
2. Ustaw kampanie dowolnie, ale pozostaw "Grupa reklam" na "- wszystkie grupy -".
3. Potwierdz: kazdy produkt jest raz jako wiersz glowny, metryki sa sumaryczne.
4. Kliknij przycisk rozwin przy kilku produktach.
5. Potwierdz: podwiersze pokazuja pelne metryki per kampania/grupa, bez inputow i bez akcji.
6. Zweryfikuj, ze edycja (`min_roas`, `CL1`, `CL4`) dziala tylko w wierszu glownym.
7. Sprawdz sortowanie, paginacje i filtry tekstowe.
8. Sprawdz konsolę przegladarki i logi PHP: brak nowych bledow.
</how-to-verify>
<resume-signal>Wpisz "approved" aby przejsc do UNIFY, albo opisz odchylenia do poprawy.</resume-signal>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- Migracje bazy i schemat tabel.
- Logika niezalezna od /products (inne kontrolery/widoki).
- Mechanika CSRF/sesji i routing aplikacji.
## SCOPE LIMITS
- Tylko zakladka `/products`.
- Tylko zachowanie listy i sposob prezentacji danych.
- Bez nowych funkcji biznesowych poza agregacja + podwiersze.
</boundaries>
<verification>
Before declaring plan complete:
- [ ] `php -l autoload/factory/class.Products.php`
- [ ] `php -l autoload/controls/class.Products.php`
- [ ] `php -l templates/products/main_view.php`
- [ ] Manualna weryfikacja AC-1..AC-4
- [ ] Brak nowych bledow JS/PHP/SQL
</verification>
<success_criteria>
- Widok przy pustym `ad_group_id` pokazuje agregat 1 produkt = 1 wiersz glowny.
- Podwiersze sa rozwijane przyciskiem i pokazuja pelne metryki per kampania/grupa.
- Edycja pozostaje tylko w wierszu glownym.
- Brak regresji filtrow, sortowania i paginacji.
</success_criteria>
<output>
After completion, create `.paul/phases/04-products-aggregate-breakdown/04-01-SUMMARY.md`.
</output>

View File

@@ -0,0 +1,138 @@
---
phase: 04-products-aggregate-breakdown
plan: 01
subsystem: ui
tags: [products, datatables, aggregation, breakdown, php, jquery]
requires:
- phase: 03-products-all-campaigns-view
provides: stable products scope filters and all-campaign selector behavior
provides:
- Aggregated product rows (1 product = 1 row when no ad group is selected)
- Expandable campaign/ad-group breakdown rows with full metrics
- Edit actions kept only on the main product row
affects:
- products table data contract from /products/get_products/
- products DataTable row rendering and child-row behavior
tech-stack:
added: []
patterns:
- aggregate main row + readonly scope breakdown in child row
key-files:
created: []
modified:
- autoload/factory/class.Products.php
- autoload/controls/class.Products.php
- templates/products/main_view.php
key-decisions:
- "Use one batch breakdown query for page product IDs to avoid N+1"
- "Expose breakdown as row_meta in DataTables payload"
- "Keep inline edit fields only in parent row"
patterns-established:
- "When scope is not narrowed to ad_group_id, aggregate per product and show scope details on demand"
duration: ~23min
started: 2026-04-25T17:05:05+02:00
completed: 2026-04-25T17:28:08+02:00
---
# Phase 4 Plan 01: Products Aggregate Breakdown (Summary)
**Na /products wdrozono model: glowny agregat per produkt + rozwijane readonly podwiersze kampania/grupa z pelnymi metrykami.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~23 min |
| Started | 2026-04-25T17:05:05+02:00 |
| Completed | 2026-04-25T17:28:08+02:00 |
| Tasks | 4/4 (3 auto + 1 human-verify) |
| Files modified | 3 |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Agregacja produktu bez wybranej grupy | Pass | Backend grupuje po `p.id`, recordsTotal liczy DISTINCT produktow |
| AC-2: Rozwijane podwiersze z pelnym rozbiciem | Pass | Child-row DataTables renderuje pelny zestaw metryk per kampania+grupa |
| AC-3: Edycja tylko w wierszu glownym | Pass | Podwiersze sa readonly; inputy i akcje zostaja w parent row |
| AC-4: Brak regresji filtrowania/listy | Pass | User checkpoint `approved`; endpoint i lintery bez bledow |
## Accomplishments
- Przebudowano zapytania listy produktow tak, aby glowny wynik byl agregowany per produkt.
- Dodano zbiorcze pobieranie breakdownu scope dla aktualnej strony (bez N+1).
- Rozszerzono payload DataTables o `row_meta.breakdown_rows` i dodano UI rozwin/zwin z tabela szczegolow.
## Task Commits
Brak commitu fazowego na tym etapie (working tree zawiera rowniez inne lokalne zmiany projektu).
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `autoload/factory/class.Products.php` | Modified (+148/-101) | Agregacja per produkt, wspolny filtr, breakdown query, records count |
| `autoload/controls/class.Products.php` | Modified (+53/-19) | Dolaczenie breakdownu do odpowiedzi i row_meta dla DataTables |
| `templates/products/main_view.php` | Modified (+162/-1) | UI toggle, child-row render, style breakdownu |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Batch breakdown query for page products | Ogranicza obciazenie DB i unika N+1 | Stabilna wydajnosc przy paginacji |
| Keep edit controls only in parent row | Zgodnosc z wymaganiem usera | Jasny podzial: edycja vs analiza |
| Render breakdown in DataTables child row | Minimalny blast radius dla istniejacego ukladu kolumn | Niska regresyjnosc UI |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 1 | Niski, bez zmiany zakresu |
| Scope additions | 0 | Brak |
| Deferred | 0 | Brak |
**Total impact:** Plan zrealizowany zgodnie z zakresem; jedna techniczna korekta podczas implementacji.
### Auto-fixed Issues
1. Tymczasowe uszkodzenie pliku kontrolera podczas edycji skryptowej
- Found during: Task 2
- Issue: `autoload/controls/class.Products.php` zostal chwilowo uszkodzony (parse error)
- Fix: przywrocenie pliku z HEAD i ponowne, punktowe patche
- Verification: `php -l autoload/controls/class.Products.php` OK
### Deferred Items
Brak.
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| Konflikty przy automatycznej podmianie wiekszego bloku kontrolera | Zmiana strategii na male, precyzyjne patche i ponowna walidacja |
## Next Phase Readiness
**Ready:**
- Kontrakt endpointu `/products/get_products/` obsluguje agregat + breakdown.
- UI listy wspiera drill-down bez naruszania obecnych akcji.
**Concerns:**
- W working tree sa tez niezalezne lokalne zmiany (`.vscode/ftp-kr.sync.cache.json`).
**Blockers:**
- Brak.
---
*Phase: 04-products-aggregate-breakdown, Plan: 01*
*Completed: 2026-04-25*