update
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: 07-xml-feed-cron-refresh — Complete
|
Phase: 08-products-page-size — Complete
|
||||||
Plan: 07-01 complete
|
Plan: 08-01 complete
|
||||||
Status: UNIFY complete. Phase complete — ready for next phase.
|
Status: UNIFY complete. Phase complete — ready for next phase.
|
||||||
Last activity: 2026-04-30T07:19:05.331Z
|
Last activity: 2026-05-01T09:27:11.436Z
|
||||||
|
|
||||||
## Loop Position
|
## Loop Position
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-04-30
|
Last session: 2026-05-01
|
||||||
Stopped at: Plan 07-01 complete
|
Stopped at: Plan 08-01 complete
|
||||||
Next action: paul_workflow('plan') for next phase
|
Next action: paul_workflow('plan') for next phase
|
||||||
Resume file: .paul/phases/07-xml-feed-cron-refresh/07-01-SUMMARY.md
|
Resume file: .paul/phases/08-products-page-size/08-01-SUMMARY.md
|
||||||
@@ -1,59 +1,25 @@
|
|||||||
# STATE
|
|
||||||
|
|
||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Milestone: (ad-hoc) Products - XML feed import
|
Phase: 08-products-page-size — Apply complete
|
||||||
Phase: 7 of 7 (XML Feed Cron Refresh) - Planning
|
Plan: 08-01 executed
|
||||||
Plan: 07-01 created, awaiting approval
|
Status: APPLY complete — 2/2 PASS, ready for UNIFY
|
||||||
Status: APPLY complete — 3/3 PASS, ready for UNIFY
|
Last activity: 2026-05-01T10:59:46+02:00 — Completed APPLY for .paul/phases/08-products-page-size/08-01-PLAN.md
|
||||||
Last activity: 2026-04-30T09:20:00+02:00 - Created .paul/phases/07-xml-feed-cron-refresh/07-01-PLAN.md
|
|
||||||
|
|
||||||
Progress:
|
Progress:
|
||||||
- Milestone: [#########-] 90%
|
- Milestone: [□□□□□□□□□□] unknown (ROADMAP.md missing)
|
||||||
- Phase 7: [----------] 0%
|
- Phase 08: [████████░░] 80%
|
||||||
|
|
||||||
## Loop Position
|
## Loop Position
|
||||||
|
|
||||||
Current loop state:
|
Current loop state:
|
||||||
```
|
```
|
||||||
PLAN --> APPLY --> UNIFY
|
PLAN ──▶ APPLY ──▶ UNIFY
|
||||||
* o o [Plan created, awaiting approval]
|
✓ ✓ ○ [APPLY complete, awaiting UNIFY]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-04-30
|
Last session: 2026-05-01T10:59:46+02:00
|
||||||
Stopped at: Plan 07-01 created
|
Stopped at: APPLY complete for Plan 08-01
|
||||||
Next action: Review and approve plan, then run $paul-apply 07-01
|
Next action: Run $paul-unify .paul/phases/08-products-page-size/08-01-PLAN.md
|
||||||
Resume file: .paul/phases/07-xml-feed-cron-refresh/07-01-PLAN.md
|
Resume file: .paul/phases/08-products-page-size/08-01-PLAN.md
|
||||||
|
|
||||||
## Historia zrealizowanych planow
|
|
||||||
|
|
||||||
- `01-01-PLAN.md` - CL3 -> CL1 w tabeli /products (completed 2026-04-22)
|
|
||||||
- `02-01-PLAN.md` - custom_label_1 w supplemental feed TSV (completed 2026-04-22)
|
|
||||||
- `03-01-PLAN.md` - Powrot do widoku "wszystkie kampanie" w /products (completed 2026-04-24)
|
|
||||||
- `04-01-PLAN.md` - Agregat produktu + rozwijane podwiersze kampania/grupa w /products (completed 2026-04-25)
|
|
||||||
- `05-01-PLAN.md` - Usuwanie wpisow historii produktu per kampania+grupa z UI breakdown (completed 2026-04-29)
|
|
||||||
- `06-01-PLAN.md` - XML feed import + rename name/title + xml_feed_url w clients (completed 2026-04-30)
|
|
||||||
|
|
||||||
## Decisions
|
|
||||||
|
|
||||||
| Date | Decision | Phase | Impact |
|
|
||||||
|------|----------|-------|--------|
|
|
||||||
| 2026-04-24 | Usuniecie placeholdera zamiast sentinel `0/all` | 3 | Minimalny blast radius, bez zmian w kontrolerze/factory |
|
|
||||||
| 2026-04-25 | Bez wybranej grupy: glowny agregat per produkt + rozwijane podwiersze per kampania/grupa | 4 | Czytelniejsza analiza produktu i szybki drill-down |
|
|
||||||
| 2026-04-25 | Podwiersze tylko readonly, edycja tylko w parent row | 4 | Spojnosc UX i brak konfliktu akcji edycyjnych |
|
|
||||||
| 2026-04-29 | Usuwanie scope-level: products_aggregate + products_history + products_history_30 (transakcja), bez ruszania `products` ani Google Ads API | 5 | Hard delete lokalny, ograniczony do trojki product+campaign+ad_group |
|
|
||||||
| 2026-04-29 | UI: nowa kolumna "Akcje" w breakdown z czerwona ikona kosza + dialog $.confirm + ajax.reload(null,false) | 5 | Brak utraty stanu paginacji/filtrow przy odswiezaniu agregatu |
|
|
||||||
| 2026-04-30 | XML feed: jeden URL w clients.xml_feed_url, mapping po g:id=offer_id, parser XMLReader (streaming), batched manual upsert (200/batch) w transakcjach | 6 | Odporno na feedy z kilkoma tysiacami pozycji, niezalezne pola _gmc dla danych edytowalnych |
|
|
||||||
| 2026-04-30 | Refaktor schematu products: name->title, title->title_gmc, dodanie description_gmc i price | 6 | Rozdzial danych zrodlowych od edytowalnych |
|
|
||||||
| 2026-04-30 | Korekta semantyki: title/description = ZRODLOWE (z feedu lub pierwszy fetch GA), title_gmc/description_gmc = EDYTOWALNE (wysylane jako supplemental feed do GMC) | 6 | XmlFeedImporter pisze do title/description, edycja UI/AI pisze do _gmc, SupplementalFeed czyta z _gmc |
|
|
||||||
| 2026-04-30 | INDEX zamiast UNIQUE na (client_id, offer_id) | 6 | Istniejace dane mialy duplikaty - manual upsert (SELECT IN + UPDATE/INSERT) zamiast ON DUPLICATE KEY UPDATE |
|
|
||||||
| 2026-04-30 | XML feed ma byc osobnym cronem `/cron/cron_xml_feed_import`, niezaleznym od `cron_universal`, z recznym odswiezeniem w `/clients` | 7 | Czytelny harmonogram, brak dublowania importu z produktami Google Ads, latwiejsza diagnostyka feedu |
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- PAUL framework dziala tutaj w trybie ad-hoc (bez ROADMAP.md i PROJECT.md).
|
|
||||||
- Plan 06-01 zostal zamkniety mimo legacy duplikatow w products - przyszle zadanie deduplikacji moze wymusic UNIQUE.
|
|
||||||
- Backfill historycznych edycji `description` -> `description_gmc` jest deferred (czeka na decyzje uzytkownika).
|
|
||||||
- Plan 07-01 obejmuje wydzielenie XML feed z `cron_universal`, reczny refresh w `/clients` i diagnostyke `offer_id=2084` dla `pomysloweprezenty.pl`.
|
|
||||||
|
|||||||
12
.paul/changelog/2026-05-01.md
Normal file
12
.paul/changelog/2026-05-01.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# 2026-05-01
|
||||||
|
|
||||||
|
## Co zrobiono
|
||||||
|
|
||||||
|
- [08-products-page-size, Plan 01]
|
||||||
|
- Task 1: Wlacz wybor liczby produktow w DataTables
|
||||||
|
- Task 2: Ogranicz i ujednolic length w kontrolerze produktow
|
||||||
|
|
||||||
|
## Zmienione pliki
|
||||||
|
|
||||||
|
- `templates/products/main_view.php`
|
||||||
|
- `autoload/controls/class.Products.php`
|
||||||
151
.paul/phases/08-products-page-size/08-01-PLAN.md
Normal file
151
.paul/phases/08-products-page-size/08-01-PLAN.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
---
|
||||||
|
phase: 08-products-page-size
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: []
|
||||||
|
files_modified:
|
||||||
|
- templates/products/main_view.php
|
||||||
|
- autoload/controls/class.Products.php
|
||||||
|
autonomous: true
|
||||||
|
delegation: off
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
## Goal
|
||||||
|
Dodac w tabeli produktow na `/products` wybor liczby produktow wyswietlanych na stronie.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Uzytkownik ma sam decydowac, czy chce przegladac mniej rekordow szybciej, czy wiecej rekordow naraz bez przechodzenia po wielu stronach.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
Zmodyfikowana konfiguracja DataTables dla `#products` oraz bezpieczna obsluga parametru `length` w endpointzie `/products/get_products/`.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
<clarifications>
|
||||||
|
- **Doprecyzowanie** - Brak pytan doprecyzowujacych; wymaganie jest jednoznaczne po analizie kodu.
|
||||||
|
-> Odpowiedz: opcja wyboru liczby produktow na strone ma dotyczyc glownej tabeli produktow na `/products`.
|
||||||
|
</clarifications>
|
||||||
|
|
||||||
|
## Project Context
|
||||||
|
@.paul/STATE.md
|
||||||
|
@.paul/phases/07-xml-feed-cron-refresh/07-01-SUMMARY.md
|
||||||
|
|
||||||
|
## Source Files
|
||||||
|
@templates/products/main_view.php
|
||||||
|
@autoload/controls/class.Products.php
|
||||||
|
@autoload/factory/class.Products.php
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- `.paul/PROJECT.md` i `.paul/ROADMAP.md` nie istnieja w tym projekcie.
|
||||||
|
- `.paul/codebase/ARCHITECTURE` i `.paul/codebase/DB_SCHEMA` sa puste/szkieletowe.
|
||||||
|
- `.paul/SPECIAL-FLOWS.md` nie istnieje, wiec sekcja specjalnych skills jest pominieta.
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<acceptance_criteria>
|
||||||
|
|
||||||
|
## AC-1: Widoczny wybor liczby rekordow
|
||||||
|
```gherkin
|
||||||
|
Given uzytkownik jest na stronie /products
|
||||||
|
When tabela produktow zostanie zainicjalizowana
|
||||||
|
Then widzi kontrolke wyboru liczby produktow na strone
|
||||||
|
And domyslna wartosc pozostaje 25
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-2: Wybor zmienia limit tabeli
|
||||||
|
```gherkin
|
||||||
|
Given uzytkownik wybral klienta i tabela ma dane
|
||||||
|
When zmieni liczbe produktow na strone na jedna z dozwolonych wartosci
|
||||||
|
Then DataTables przeladowuje dane z nowa wartoscia length
|
||||||
|
And endpoint /products/get_products/ zwraca maksymalnie tyle rekordow, ile wybrano
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-3: Przegladarka pamieta wybor limitu
|
||||||
|
```gherkin
|
||||||
|
Given uzytkownik zmienil liczbe produktow na strone
|
||||||
|
When odswiezy strone /products albo wroci do niej pozniej w tej samej przegladarce
|
||||||
|
Then tabela startuje z ostatnio wybrana liczba produktow na strone
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-4: Limit jest bezpiecznie walidowany po stronie serwera
|
||||||
|
```gherkin
|
||||||
|
Given request do /products/get_products/ zawiera length spoza dozwolonego zakresu
|
||||||
|
When kontroler obsluguje request
|
||||||
|
Then uzywa bezpiecznej wartosci domyslnej albo maksymalnej
|
||||||
|
And nie przekazuje niekontrolowanego limitu do zapytania SQL
|
||||||
|
```
|
||||||
|
|
||||||
|
</acceptance_criteria>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Wlacz wybor liczby produktow w DataTables</name>
|
||||||
|
<files>templates/products/main_view.php</files>
|
||||||
|
<action>
|
||||||
|
Zmien konfiguracje `new DataTable( '#products', ... )`:
|
||||||
|
- ustaw `lengthChange: true`,
|
||||||
|
- zostaw domyslne `pageLength: 25`, jesli przegladarka nie ma zapisanego wyboru,
|
||||||
|
- dodaj `lengthMenu` z konserwatywnymi opcjami, np. 10, 25, 50, 100,
|
||||||
|
- dodaj polski tekst `language.lengthMenu`, np. "Pokaz _MENU_ produktow",
|
||||||
|
- zapisz wybrana wartosc w `localStorage` i uzyj jej przy ponownej inicjalizacji tabeli,
|
||||||
|
- nie zmieniaj definicji kolumn, filtracji, sortowania ani logiki reloadow.
|
||||||
|
</action>
|
||||||
|
<verify>php -l templates/products/main_view.php</verify>
|
||||||
|
<done>AC-1, AC-2 i AC-3 sa spelnione: kontrolka jest widoczna, domyslna wartosc to 25, DataTables wysyla wybrane `length`, a przegladarka pamieta wybor.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Ogranicz i ujednolic length w kontrolerze produktow</name>
|
||||||
|
<files>autoload/controls/class.Products.php</files>
|
||||||
|
<action>
|
||||||
|
W `Products::get_products()` dodaj lokalna walidacje parametru `length`:
|
||||||
|
- rzutuj wartosc z `\S::get( 'length' )` na int,
|
||||||
|
- dopusc tylko ustalone opcje z UI: 10, 25, 50, 100,
|
||||||
|
- dla braku lub niedozwolonej wartosci ustaw 25,
|
||||||
|
- zachowaj obecna obsluge `start`, sortowania, filtrow i wywolania `\factory\Products::get_products()`.
|
||||||
|
Unikaj zmiany zapytan SQL w `factory`, bo `get_products()` juz rzutuje `limit` i `start` na liczby.
|
||||||
|
</action>
|
||||||
|
<verify>php -l autoload/controls/class.Products.php</verify>
|
||||||
|
<done>AC-2 i AC-4 sa spelnione: endpoint honoruje dozwolone limity i ignoruje wartosci spoza listy.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<boundaries>
|
||||||
|
|
||||||
|
## DO NOT CHANGE
|
||||||
|
- Nie zmieniac schematu bazy ani migracji.
|
||||||
|
- Nie zmieniac logiki filtrow klient/kampania/grupa/CL1/CL4.
|
||||||
|
- Nie zmieniac kolumn, bulk delete, edycji produktu, historii produktu ani integracji AI.
|
||||||
|
- Nie zmieniac `autoload/factory/class.Products.php`, chyba ze podczas APPLY wyjdzie koniecznosc naprawy zwiazana bezposrednio z limitem.
|
||||||
|
|
||||||
|
## SCOPE LIMITS
|
||||||
|
- Zakres obejmuje tylko glowna tabele `#products` na `/products`.
|
||||||
|
- Nie dodawac globalnego ustawienia uzytkownika w bazie.
|
||||||
|
- Nie zmieniac paginacji tabeli historii produktu.
|
||||||
|
|
||||||
|
</boundaries>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Before declaring plan complete:
|
||||||
|
- [ ] `php -l templates/products/main_view.php`
|
||||||
|
- [ ] `php -l autoload/controls/class.Products.php`
|
||||||
|
- [ ] Manualnie na `/products`: kontrolka limitu jest widoczna i domyslnie ustawiona na 25.
|
||||||
|
- [ ] Manualnie na `/products`: wybor 10/25/50/100 przeladowuje tabele i zachowuje filtry.
|
||||||
|
- [ ] Manualnie na `/products`: po odswiezeniu strony tabela zachowuje ostatnio wybrany limit.
|
||||||
|
- [ ] All acceptance criteria met
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- Tabela produktow pozwala wybrac liczbe produktow na strone.
|
||||||
|
- Domyslnie pozostaje 25 produktow na strone.
|
||||||
|
- Przegladarka pamieta ostatnio wybrany limit tabeli produktow.
|
||||||
|
- Backend akceptuje tylko dozwolone limity.
|
||||||
|
- Brak nowych bledow PHP lint w modyfikowanych plikach.
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.paul/phases/08-products-page-size/08-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
37
.paul/phases/08-products-page-size/08-01-SUMMARY.md
Normal file
37
.paul/phases/08-products-page-size/08-01-SUMMARY.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
phase: 08-products-page-size
|
||||||
|
plan: 01
|
||||||
|
completed: 2026-05-01T09:27:11.436Z
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 08-01 Summary
|
||||||
|
|
||||||
|
****
|
||||||
|
|
||||||
|
## Acceptance Criteria Results
|
||||||
|
|
||||||
|
| Criterion | Status |
|
||||||
|
|-----------|--------|
|
||||||
|
| Task 1: Wlacz wybor liczby produktow w DataTables | Pass — Dodano wybor 10/25/50/100, domyslnie 25 z localStorage, zapis przez length.dt. Finalny uklad zrealizowany oficjalnym layout DataTables 2: info po lewej, paging + pageLength po prawej. Style w layout/style.scss i skompilowane do layout/style.css. Uzytkownik potwierdzil UAT: 'Teraz jest ok'. |
|
||||||
|
| Task 2: Ogranicz i ujednolic length w kontrolerze produktow | Pass — Kontroler /products/get_products/ akceptuje tylko 10/25/50/100, fallback 25. php -l autoload/controls/class.Products.php OK. |
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- Task 1: Wlacz wybor liczby produktow w DataTables: Dodano wybor 10/25/50/100, domyslnie 25 z localStorage, zapis przez length.dt. Finalny uklad zrealizowany oficjalnym layout DataTables 2: info po lewej, paging + pageLength po prawej. Style w layout/style.scss i skompilowane do layout/style.css. Uzytkownik potwierdzil UAT: 'Teraz jest ok'.
|
||||||
|
- Task 2: Ogranicz i ujednolic length w kontrolerze produktow: Kontroler /products/get_products/ akceptuje tylko 10/25/50/100, fallback 25. php -l autoload/controls/class.Products.php OK.
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
- `templates/products/main_view.php`
|
||||||
|
- `autoload/controls/class.Products.php`
|
||||||
|
- `layout/style.scss`
|
||||||
|
- `layout/style.css`
|
||||||
|
- `layout/style.css.map`
|
||||||
|
|
||||||
|
## Deviations
|
||||||
|
|
||||||
|
Plan poczatkowo zakladal tylko templates/products/main_view.php i autoload/controls/class.Products.php. W trakcie UAT dodano layout/style.scss oraz skompilowane layout/style.css i layout/style.css.map, bo wymaganie wygladu i pozycji kontrolki wymagalo stylowania. Finalnie usunieto obejscia DOM i zastosowano oficjalny layout DataTables 2.
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 08-products-page-size, Plan: 01*
|
||||||
|
*Completed: 2026-05-01*
|
||||||
@@ -918,7 +918,12 @@ class Products
|
|||||||
$client_id = \S::get( 'client_id' );
|
$client_id = \S::get( 'client_id' );
|
||||||
$campaign_id = (int) \S::get( 'campaign_id' );
|
$campaign_id = (int) \S::get( 'campaign_id' );
|
||||||
$ad_group_id = (int) \S::get( 'ad_group_id' );
|
$ad_group_id = (int) \S::get( 'ad_group_id' );
|
||||||
$limit = \S::get( 'length' ) ? \S::get( 'length' ) : 10;
|
$allowed_limits = [ 10, 25, 50, 100 ];
|
||||||
|
$limit = (int) \S::get( 'length' );
|
||||||
|
if ( !in_array( $limit, $allowed_limits, true ) )
|
||||||
|
{
|
||||||
|
$limit = 25;
|
||||||
|
}
|
||||||
$start = \S::get( 'start' ) ? \S::get( 'start' ) : 0;
|
$start = \S::get( 'start' ) ? \S::get( 'start' ) : 0;
|
||||||
|
|
||||||
$order_dir = $_POST['order'][0]['dir'] ? strtoupper( $_POST['order'][0]['dir'] ) : 'DESC';
|
$order_dir = $_POST['order'][0]['dir'] ? strtoupper( $_POST['order'][0]['dir'] ) : 'DESC';
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -851,7 +851,7 @@ table {
|
|||||||
border-top: 1px solid #F1F5F9;
|
border-top: 1px solid #F1F5F9;
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
display: none;
|
display: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2113,6 +2113,80 @@ table {
|
|||||||
background: $cWhite;
|
background: $cWhite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.products-dt-footer {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 14px 20px;
|
||||||
|
border-top: 1px solid #F1F5F9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.products-dt-info {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
color: #8899A6;
|
||||||
|
font-size: 13px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.products-dt-actions {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 14px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-paging {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-length {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
color: #64748B;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
select,
|
||||||
|
select.dt-input {
|
||||||
|
height: 36px;
|
||||||
|
min-width: 70px;
|
||||||
|
border: 1px solid $cBorder;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: $cWhite;
|
||||||
|
color: $cTextDark;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0 10px;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.2s, box-shadow 0.2s, background-color 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: $cPrimary;
|
||||||
|
background: #F8FAFC;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: $cPrimary;
|
||||||
|
box-shadow: 0 0 0 3px rgba($cPrimary, 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Przycisk usuwania w wierszu
|
// Przycisk usuwania w wierszu
|
||||||
@@ -3683,4 +3757,4 @@ table#products {
|
|||||||
.js-title-alt-apply {
|
.js-title-alt-apply {
|
||||||
color: #000;
|
color: #000;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -275,6 +275,8 @@ var AI_OPENAI_ENABLED = <?= $openai_enabled ? 'true' : 'false'; ?>;
|
|||||||
var AI_CLAUDE_ENABLED = <?= $claude_enabled ? 'true' : 'false'; ?>;
|
var AI_CLAUDE_ENABLED = <?= $claude_enabled ? 'true' : 'false'; ?>;
|
||||||
var AI_GEMINI_ENABLED = <?= $gemini_enabled ? 'true' : 'false'; ?>;
|
var AI_GEMINI_ENABLED = <?= $gemini_enabled ? 'true' : 'false'; ?>;
|
||||||
var PRODUCTS_COLUMNS_STORAGE_KEY = 'products.columns.visibility';
|
var PRODUCTS_COLUMNS_STORAGE_KEY = 'products.columns.visibility';
|
||||||
|
var PRODUCTS_PAGE_LENGTH_STORAGE_KEY = 'products.page.length';
|
||||||
|
var PRODUCTS_ALLOWED_PAGE_LENGTHS = [ 10, 25, 50, 100 ];
|
||||||
var PRODUCTS_LOCKED_COLUMNS = [ 0, 21 ];
|
var PRODUCTS_LOCKED_COLUMNS = [ 0, 21 ];
|
||||||
var products_bestseller_settings_loading = false;
|
var products_bestseller_settings_loading = false;
|
||||||
var products_bestseller_preview_timer = null;
|
var products_bestseller_preview_timer = null;
|
||||||
@@ -356,6 +358,30 @@ function products_storage_get( key )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function products_get_saved_page_length()
|
||||||
|
{
|
||||||
|
var page_length = parseInt( products_storage_get( PRODUCTS_PAGE_LENGTH_STORAGE_KEY ), 10 );
|
||||||
|
|
||||||
|
if ( PRODUCTS_ALLOWED_PAGE_LENGTHS.indexOf( page_length ) === -1 )
|
||||||
|
{
|
||||||
|
return 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
return page_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function products_save_page_length( page_length )
|
||||||
|
{
|
||||||
|
page_length = parseInt( page_length, 10 );
|
||||||
|
|
||||||
|
if ( PRODUCTS_ALLOWED_PAGE_LENGTHS.indexOf( page_length ) === -1 )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
products_storage_set( PRODUCTS_PAGE_LENGTH_STORAGE_KEY, page_length );
|
||||||
|
}
|
||||||
|
|
||||||
function products_is_locked_column( idx )
|
function products_is_locked_column( idx )
|
||||||
{
|
{
|
||||||
return PRODUCTS_LOCKED_COLUMNS.indexOf( Number( idx ) ) !== -1;
|
return PRODUCTS_LOCKED_COLUMNS.indexOf( Number( idx ) ) !== -1;
|
||||||
@@ -714,8 +740,30 @@ $( function()
|
|||||||
serverSide: true,
|
serverSide: true,
|
||||||
autoWidth: false,
|
autoWidth: false,
|
||||||
searching: false,
|
searching: false,
|
||||||
lengthChange: false,
|
lengthChange: true,
|
||||||
pageLength: 25,
|
lengthMenu: [ PRODUCTS_ALLOWED_PAGE_LENGTHS, PRODUCTS_ALLOWED_PAGE_LENGTHS ],
|
||||||
|
pageLength: products_get_saved_page_length(),
|
||||||
|
layout: {
|
||||||
|
topStart: null,
|
||||||
|
topEnd: null,
|
||||||
|
bottomStart: {
|
||||||
|
rowClass: 'products-dt-footer',
|
||||||
|
className: 'products-dt-info',
|
||||||
|
features: [ 'info' ]
|
||||||
|
},
|
||||||
|
bottomEnd: {
|
||||||
|
rowClass: 'products-dt-footer',
|
||||||
|
className: 'products-dt-actions',
|
||||||
|
features: [
|
||||||
|
'paging',
|
||||||
|
{
|
||||||
|
pageLength: {
|
||||||
|
menu: PRODUCTS_ALLOWED_PAGE_LENGTHS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{ width: '30px', orderable: false, className: 'select-checkbox', render: function( data, type, row ) {
|
{ width: '30px', orderable: false, className: 'select-checkbox', render: function( data, type, row ) {
|
||||||
return '<input type="checkbox" class="product-checkbox" value="' + row[1] + '" />';
|
return '<input type="checkbox" class="product-checkbox" value="' + row[1] + '" />';
|
||||||
@@ -748,9 +796,9 @@ $( function()
|
|||||||
{ width: '50px', name: 'cpc', className: "dt-type-numeric" },
|
{ width: '50px', name: 'cpc', className: "dt-type-numeric" },
|
||||||
{ width: '50px', name: 'conversions' },
|
{ width: '50px', name: 'conversions' },
|
||||||
{ width: '90px', name: 'conversions_value', className: "dt-type-numeric" },
|
{ width: '90px', name: 'conversions_value', className: "dt-type-numeric" },
|
||||||
{ width: '60px', name: 'roas' },
|
{ width: '50px', name: 'roas' },
|
||||||
{ width: '70px', name: 'min_roas' },
|
{ width: '70px', name: 'min_roas' },
|
||||||
{ width: '120px', name: 'custom_label_1' },
|
{ width: '180px', name: 'custom_label_1' },
|
||||||
{ width: '120px', orderable: false },
|
{ width: '120px', orderable: false },
|
||||||
{ width: '190px', orderable: false, className: 'dt-center' }
|
{ width: '190px', orderable: false, className: 'dt-center' }
|
||||||
],
|
],
|
||||||
@@ -766,6 +814,7 @@ $( function()
|
|||||||
emptyTable: 'Brak produktów do wyświetlenia',
|
emptyTable: 'Brak produktów do wyświetlenia',
|
||||||
info: 'Produkty _START_ - _END_ z _TOTAL_',
|
info: 'Produkty _START_ - _END_ z _TOTAL_',
|
||||||
infoEmpty: '',
|
infoEmpty: '',
|
||||||
|
lengthMenu: 'Pokaż _MENU_ produktów',
|
||||||
paginate: {
|
paginate: {
|
||||||
first: 'Pierwsza',
|
first: 'Pierwsza',
|
||||||
last: 'Ostatnia',
|
last: 'Ostatnia',
|
||||||
@@ -778,6 +827,10 @@ $( function()
|
|||||||
products_apply_saved_columns_visibility( products_table );
|
products_apply_saved_columns_visibility( products_table );
|
||||||
products_render_columns_picker( products_table );
|
products_render_columns_picker( products_table );
|
||||||
|
|
||||||
|
products_table.on( 'length.dt', function( e, settings, page_length ) {
|
||||||
|
products_save_page_length( page_length );
|
||||||
|
} );
|
||||||
|
|
||||||
$( '#products tbody' ).on( 'click', '.js-products-breakdown-toggle', function( e ) {
|
$( '#products tbody' ).on( 'click', '.js-products-breakdown-toggle', function( e ) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user