feat(new-layout): add-to-cart handler + piece configurator (Phase 02 plans 01-02)
Plan 02-01 (piece/crop configurator, complete):
- #piece reuse z shared partial product-cover-thumbnails.tpl
- 8 hidden inputs (is_crop, crop_pos_x/y, crop_width/height, piece_bg_top/left, is_reflection) w formie #add-to-cart-or-refresh
- Defensive setup w custom.js: setTimeout(600) init, no-op override totalpriceinfospecific/prod, DOM stubs
- CSS scope pod body#product .product-size-data .product-size-data--new
Plan 02-02 (add-to-cart submission, PARTIAL):
- Capture-phase native addEventListener (useCapture=true) blokuje PS core crash
(button poza formą w nowym layoucie — closest('form') zwracało 0)
- Manualny AJAX POST: form.serialize() + qty + add=1&action=update do /pl/koszyk
- Fancybox-blocker port z custom.js:327 (nie odpalał się bo selector 0 matches)
- Manual sync is_crop/crop_width/height przed POST (obejście crash checkedHandler)
- prestashop.emit('updatedCart') + defensive blockcart refresh fetch
- Loading spinner + success flash CSS
- Inline handler mirror w product.tpl z idempotency guard (window.__p02p02Bound)
— cache-buster dla browser cachowanego custom.js
Deferred do Plan 02-03 (customization + modal blocker dla production):
- Customization nie zapisuje się (squaremeter hook gate'owany discretion=on + brak dimension fields)
- Success modal (wymaga POST do /module/ps_shoppingcart/ajax)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
35
.paul/PROJECT.md
Normal file
35
.paul/PROJECT.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# PROJECT.md
|
||||
|
||||
## Name
|
||||
newwalls.pl — PrestaShop 1.7 (theme: ayon)
|
||||
|
||||
## Mission
|
||||
Sklep z tapetami na wymiar. Klient prowadzi migrację wyglądu strony produktu na nowy layout. Do czasu zakończenia testów nowy layout jest warunkowany IP administratora.
|
||||
|
||||
## Core Constraints
|
||||
- Stary i nowy wygląd strony produktu współegzystują w `themes/ayon/templates/catalog/product.tpl`:
|
||||
- Stary layout: `{if $smarty.server.REMOTE_ADDR != '89.69.31.86'} ... {/if}`
|
||||
- Nowy layout: `{if $smarty.server.REMOTE_ADDR == '89.69.31.86'} ... {/if}`
|
||||
- Nie wolno modyfikować starego layoutu (produkcja działa dla zwykłych użytkowników).
|
||||
- Partial `themes/ayon/templates/catalog/_partials/product-variants.tpl` jest współdzielony — zmiany w nim muszą być zgodne z oboma layoutami.
|
||||
- Stack: PrestaShop 1.7, Smarty, jQuery, SCSS (`themes/ayon/assets/css/custom.scss` → `custom.css`).
|
||||
|
||||
## Value Proposition
|
||||
Nowy layout strony produktu ma dać czystszy, bardziej prezentowalny UI konfiguratora tapety przy zachowaniu dotychczasowej funkcjonalności (wybór wariantu kolorystycznego, wymiary, dodanie do koszyka itd.).
|
||||
|
||||
## Known Broken After Redesign
|
||||
|
||||
### ✅ Naprawione (Phase 01)
|
||||
- `.product-variants` (wariant kolorystyczny) — wygląd grid 3×1 wg Figma 27:9867 + klik zmienia wariant in-place (AJAX `action=refresh` + `history.pushState`).
|
||||
|
||||
### ⚠️ Do naprawy (Phase 02+)
|
||||
- Brak `<form id="add-to-cart-or-refresh">` / `.product-actions` wokół `product_add_to_cart` w nowym layoucie — „Dodaj do koszyka" prawdopodobnie nie działa.
|
||||
- Puste bloki `<div class="product-box--data"></div>`: `product-size-data`, `product-protect`, `product-installation`, `product-order-sample`.
|
||||
- Konfigurator rozmiaru „piece" (crop + odbicie lustrzane) — brak markup'u.
|
||||
- Brakujące elementy dla pełnego PS `updatedProduct` flow (`.product-cover-thumbnails`, `.js-product-images2-modal`, `.product-details`, `.product-customization`, `.product-additional-info`) — wpływa na edge case'y przy zmianie wariantu z różnymi miniaturami/opisami.
|
||||
|
||||
## Established Patterns (Phase 01)
|
||||
- **Scoped CSS under `body#product .product-variants-data--new`** — izoluje zmiany nowego layoutu od globalnych reguł i starego widoku.
|
||||
- **Własny AJAX refresh w `custom.js`** — `action=refresh` (nie `productrefresh`), POST na `window.location.href.split('?')[0].split('#')[0]`, `dataType: 'json'`, header `Accept: application/json`.
|
||||
- **In-place DOM update**: `history.pushState(resp.product_url)` + `$('.product-prices-data .product-prices').replaceWith(resp.product_prices)` + `$('.product_image_wrapper').html(resp.product_cover_thumbnails)` + `prestashop.emit('updatedProduct', resp)`. Fallback na `window.location.reload()` przy błędzie.
|
||||
- **Pipeline SCSS → CSS**: edytuj tylko `themes/ayon/assets/css/custom.scss`, `custom.css` auto-generowany przez user'a watcher (feedback memory).
|
||||
35
.paul/ROADMAP.md
Normal file
35
.paul/ROADMAP.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# ROADMAP.md
|
||||
|
||||
## Milestone v0.1 — Naprawa funkcji w nowym layoucie strony produktu
|
||||
|
||||
Status: In progress (Phase 1 / 2 complete)
|
||||
|
||||
Cel: przywrócić pełną funkcjonalność strony produktu w nowym wyglądzie (IP 89.69.31.86) bez naruszania starego.
|
||||
|
||||
### Phase 01 — Product variants (wariant kolorystyczny) — **✅ Complete (2026-04-23)**
|
||||
- Wizualnie: blok `.product-variants` w `.product-box.product-variants-data` wyglada jak `d:\temp\Frame 33.png` / Figma 27:9867 (3 kafelki z obrazem + podpis pod spodem, responsive 2×1 < 768 px). ✓
|
||||
- Funkcjonalnie: kliknięcie miniatury zmienia wariant produktu — AJAX `action=refresh` + in-place DOM update (obraz + cena + URL przez `history.pushState`), bez reloadu. ✓
|
||||
- Dotyczy tylko gałęzi `REMOTE_ADDR == '89.69.31.86'` w `product.tpl` oraz scope'owanego CSS/JS. Stary layout nietknięty.
|
||||
- Summary: `.paul/phases/01-product-variants-fix/01-01-SUMMARY.md`
|
||||
|
||||
### Phase 02 — Product actions fixes (piece + add-to-cart + empty blocks) — **Planning**
|
||||
|
||||
Plan 02-01 (in planning): Konfigurator „piece" (crop + odbicie lustrzane) w nowym layoucie — port funkcjonalności ze starego layoutu z zachowaniem identycznego kontraktu DOM (hidden inputs `is_crop`, `crop_pos_x/y`, `crop_width/height`, `piece_bg_top/left`) dla serializacji do koszyka. Drag + resize przez inputy + mirror, re-init po AJAX refresh wariantu. Scope limit: zero nowych zależności, zero zmian w starym layoucie.
|
||||
- Plan file: `.paul/phases/02-product-actions-fixes/02-01-PLAN.md`
|
||||
|
||||
Plan 02-02 (**PARTIAL — closed 2026-04-23**): Add-to-cart submission działa (POST → cart saves item), ale customization nie zapisuje się i success modal nie pojawia. Core infrastructure: capture-phase native handler w custom.js + inline mirror w product.tpl (cache-buster), idempotency guard, blockcart refresh. 5 AC pass (technicznie), ale feature niekompletne z perspektywy UX.
|
||||
- Plan file: `.paul/phases/02-product-actions-fixes/02-02-PLAN.md`
|
||||
- Summary: `.paul/phases/02-product-actions-fixes/02-02-SUMMARY.md`
|
||||
|
||||
Plan 02-03 (**wymagany dla production** — kolejny do zaplanowania): Customization + success modal + cena per-sqm + systemowy cache-buster.
|
||||
- Root cause: Plan 02-01 override `totalpriceinfospecific` na no-op wyłączył squaremeter flow → customization hook `hookActionObjectCartUpdateBefore` gate'owany `discretion=on` + wiele pól (`dim`, `qty`, `qty_alth`, `product_total_price_calc`, `extrafeevalue`, `wastevalue`, `calculated_total`, etc.) nie jest wypełnianych.
|
||||
- Zakres:
|
||||
1. Przywrócić squaremeter dimension flow w nowym layoucie (alternatywa dla `totalpriceinfospecific`) — UI dimension input'ów + live kalkulacja + synchronizacja hidden inputs.
|
||||
2. Cena per-sqm kalkulacja (zależna od #1).
|
||||
3. Success modal po add-to-cart przez POST do `/module/ps_shoppingcart/ajax?action=add-to-cart` → render modal (resp.modal HTML).
|
||||
4. Systemowy cache-buster `?v=<mtime>` dla `<script src=custom.js>` — znaleźć miejsce rejestracji (theme.yml ma puste assets). Pozwoli wycofać inline mirror z product.tpl.
|
||||
- Blocker dla publikacji nowego layoutu: TAK (produkcja wymaga modal + customization detail w koszyku).
|
||||
|
||||
Kolejne plany w Phase 02 (po 02-03):
|
||||
- Plan 02-04+ — Puste bloki `.product-protect`, `.product-installation`, `.product-order-sample` + potencjalnie brakujące elementy PS `updatedProduct` flow (`.js-product-images2-modal`, `.product-customization`, itd.)
|
||||
- Opcjonalny add-on — resize handles bezpośrednio na `#piece` (bonus feature, user zgłosił w rozmowie).
|
||||
89
.paul/STATE.md
Normal file
89
.paul/STATE.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# STATE.md
|
||||
|
||||
## Current Position
|
||||
|
||||
Milestone: v0.1 Naprawa nowego layoutu strony produktu
|
||||
Phase: 2 of 2 (Product actions fixes) — Planning
|
||||
Plan: 02-03 (customization save + success modal) created, awaiting approval
|
||||
Status: PLAN created. Plan 02-02 zamknięty PARTIAL, Plan 02-03 fokus na end-to-end UX.
|
||||
Last activity: 2026-04-23 — Created `.paul/phases/02-product-actions-fixes/02-03-PLAN.md` — scope: customization + modal (cena + cache-buster wydzielone do 02-04/02-05)
|
||||
|
||||
Progress:
|
||||
- Milestone: [█████████░] 85% (Phase 1 zamknięty; Phase 2 plan 02-01 zamknięty, plan 02-02 PARTIAL, plan 02-03 w PLAN)
|
||||
- Phase 1: [██████████] 100%
|
||||
- Phase 2: [██████████░] 70% (plan 02-01 closed, plan 02-02 PARTIAL, plan 02-03 w PLAN stage; 02-04/02-05 do zaplanowania)
|
||||
|
||||
**UWAGA:** Milestone NIE jest ready na usunięcie IP gate (`REMOTE_ADDR == '89.69.31.86'`). Plan 02-03 jest kluczowy dla production readiness.
|
||||
|
||||
## Loop Position
|
||||
|
||||
Current loop state:
|
||||
```
|
||||
PLAN ──▶ APPLY ──▶ UNIFY
|
||||
✓ ○ ○ [Plan 02-03 created, awaiting approval]
|
||||
```
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-04-23
|
||||
Stopped at: Plan 02-03 PLAN.md created
|
||||
Next action: Review plan `.paul/phases/02-product-actions-fixes/02-03-PLAN.md`, potwierdzić scope (customization + modal, bez ceny i cache-busta), następnie `/paul:apply .paul/phases/02-product-actions-fixes/02-03-PLAN.md`
|
||||
Resume file: `.paul/phases/02-product-actions-fixes/02-03-PLAN.md`
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
### Decisions (Phase 01)
|
||||
|
||||
| Data | Decyzja | Wpływ |
|
||||
|---|---|---|
|
||||
| 2026-04-23 | Rozszerzenie scope Task 1 o wrapper `<form id="add-to-cart-or-refresh">` | Bez niego PS nie może AJAX-ować wariantu — konieczne do realizacji AC-2 |
|
||||
| 2026-04-23 | Ręczny AJAX w custom.js zamiast fake `.product-actions` | PS core handler szuka formy przez `.product-actions` — unikam dodawania problematycznej klasy (kolizja stylów), piszę własny flow |
|
||||
| 2026-04-23 | Edytuj tylko `custom.scss`, nie `custom.css` | User ma własny watcher SCSS — manualne zmiany w CSS są nadpisywane |
|
||||
| 2026-04-23 | In-place DOM update zamiast full redirect | User feedback "na starym layoucie działało bez reloadu" — `history.pushState` + manual replace `.product-prices` / `.product_image_wrapper` |
|
||||
| 2026-04-23 | `action=refresh` (nie `productrefresh`) | Empiryczne probowanie endpointów — `productrefresh` zwracał pustą odpowiedź |
|
||||
|
||||
### Decisions (Phase 02 — Plan 02-01)
|
||||
|
||||
| Data | Decyzja | Wpływ |
|
||||
|---|---|---|
|
||||
| 2026-04-23 | Priorytet: piece/crop przed add-to-cart | User zmienił priorytety z ROADMAP — konfigurator fragmentu krytyczny dla produktu („na wymiar") |
|
||||
| 2026-04-23 | `#piece` reużywany z shared partial `product-cover-thumbnails.tpl` | Pierwotna próba dodania osobnego `<div id="piece">` tworzyła duplikat ID — rozwiązanie: reuse istniejącego + inherit stylowania ze starego SCSS |
|
||||
| 2026-04-23 | Override `totalpriceinfospecific` + `prod` na no-op w nowym layoucie | ⚠️ **WYWOŁAŁO regresję squaremeter customization flow**: hook wymaga `discretion=on` + dimension fields ktore były synchronizowane przez tę funkcję. Plan 02-02 ujawnił że customization nie zapisuje się. Do fix'u w Plan 02-03. |
|
||||
| 2026-04-23 | DOM stubs wstrzykiwane przez JS (nie template) | Smarty template cache + FTP sync delay — JS deploy'uje się niezawodniej. |
|
||||
| 2026-04-23 | Piece NIE auto-init'uje się na load — pojawia się tylko po kliknięciu trigger'a popupu | User feedback w trakcie checkpoint — zmiana wymaganej behawiorystyki vs pierwotny plan |
|
||||
| 2026-04-23 | Defensive `.pp_stick_parent` guard w fancybox handler | Element istnieje tylko w starym layoucie — bez guard'a popup aborts przed `$.fancybox()` w nowym |
|
||||
|
||||
### Decisions (Phase 02 — Plan 02-02)
|
||||
|
||||
| Data | Decyzja | Wpływ |
|
||||
|---|---|---|
|
||||
| 2026-04-23 | Plan 02-02 = submission + cart widget (bez ceny) | Cena wydzielona do Plan 02-03 dla scope 2-3 task. W trakcie APPLY odkryto że customization + modal TEŻ są w Plan 02-03. |
|
||||
| 2026-04-23 | Task 1 = live diagnosis (checkpoint:human-verify) | Playwright ujawnił: button+qty są POZA formą `#add-to-cart-or-refresh` — PS core `closest('form')` zwraca 0, POST nigdy nie wychodzi. |
|
||||
| 2026-04-23 | Wybór S3 (własny AJAX submit handler) | S2 (restrukturyzacja form) naruszałoby Bootstrap grid + shared partial risk. S3 bezpieczniejsze. |
|
||||
| 2026-04-23 | Capture-phase native addEventListener (useCapture=true) zamiast jQuery `.on()` | Pierwsza iteracja dubluje POST (PS core i nasz oba firują). Capture phase fires PRZED bubble — całkowicie blokuje PS core. Verified. |
|
||||
| 2026-04-23 | Manual blockcart refresh po emit('updatedCart') | Natural PS listener nie odświeżał widget w nowym layoucie. Defensive $.get fallback działał. |
|
||||
| 2026-04-23 | Inline script mirror w product.tpl + idempotency guard | Browser cache serwuje old custom.js (transferSize=0). Inline HTML response zawsze fresh. Guard `window.__p02p02Bound` chroni przed double-register. |
|
||||
| 2026-04-23 | UNIFY Plan 02-02 jako PARTIAL | Core POST działa ale: (a) customization nie zapisuje się — squaremeter hook gate'owany `discretion=on` z brakującymi polami, (b) success modal brak — wymaga osobny POST do `/module/ps_shoppingcart/ajax`. Delegowane Plan 02-03. |
|
||||
|
||||
### Open observations (do kolejnych planów Phase 02)
|
||||
|
||||
- **Plan 02-03 (wymagany dla production) — Customization + modal + cena + cache-buster:**
|
||||
- Przywrócić squaremeter dimension flow zamiast Plan 02-01 no-op override (`totalpriceinfospecific` / `prod`). Synchronizować pola: `discretion=on`, `dim`, `qty`, `qty_alth`, `product_total_price_calc`, `extrafeevalue`, `wastevalue`, `calculated_total`, `grand_calculated_total`, `converted_ea`, `directinput`, `qty_alt`, `qty_altd`, `width_module`, `height_module`, `unittype_module`.
|
||||
- Po udanym POST do `/koszyk` → drugi POST do `/module/ps_shoppingcart/ajax?action=add-to-cart` → pokazać modal (resp.modal HTML) z "Kontynuuj zakupy / Przejdź do koszyka".
|
||||
- Cena per-sqm calculation w UI (live update przy zmianie piece dimensions / qty).
|
||||
- Systemowy cache-buster `?v=<mtime>` dla `<script src=custom.js>` tag'a — znaleźć miejsce rejestracji (theme.yml ma puste assets — rejestracja via hook/override do znalezienia). Pozwoli wycofać inline mirror z product.tpl.
|
||||
- **Plan 02-04+ — Puste bloki**: `.product-protect`, `.product-installation`, `.product-order-sample` — wypełnić treścią.
|
||||
- **Re-verify po Plan 02-01** (przed git commit phase'a):
|
||||
- AC-5: stary layout (IP != 89.69.31.86) bez regresji
|
||||
- AC-4: zmiana wariantu kolorystycznego → AJAX refresh → `#piece` re-rendered → dragElement re-binds
|
||||
- Mirror button end-to-end w nowym layoucie
|
||||
- Piece-size-view "50x50" placeholder po kliknieciu popup (cosmetic).
|
||||
- Resize handles bezpośrednio na `#piece` (deferred bonus).
|
||||
|
||||
### Skill audit
|
||||
|
||||
SPECIAL-FLOWS.md nadal nie skonfigurowane (rekomendacja z 02-01 niespełniona). Plan 02-02 podkreślił krytyczność Playwright MCP (diagnoza struktury DOM, event phases, customization flow — niemożliwe bez live debug).
|
||||
|
||||
**Pattern Ustalony:** Structure-first diagnosis przed implementacją — sprawdzić DOM relationships, event flow, POST payload requirements. Oszczędza iteracje.
|
||||
|
||||
**Rekomendacja:** Utworzyć SPECIAL-FLOWS.md z Playwright MCP jako required skill dla Plan 02-03+ — customization flow wymaga end-to-end testów POST → DB state → UI re-render.
|
||||
35
.paul/changelog/2026-04-23.md
Normal file
35
.paul/changelog/2026-04-23.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# 2026-04-23
|
||||
|
||||
## Co zrobiono
|
||||
|
||||
- [Phase 01 / Plan 01] Naprawa wariantu kolorystycznego w nowym layoucie strony produktu (IP-gated `REMOTE_ADDR == '89.69.31.86'`)
|
||||
- Task 1: wrapper markup w `product.tpl` — `<div class="product-variants-data--new">` + `<form id="add-to-cart-or-refresh">` (token/id_product/id_customization) + `<div class="product-variants-grid">`
|
||||
- Task 2: styling SCSS pod scope `body#product .product-variants-data--new` wg Figma 27:9867 (grid 3×1 / 2×1 <768 px, Inter 14/25 #8c8c8c left, active outline #7d6e4f)
|
||||
- Task 3: delegowany click+change handler w `custom.js` — własny AJAX `action=refresh` + in-place DOM update (pushState + replace `.product-prices`, `.product_image_wrapper` + emit `updatedProduct`)
|
||||
- Scope addition (uzgodniony): form `add-to-cart-or-refresh` w nowym layoucie — bez niej PS nie mógł AJAX-ować wariantu
|
||||
- Bootstrap PAUL w projekcie: PROJECT.md, ROADMAP.md, STATE.md, phase dir
|
||||
- Feedback memory: edytuj tylko `custom.scss`, nie `custom.css` (user ma lokalny watcher)
|
||||
- Deferred do Phase 02+: add-to-cart w nowym layoucie, konfigurator „piece", puste bloki rozmiar/odbicie/montaż/próbka/zabezpiecz
|
||||
|
||||
- [Phase 02 / Plan 01] Port konfiguratora „piece" (wybór fragmentu tapety + mirror) do nowego layoutu — popup trigger + fancybox zgodnie z Figma, 8 hidden inputów (`is_crop`, `is_reflection`, `crop_pos_x/y`, `crop_width/height`, `piece_bg_top/left`) w formie `#add-to-cart-or-refresh`
|
||||
- Task 1: markup w gałęzi `== '89.69.31.86'` — hidden inputs w formie, `.piece-summary` anchor w `.product-size-data`, mirror button, hidden state (checkbox/width/height/position divs)
|
||||
- Task 2: custom.js defensive setup — setTimeout(600) init z override `totalpriceinfospecific`/`prod` na no-op + wstrzyknięcie DOM stubs (`#totalpriceinfo`, `#custom-wallpaper-price*`, `#quantity_wanted*`), `prestashop.on('updatedProduct')` re-bind dragElement po variant AJAX, defensive `.pp_stick_parent` guard w fancybox handler
|
||||
- Task 3: CSS scoped pod `.product-size-data .product-size-data--new` (flex layout, `.piece-summary` trigger, mirror button hover)
|
||||
- 5 ukrytych problemów wykrytych w live Playwright debug: duplikat `#piece` (reuse z shared partial), CSS `:has()` edge case, ready queue fighting, inline-script crash (module hook), brakujący `.pp_stick_parent`
|
||||
- User feedback w trakcie checkpoint: piece nie auto-init'uje się na load — pojawia się dopiero po kliknięciu trigger'a popupu
|
||||
- Deferred do re-verify: AC-4 (AJAX variant re-init), AC-5 (stary layout regresja), mirror button end-to-end
|
||||
|
||||
## Zmienione pliki
|
||||
|
||||
- `themes/ayon/templates/catalog/product.tpl`
|
||||
- `themes/ayon/assets/css/custom.scss`
|
||||
- `themes/ayon/assets/js/custom.js`
|
||||
- `.paul/PROJECT.md`
|
||||
- `.paul/ROADMAP.md`
|
||||
- `.paul/STATE.md`
|
||||
- `.paul/phases/01-product-variants-fix/01-01-PLAN.md`
|
||||
- `.paul/phases/01-product-variants-fix/01-01-SUMMARY.md`
|
||||
- `.paul/phases/02-product-actions-fixes/02-01-PLAN.md`
|
||||
- `.paul/phases/02-product-actions-fixes/02-01-SUMMARY.md`
|
||||
- `.claude/memory/MEMORY.md`
|
||||
- `.claude/memory/feedback_scss_only.md`
|
||||
3
.paul/docs/API.md
Normal file
3
.paul/docs/API.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# API
|
||||
|
||||
> Endpointy, kontrakty request/response, autentykacja.
|
||||
3
.paul/docs/ARCHITECTURE.md
Normal file
3
.paul/docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# ARCHITECTURE
|
||||
|
||||
> Struktura klas, modulow, przeplywow i zaleznosci w projekcie.
|
||||
3
.paul/docs/DB_SCHEMA.md
Normal file
3
.paul/docs/DB_SCHEMA.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# DB_SCHEMA
|
||||
|
||||
> Schemat bazy danych — tabele, kolumny, FK, indeksy.
|
||||
3
.paul/docs/DECISIONS.md
Normal file
3
.paul/docs/DECISIONS.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# DECISIONS
|
||||
|
||||
> Kluczowe decyzje techniczne i ich uzasadnienia.
|
||||
3
.paul/docs/STACK.md
Normal file
3
.paul/docs/STACK.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# STACK
|
||||
|
||||
> Stack technologiczny, wersje, zaleznosci, srodowisko.
|
||||
3
.paul/docs/TECH_CHANGELOG.md
Normal file
3
.paul/docs/TECH_CHANGELOG.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# TECH_CHANGELOG
|
||||
|
||||
> Chronologiczny log zmian technicznych — co i dlaczego.
|
||||
3
.paul/docs/TODO.md
Normal file
3
.paul/docs/TODO.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# TODO
|
||||
|
||||
> Luzny parking pomyslow, rzeczy do sprawdzenia.
|
||||
83
.paul/governance/governance_2026-04-23.jsonl
Normal file
83
.paul/governance/governance_2026-04-23.jsonl
Normal file
@@ -0,0 +1,83 @@
|
||||
{"ts":"2026-04-23T17:42:35Z","tool":"Bash","cmd":"mkdir -p .paul/phases/01-product-variants-fix\",\"description\":\"Create PAUL phase directory\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"noOutputEx","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T17:42:52Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T17:43:01Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T17:43:10Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T17:45:00Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\phases\\\\01-product-variants-fix\\\\01-01-PLAN.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T17:46:16Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T17:47:32Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\css\\\\custom.scss","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T17:48:12Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T17:58:53Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T17:59:36Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\css\\\\custom.scss","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:00:54Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.claude\\\\memory\\\\feedback_scss_only.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:01:02Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.claude\\\\memory\\\\MEMORY.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:01:08Z","tool":"Bash","cmd":"git checkout -- themes/ayon/assets/css/custom.css themes/ayon/assets/css/custom.css.map && echo 'reverted' && git diff --stat -- themes/ayon/assets/css/\",\"description\":\"Revert custom.css and css.m","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:14:18Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:18:29Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:23:08Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:24:59Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\phases\\\\01-product-variants-fix\\\\01-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:25:17Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:27:38Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\phases\\\\01-product-variants-fix\\\\01-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:27:55Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\changelog\\\\2026-04-23.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:28:20Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:28:49Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:29:06Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:36:38Z","tool":"Bash","cmd":"mkdir -p \".paul/phases/02-product-actions-fixes\"\",\"description\":\"Create phase directory\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"noOutputEx","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:38:50Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\phases\\\\02-product-actions-fixes\\\\02-01-PLAN.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:39:28Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:39:41Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:41:42Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:41:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:41:56Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:42:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:43:13Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:43:21Z","tool":"Bash","cmd":"wc -l themes/ayon/assets/css/custom.scss\",\"description\":\"Get line count of custom.scss\"},\"tool_response\":{\"stdout\":\"3892 themes/ayon/assets/css/custom.scss\",\"stderr\":\"\",\"interrupted\":","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:44:09Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\css\\\\custom.scss","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:44:54Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\css\\\\custom.scss","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:45:20Z","tool":"Bash","cmd":"git diff --stat themes/ayon/templates/catalog/product.tpl themes/ayon/assets/js/custom.js themes/ayon/assets/css/custom.scss\",\"description\":\"Diff stat for modified files\"},\"tool_response\":{\"st","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T18:45:30Z","tool":"Bash","cmd":"wc -l themes/ayon/assets/css/custom.scss && tail -5 themes/ayon/assets/css/custom.scss\",\"description\":\"Verify scss file size and end\"},\"tool_response\":{\"stdout\":\"4029 themes/ayon/assets/css/","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:39:09Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:39:20Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:39:51Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:40:09Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\css\\\\custom.scss","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:44:34Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:44:39Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\css\\\\custom.scss","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:44:43Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:45:09Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:53:29Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:53:33Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:53:43Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:53:56Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\css\\\\custom.scss","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:56:22Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\css\\\\custom.scss","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:56:30Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:58:52Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T19:59:55Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:02:19Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:06:28Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:08:16Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:16:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:16:50Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:17:13Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:20:37Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:23:51Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\phases\\\\02-product-actions-fixes\\\\02-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:24:24Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\changelog\\\\2026-04-23.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:25:09Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:33:34Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\phases\\\\02-product-actions-fixes\\\\02-02-PLAN.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:34:34Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:34:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:45:56Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:46:44Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:47:17Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\css\\\\custom.scss","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:49:47Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:49:56Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\css\\\\custom.scss","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:53:24Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T20:53:45Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T21:10:18Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T21:10:22Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\assets\\\\js\\\\custom.js","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T21:10:49Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\themes\\\\ayon\\\\templates\\\\catalog\\\\product.tpl","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T21:24:41Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\phases\\\\02-product-actions-fixes\\\\02-02-SUMMARY.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T21:25:47Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T21:26:11Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T21:30:02Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\phases\\\\02-product-actions-fixes\\\\02-03-PLAN.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T21:30:24Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\newwalls.pl-1\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T21:32:40Z","tool":"Bash","cmd":"git status\",\"description\":\"Show working tree status\"},\"tool_response\":{\"stdout\":\"On branch main\\nYour branch is up to date with 'origin/main'.\\n\\nChanges not staged for commit:\\n (use \","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
{"ts":"2026-04-23T21:32:53Z","tool":"Bash","cmd":"git log --oneline -5 && echo \"---DIFFSTAT---\" && git diff --stat\",\"description\":\"Get commit style and diff stats\"},\"tool_response\":{\"stdout\":\"161c090 product structure\\n9f00dce Save\\nbcf","cwd":"/c/visual studio code/projekty/newwalls.pl-1"}
|
||||
331
.paul/phases/01-product-variants-fix/01-01-PLAN.md
Normal file
331
.paul/phases/01-product-variants-fix/01-01-PLAN.md
Normal file
@@ -0,0 +1,331 @@
|
||||
---
|
||||
phase: 01-product-variants-fix
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- themes/ayon/templates/catalog/product.tpl
|
||||
- themes/ayon/assets/css/custom.scss
|
||||
- themes/ayon/assets/css/custom.css
|
||||
- themes/ayon/assets/js/custom.js
|
||||
autonomous: false
|
||||
delegation: off
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
W nowym layoucie strony produktu (gałąź `{if $smarty.server.REMOTE_ADDR == '89.69.31.86'}`) doprowadzić blok `.product-variants` w sekcji `.product-box.product-variants-data` (pod nagłówkiem „Wybierz wersję kolorystyczną") do stanu:
|
||||
|
||||
1. **Wizualnie** — wygląda jak wzór `d:\temp\Frame 33.png`:
|
||||
- kafelki wariantów (miniaturki) ułożone obok siebie w rzędzie (min. 3 w linii na desktopie, responsywnie),
|
||||
- każdy kafelek = kwadratowe zdjęcie tekstury wariantu,
|
||||
- pod każdym kafelkiem podpis z nazwą wariantu (np. „Lorem ipsum" w makiecie → `{$group_attribute.name}`),
|
||||
- aktywny wariant wyróżniony (obramowanie lub inny stan aktywny),
|
||||
- bez dziedziczenia stylów starego „box-color-variants" (które zakładały modal/overlay).
|
||||
|
||||
2. **Funkcjonalnie** — kliknięcie miniaturki wariantu zmienia wariant produktu (przekierowanie/refresh PrestaShop do właściwej kombinacji), analogicznie do starego wyglądu. Pozostałe funkcje strony produktu (konfigurator rozmiaru, odbicie, dodanie do koszyka) NIE mogą zostać zepsute.
|
||||
|
||||
## Purpose
|
||||
Klient używa IP `89.69.31.86` do prezentacji nowego wyglądu. Wariant kolorystyczny jest kluczowym elementem konwersji — bez działającej zmiany wariantu strona produktu jest nieużywalna w nowym layoucie.
|
||||
|
||||
## Output
|
||||
- Zmieniony `product.tpl` (tylko sekcja `{if ... == '89.69.31.86'}` — część dotycząca bloku `product_variants`).
|
||||
- Nowe reguły SCSS/CSS dla `.product-variants-data .product-variants` widoczne TYLKO w nowym layoucie (np. scope `body#product .product-box.product-variants-data .product-variants ...`).
|
||||
- Ewentualnie: drobna korekta w `custom.js` zapewniająca zmianę wariantu po kliknięciu (delegowany handler zamiast bezpośredniego `.click()`), bez ingerowania w starą ścieżkę.
|
||||
- SUMMARY.md w `.paul/phases/01-product-variants-fix/01-01-SUMMARY.md`.
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@themes/ayon/templates/catalog/product.tpl
|
||||
@themes/ayon/templates/catalog/_partials/product-variants.tpl
|
||||
@themes/ayon/assets/css/custom.scss
|
||||
@themes/ayon/assets/js/custom.js
|
||||
|
||||
## Reference Mockup
|
||||
- `d:\temp\Frame 33.png` — 3 kwadratowe kafelki obok siebie, pod każdym krótki podpis tekstowy.
|
||||
|
||||
## Current State (z researchu)
|
||||
- Partial `product-variants.tpl`:
|
||||
- Dla `$product_variant_mode == 2` (color) generuje `<ul id="group_{id}"><li class="wariant_kolorystyczny"><label><input type="radio" name="group[..]"><img .../><span class="color ..."></span></label></li>...</ul>`.
|
||||
- Radio + label to standardowy mechanizm PrestaShop do zmiany wariantu (core.js nasłuchuje `change` na `input[name^="group["]`).
|
||||
- W nowym layoucie (linia ~604–607 w `product.tpl`) partial jest włączany z `$product_variant_mode = 2` w bloku:
|
||||
```
|
||||
<div class="product-box product-variants-data">
|
||||
<h4 class="block-title">Wybierz wersję kolorystyczną</h4>
|
||||
{include file='catalog/_partials/product-variants.tpl'}
|
||||
</div>
|
||||
```
|
||||
ale NIE jest owinięty w `#box-color-variants`, przez co istniejące reguły CSS i handlery JS pisane pod stary layout (modal z fadeIn/fadeOut) nie aplikują się.
|
||||
- `custom.js:225` — `jQuery(".wariant_kolorystyczny").click(...)` — bezpośrednie binding, odpala tylko logikę „piece" konfiguratora (nie zmiana wariantu). Zmiana wariantu powinna wychodzić z natywnego PrestaShop flow (change na radio).
|
||||
- `custom.js:619` — `$(document).on('click', '#box-color-variants .wariant_kolorystyczny', ...)` — zamyka modal w starym layoucie. W nowym layoucie ten selektor się nie dopasowuje (i dobrze — nowy wygląd nie jest modalem).
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Wizualny kształt bloku wariantów
|
||||
```gherkin
|
||||
Given jestem klientem korzystającym z IP 89.69.31.86
|
||||
And otwieram stronę dowolnego produktu z kilkoma wariantami kolorystycznymi
|
||||
When patrzę na sekcję "Wybierz wersję kolorystyczną"
|
||||
Then widzę miniatury wariantów ułożone w siatce / rzędzie (min. 3 kafelki w linii na szerokim ekranie)
|
||||
And każdy kafelek jest kwadratowy i zawiera obraz tekstury wariantu
|
||||
And pod każdym kafelkiem jest widoczny podpis z nazwą wariantu
|
||||
And aktywny (wybrany) wariant jest wizualnie wyróżniony (np. obramowanie, tło)
|
||||
And układ jest responsywny (co najmniej 2 kafelki w linii poniżej 768 px)
|
||||
```
|
||||
|
||||
## AC-2: Klik zmienia wariant produktu
|
||||
```gherkin
|
||||
Given jestem klientem korzystającym z IP 89.69.31.86
|
||||
And otwieram stronę produktu w nowym layoucie
|
||||
When klikam miniaturę innego wariantu niż aktualnie zaznaczony
|
||||
Then PrestaShop przełącza na wybraną kombinację (URL / cena / obrazy produktu aktualizują się tak samo jak w starym layoucie)
|
||||
And klikany wariant staje się wariantem aktywnym (wizualne wyróżnienie)
|
||||
And żadne błędy JS nie pojawiają się w konsoli
|
||||
```
|
||||
|
||||
## AC-3: Brak regresji w starym layoucie i innych funkcjach nowego
|
||||
```gherkin
|
||||
Given jestem klientem z IP innym niż 89.69.31.86
|
||||
When otwieram stronę produktu
|
||||
Then stary layout wygląda i działa identycznie jak przed zmianami (wariant kolorystyczny w modalu #box-color-variants, konfigurator rozmiaru, odbicie, dodanie do koszyka)
|
||||
|
||||
Given jestem klientem z IP 89.69.31.86 w nowym layoucie
|
||||
When używam innych elementów strony produktu (konfigurator rozmiaru, przyciski, zakładki opisu)
|
||||
Then elementy te zachowują się co najmniej tak samo jak przed zmianami w tym planie (nie zepsute dodatkowo)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Dostosuj markup bloku product-variants w nowym layoucie (product.tpl)</name>
|
||||
<files>themes/ayon/templates/catalog/product.tpl</files>
|
||||
<action>
|
||||
W gałęzi `{if $smarty.server.REMOTE_ADDR == '89.69.31.86'}` (okolice linii 604–607) wokół `{include file='catalog/_partials/product-variants.tpl'}` dla `$product_variant_mode = 2` dodaj pomocniczy kontener-klasę, która pozwoli stargetować nowy wygląd w CSS bez ruszania partiala i starego kodu. Struktura docelowa:
|
||||
|
||||
```smarty
|
||||
{block name='product_variants'}
|
||||
{$product_variant_mode = 2}
|
||||
<div class="product-box product-variants-data product-variants-data--new">
|
||||
<h4 class="block-title">Wybierz wersję kolorystyczną</h4>
|
||||
<div class="product-variants-grid">
|
||||
{include file='catalog/_partials/product-variants.tpl'}
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
||||
```
|
||||
|
||||
Wymagania:
|
||||
- NIE zmieniaj partiala `product-variants.tpl` (dzielony ze starym layoutem).
|
||||
- NIE zmieniaj gałęzi `{if ... != '89.69.31.86'}` — stary layout nietknięty.
|
||||
- Owinięcie w `.product-variants-grid` daje nam czysty scope CSS tylko dla nowego widoku.
|
||||
- W partialu dla mode=2 jest `{if $id_attribute_group == 5}<a ... class="fancybox-material-controls">` — w mode=2 ten warunek nie dotyczy, więc OK.
|
||||
|
||||
Avoid: modyfikowania partiala, starej ścieżki, innych sekcji `product.tpl`.
|
||||
</action>
|
||||
<verify>
|
||||
`git diff themes/ayon/templates/catalog/product.tpl` pokazuje zmianę TYLKO w bloku między liniami `{if $smarty.server.REMOTE_ADDR == '89.69.31.86'}` a `{/if}` (ta druga linia ~800+). Brak zmian w gałęzi `!=`.
|
||||
</verify>
|
||||
<done>AC-1 przygotowane strukturalnie (markup gotowy pod CSS); AC-3 niezagrożone (stary layout nie ruszony).</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Stwórz style SCSS/CSS dla .product-variants-data--new (kafelki 3-w-rzędzie)</name>
|
||||
<files>themes/ayon/assets/css/custom.scss, themes/ayon/assets/css/custom.css</files>
|
||||
<action>
|
||||
W `custom.scss` dodaj blok (na końcu pliku lub w sekcji poświęconej stronie produktu):
|
||||
|
||||
```scss
|
||||
/* NEW product page — color variants grid (IP-gated layout) */
|
||||
body#product .product-variants-data--new {
|
||||
.product-variants-grid > .product-variants {
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
.product-variants-grid ul[id^="group_"] {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
.product-variants-grid li.wariant_kolorystyczny {
|
||||
float: none;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.product-variants-grid li.wariant_kolorystyczny > label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 4px;
|
||||
padding: 6px;
|
||||
transition: border-color .15s ease-in-out;
|
||||
|
||||
/* ukryj natywny radio — wybór po kliknięciu labela */
|
||||
input.input-color {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* podpis = ukryty span.sr-only.tip z nazwą wariantu — pokaż go */
|
||||
.sr-only.tip {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: auto;
|
||||
clip: auto;
|
||||
overflow: visible;
|
||||
white-space: normal;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
color: #3c3c3c;
|
||||
}
|
||||
|
||||
/* kwadracik koloru / tekstury ze starego markupu — w nowym widoku ukryty,
|
||||
bo prezentujemy sam obraz wariantu */
|
||||
> span.color { display: none; }
|
||||
|
||||
&:hover { border-color: #c9bda4; }
|
||||
}
|
||||
.product-variants-grid li.wariant_kolorystyczny > label:has(input.input-color:checked) {
|
||||
border-color: #7d6e4f;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Następnie zregeneruj `custom.css`:
|
||||
- Jeśli w projekcie jest pipeline SCSS → uruchom kompilację.
|
||||
- Jeśli nie — odpowiadający blok CSS (rozwinięty, bez zagnieżdżeń) dopisz ręcznie na końcu `custom.css` tak, by trafił do produkcji.
|
||||
|
||||
Avoid:
|
||||
- Nadpisywania reguł globalnych `.product-variants`, `.wariant_kolorystyczny` bez scope'a `body#product .product-variants-data--new` — zepsułoby to stary layout i quickview.
|
||||
- Usuwania istniejących reguł w custom.css/custom.scss.
|
||||
</action>
|
||||
<verify>
|
||||
1. Zaloguj się z IP 89.69.31.86, otwórz produkt z ≥3 wariantami.
|
||||
2. Wizualnie: 3 kafelki obok siebie, kwadratowe obrazy, podpisy pod spodem, aktywny wyróżniony obramowaniem.
|
||||
3. Stary layout (inne IP) w tabeli wariantów wygląda tak jak przed zmianą.
|
||||
</verify>
|
||||
<done>AC-1 spełnione; AC-3 (część wizualna starego layoutu) utrzymane.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Zapewnij zmianę wariantu po kliknięciu w nowym layoucie (custom.js)</name>
|
||||
<files>themes/ayon/assets/js/custom.js</files>
|
||||
<action>
|
||||
PrestaShop core.js natywnie nasłuchuje `change` na `input[name^="group["]` — klik w `<label>` powinien przełączyć radio i wywołać refresh wariantu. Problem pojawia się, gdy po refreshu AJAX kontener `.product-variants` jest podmieniany i bezpośrednie bindingi (np. `custom.js:225 jQuery(".wariant_kolorystyczny").click(...)`) giną.
|
||||
|
||||
Dodaj na końcu pliku `themes/ayon/assets/js/custom.js` (po istniejącym handlerze `#box-color-variants .wariant_kolorystyczny`) delegowany listener gwarantujący działanie również w nowym layoucie:
|
||||
|
||||
```js
|
||||
/* NEW layout — klik w kafelek wariantu zmienia wariant (delegowany, przeżywa refresh AJAX) */
|
||||
$(document).on('click', '.product-variants-data--new .wariant_kolorystyczny label', function (e) {
|
||||
var $label = $(this);
|
||||
var $radio = $label.find('input.input-color');
|
||||
if (!$radio.length) return;
|
||||
if ($radio.is(':checked')) return; // już aktywny
|
||||
$radio.prop('checked', true).trigger('change');
|
||||
});
|
||||
```
|
||||
|
||||
Uwaga:
|
||||
- NIE zmieniaj istniejącego handlera `jQuery(".wariant_kolorystyczny").click(...)` (linia ~225) — obsługuje logikę „piece" i działa w starym layoucie.
|
||||
- NIE zmieniaj handlera `#box-color-variants .wariant_kolorystyczny` (linia ~619) — zamyka modal w starym layoucie.
|
||||
- Scope `.product-variants-data--new` (dodany w Task 1) zapewnia, że nowy kod nie dotyka starego layoutu.
|
||||
- `trigger('change')` na radio uruchamia natywny mechanizm PrestaShop do przełączenia wariantu.
|
||||
|
||||
Avoid: `e.preventDefault()` na labelu (zablokowałoby natywny toggle radia).
|
||||
</action>
|
||||
<verify>
|
||||
1. IP 89.69.31.86 → strona produktu z wariantami → otwórz DevTools → Console.
|
||||
2. Kliknij inny wariant niż aktualny → strona powinna zaktualizować wariant (URL, cena, zdjęcia) bez błędów JS.
|
||||
3. Kliknij ponownie ten sam wariant → nic się nie zmienia (brak zbędnych przeładowań).
|
||||
</verify>
|
||||
<done>AC-2 spełnione; AC-3 utrzymane (stara ścieżka nietknięta).</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>
|
||||
Nowy wygląd bloku wariantów kolorystycznych na stronie produktu (IP 89.69.31.86):
|
||||
- 3 kafelki obok siebie z obrazem + podpisem,
|
||||
- klik w kafelek przełącza wariant produktu,
|
||||
- stary layout (inne IP) nie zmieniony.
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
1. Jako klient z IP 89.69.31.86 wejdź na stronę dowolnego produktu z kilkoma wariantami kolorystycznymi.
|
||||
2. Sprawdź wizualnie: sekcja „Wybierz wersję kolorystyczną" ma wyglądać jak w `d:\temp\Frame 33.png` (3 kafelki w rzędzie, podpisy pod spodem).
|
||||
3. Sprawdź na mniejszym oknie (poniżej 768 px): kafelki układają się w 2 kolumny.
|
||||
4. Kliknij inny wariant niż aktywny → potwierdź: URL się zmienia, cena i obrazy produktu się aktualizują, klikany kafelek staje się wyróżniony.
|
||||
5. Otwórz konsolę DevTools — brak błędów JS.
|
||||
6. Wejdź na tę samą stronę spoza IP 89.69.31.86 → potwierdź: stary layout (modal #box-color-variants) wygląda i działa jak przed zmianami.
|
||||
7. Na stronie produktu z IP 89.69.31.86 sprawdź, że pozostałe elementy (konfigurator rozmiaru, odbicie, dodanie do koszyka) działają co najmniej tak samo jak przed zmianą.
|
||||
</how-to-verify>
|
||||
<resume-signal>Wpisz "approved" aby zamknąć plan lub opisz problemy do poprawy.</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- Gałąź `{if $smarty.server.REMOTE_ADDR != '89.69.31.86'}` w `product.tpl` (stary layout produkcyjny).
|
||||
- `themes/ayon/templates/catalog/_partials/product-variants.tpl` (partial wspólny dla starego i nowego + quickview).
|
||||
- Istniejące handlery `jQuery(".wariant_kolorystyczny").click(...)` (custom.js ~225) i `$(document).on('click', '#box-color-variants .wariant_kolorystyczny', ...)` (custom.js ~619).
|
||||
- Globalne reguły CSS dla `.product-variants`, `.wariant_kolorystyczny`, `#box-color-variants` w `custom.scss` / `custom.css` — nowy CSS dopisywany TYLKO pod scope `.product-variants-data--new` (lub `body#product .product-variants-data--new`).
|
||||
- PrestaShop core (`themes/ayon/assets/js/theme.js`, bundles).
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Ten plan naprawia wyłącznie sekcję wariantów kolorystycznych w nowym layoucie.
|
||||
- Nie naprawiamy w tym planie: konfiguratora rozmiaru, odbicia lustrzanego, ceny, dodania do koszyka, zakładek, próbki, struktur — nawet jeśli są popsute (idą do Phase 02).
|
||||
- Nie dodajemy nowych zależności (brak npm / composer install).
|
||||
- Nie zmieniamy backendu (kontrolery, moduły).
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Przed zamknięciem planu:
|
||||
- [ ] `git diff` pokazuje zmiany wyłącznie w: `product.tpl` (gałąź nowego layoutu), `custom.scss`, `custom.css`, `custom.js`.
|
||||
- [ ] Test ręczny z IP 89.69.31.86 — AC-1, AC-2 spełnione.
|
||||
- [ ] Test ręczny z innym IP — AC-3 część „stary layout" OK.
|
||||
- [ ] Brak błędów JS w konsoli na stronie produktu w obu trybach.
|
||||
- [ ] Checkpoint `human-verify` zatwierdzony przez użytkownika ("approved").
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- AC-1, AC-2, AC-3 spełnione.
|
||||
- Checkpoint zatwierdzony.
|
||||
- Brak regresji w partialu `product-variants.tpl` i starym layoucie.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Po zakończeniu planu utwórz `.paul/phases/01-product-variants-fix/01-01-SUMMARY.md` z:
|
||||
- listą zmodyfikowanych plików (z krótkim opisem zmiany),
|
||||
- decyzjami (np. dlaczego scope `.product-variants-data--new` zamiast globalnego),
|
||||
- potencjalnymi side-effectami do obserwacji w Phase 02.
|
||||
</output>
|
||||
182
.paul/phases/01-product-variants-fix/01-01-SUMMARY.md
Normal file
182
.paul/phases/01-product-variants-fix/01-01-SUMMARY.md
Normal file
@@ -0,0 +1,182 @@
|
||||
---
|
||||
phase: 01-product-variants-fix
|
||||
plan: 01
|
||||
subsystem: ui
|
||||
tags: [prestashop, smarty, jquery, scss, product-variants, ajax-refresh]
|
||||
|
||||
requires: []
|
||||
provides:
|
||||
- Działający wariant kolorystyczny w nowym layoucie strony produktu (IP-gated `== '89.69.31.86'`)
|
||||
- Grid 3×1 (desktop) / 2×1 (<768 px) kafelków wariantów wg Figma 27:9867
|
||||
- In-place refresh wariantu (obraz + cena + URL) bez przeładowania strony
|
||||
affects:
|
||||
- Phase 02+ — kolejne naprawy nowego layoutu (add-to-cart, konfigurator rozmiaru, odbicie, zakładki)
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Scoped CSS pod body#product .product-variants-data--new — izoluje nowy layout od globalnych reguł"
|
||||
- "Własny AJAX PS refresh (action=refresh) + manual DOM replace jako fallback gdy PS core handler nie znajduje wymaganych selektorów"
|
||||
- "Dwa IP-gated branche w product.tpl renderują się wzajemnie wyłącznie — duplikaty ID (np. add-to-cart-or-refresh) OK"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- .paul/PROJECT.md
|
||||
- .paul/ROADMAP.md
|
||||
- .paul/STATE.md
|
||||
- .paul/phases/01-product-variants-fix/01-01-PLAN.md
|
||||
- .claude/memory/feedback_scss_only.md
|
||||
- .claude/memory/MEMORY.md
|
||||
modified:
|
||||
- themes/ayon/templates/catalog/product.tpl
|
||||
- themes/ayon/assets/css/custom.scss
|
||||
- themes/ayon/assets/js/custom.js
|
||||
|
||||
key-decisions:
|
||||
- "Ręczny AJAX refresh w custom.js zamiast fake .product-actions — unika kolizji stylów ze starym layoutem"
|
||||
- "action=refresh (nie productrefresh) — odkryte empirycznie przez probing endpointów"
|
||||
- "Edytuj tylko custom.scss, nie custom.css — user ma lokalny watcher SCSS (zapisane jako feedback memory)"
|
||||
- "In-place update (history.pushState + manual DOM replace) zamiast full redirect — user feedback o starym layoucie"
|
||||
|
||||
patterns-established:
|
||||
- "IP-gated layout switching: nowy layout pod REMOTE_ADDR == '89.69.31.86', stary dla reszty. Zmiany CSS/JS scope'owane pod .product-variants-data--new aby nie dotykać starego layoutu"
|
||||
- "Fallback graceful: próba in-place update → przy wyjątku/błędzie HTTP fallback na window.location.href = resp.product_url lub reload"
|
||||
|
||||
duration: ~2h
|
||||
started: 2026-04-23T17:50:00Z
|
||||
completed: 2026-04-23T18:25:00Z
|
||||
---
|
||||
|
||||
# Phase 01 Plan 01: Product variants (nowy layout) — Summary
|
||||
|
||||
**Wariant kolorystyczny w nowym layoucie: 3-kafelkowy grid wg Figma + klik zmienia kombinację produktu w miejscu (AJAX refresh, bez reloadu).**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~2h |
|
||||
| Started | 2026-04-23T17:50:00Z |
|
||||
| Completed | 2026-04-23T18:25:00Z |
|
||||
| Tasks | 3 auto + 1 checkpoint (ok) |
|
||||
| Files modified | 3 source + 6 PAUL/memory meta |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Wizualny kształt bloku wariantów (3 kafelki, responsive) | Pass | Grid 3×1 desktop / 2×1 <768 px; Inter 14/25 `#8c8c8c` left-aligned; active outline `#7d6e4f`. Zgodnie z Figma 27:9867. |
|
||||
| AC-2: Klik zmienia wariant produktu (URL/cena/obrazy) | Pass | Test live w Playwright: klik wariant 2 → obraz `919/...` → `920/...`, URL `202-2306-...` → `202-2289-...`, bez reloadu. |
|
||||
| AC-3: Brak regresji w starym layoucie i innych funkcjach | Pass | Stara gałąź `!= '89.69.31.86'` nietknięta, partial `product-variants.tpl` nietknięty, istniejące handlery w custom.js (`~225`, `~619`) nietknięte, CSS scope'owany. |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Dopasowany wygląd sekcji wariantów w nowym layoucie do makiety Figma (3-kafelkowy grid z podpisami, minimalistyczny styling).
|
||||
- Przywrócona funkcjonalność zmiany wariantu (nowy layout nie miał `<form id="add-to-cart-or-refresh">`) + zaimplementowany flow in-place refresh bez reloadu, lepsze UX niż pełne przekierowanie.
|
||||
- Zbudowany szkielet PAUL (PROJECT/ROADMAP/STATE + phase dir) w projekcie, który ułatwi kolejne fazy.
|
||||
|
||||
## Task Commits
|
||||
|
||||
Task commits nie zostały jeszcze utworzone — APPLY był wykonywany inline bez per-task commitów (delegation: off). Commit fazowy wykona `transition-phase` (pending user approval).
|
||||
|
||||
| Task | Commit | Type | Description |
|
||||
|------|--------|------|-------------|
|
||||
| Task 1 (markup) | pending | feat | Wrap bloku wariantów w `.product-variants-data--new` + `<form#add-to-cart-or-refresh>` w product.tpl (nowa gałąź) |
|
||||
| Task 2 (CSS) | pending | feat | SCSS grid pod `body#product .product-variants-data--new` wg Figma |
|
||||
| Task 3 (JS) | pending | feat | Delegowany click handler + własny AJAX refresh z in-place DOM update w custom.js |
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `themes/ayon/templates/catalog/product.tpl` | Modified | Nowa gałąź `REMOTE_ADDR == '89.69.31.86'`, blok `product_variants` (ok. linia 601) — dodany wrapper `.product-variants-data--new`, `<form#add-to-cart-or-refresh>` z hidden inputs `token` / `id_product` / `id_customization`, grid kontener `.product-variants-grid` |
|
||||
| `themes/ayon/assets/css/custom.scss` | Modified | Dopisany blok `body#product .product-variants-data--new {...}` — grid, typography Inter 14/25 #8c8c8c left, active outline |
|
||||
| `themes/ayon/assets/js/custom.js` | Modified | Dopisane 2 delegowane handlery: click label (toggle radio) + change radio (AJAX `action=refresh` → pushState + replace `.product-prices`, `.product_image_wrapper` + emit `updatedProduct`) |
|
||||
| `.paul/PROJECT.md` | Created | Bootstrap PAUL — opis projektu, constraints (IP-gated layout, shared partial) |
|
||||
| `.paul/ROADMAP.md` | Created | Milestone v0.1 — 2 fazy (Phase 01 wariant kolorystyczny; Phase 02 pozostałe funkcje) |
|
||||
| `.paul/STATE.md` | Created + updated | Loop position tracking |
|
||||
| `.paul/phases/01-product-variants-fix/01-01-PLAN.md` | Created | Plan fazy 1 |
|
||||
| `.paul/phases/01-product-variants-fix/01-01-SUMMARY.md` | Created | Ten plik |
|
||||
| `.claude/memory/feedback_scss_only.md` | Created | Feedback memory: edytuj tylko custom.scss |
|
||||
| `.claude/memory/MEMORY.md` | Created | Index memory |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Dodanie `<form#add-to-cart-or-refresh>` w gałęzi nowego layoutu (scope creep względem PLAN) | Bez niego PS serializuje pustą formę i AJAX nie zna `group[X]=Y`. Bez tego nie da się zrealizować AC-2. | Minimalny + potrzebny. Przygotowuje fundament pod Phase 02 (ta sama forma może być używana do add-to-cart). |
|
||||
| Ręczny AJAX refresh w custom.js (nie zmuszanie PS core handler do działania przez dodawanie klasy `.product-actions`) | PS core handler `updateProduct_` szuka formy przez `$('.product-actions').find('form:first')`. Dodanie tej klasy do mojego wrappera mogłoby wciągnąć style starego layoutu i kolizje. | Bezpieczniejsze, bardziej deterministyczne, w pełni pod kontrolą. |
|
||||
| `action=refresh` (nie `productrefresh`) | Empiryczne probowanie endpointów w Playwright: `productrefresh` zwracał pustą odpowiedź, `refresh` zwraca JSON z fragmentami HTML. | Wiedza do reużycia w Phase 02. |
|
||||
| In-place DOM update z `history.pushState` (vs. `window.location.href = resp.product_url` reload) | User feedback: „na starym layoucie działało bez przeładowania i wyglądało lepiej". | Lepsze UX. Fallback na reload przy błędzie. |
|
||||
| Edytuj tylko `custom.scss`, nie `custom.css` | User ma lokalny watcher SCSS → lokalne zmiany w `custom.css` nadpisane przy rekompilacji. | Zapisane w feedback memory — przyszłe sesje zastosują regułę automatycznie. |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 2 | Niezbędne do spełnienia AC-2 |
|
||||
| Scope additions | 1 | Konieczne (form wrapper) — uzgodnione z userem |
|
||||
| Deferred | 1 | Pełny wizualny fit — dociągnie kompilacja SCSS |
|
||||
|
||||
**Total impact:** Zero scope creep poza niezbędnymi do działania AC-2. Wszystkie odstępstwa zaakceptowane przez usera w trakcie.
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. Brak `<form id="add-to-cart-or-refresh">` w nowym layoucie — blokada AJAX refresh**
|
||||
- **Found during:** Live test w Playwright po Task 3, klik wariantu nie triggerował AJAX.
|
||||
- **Issue:** PS serializuje formę o tym ID; bez niej puste body → serwer nie zna `group[X]=Y`.
|
||||
- **Fix:** Dodany form w bloku `product_variants` w nowym layoucie, z hidden inputs (`token`, `id_product`, `id_customization`). Scope creep uzgodniony z userem (odpowiedź „1. Apply. Tylko na razie obie wersje muszą działać jednocześnie").
|
||||
- **Files:** `themes/ayon/templates/catalog/product.tpl`
|
||||
- **Verification:** `$('#add-to-cart-or-refresh').serialize()` w DevTools zwraca `token=...&id_product=202&...&group%5B4%5D=5`.
|
||||
|
||||
**2. PS core handler `updateProduct_` szuka formy w `.product-actions` — nie istnieje w nowym layoucie**
|
||||
- **Found during:** Live test w Playwright, klik triggerował `change` ale PS handler nie wysyłał AJAX.
|
||||
- **Issue:** Handler: `var t = $(".product-actions"); ...$.ajax({url: t.find('form:first').attr('action')...})` — `t` pusty w nowym layoucie.
|
||||
- **Fix:** Dopisany własny delegowany `change` handler w `custom.js` który robi własny POST (`action=refresh`) na bieżący URL produktu i aktualizuje DOM in-place.
|
||||
- **Files:** `themes/ayon/assets/js/custom.js`
|
||||
- **Verification:** Playwright test — klik wariant 2, AJAX `200 OK` + JSON, obraz i URL się zmieniają bez reloadu.
|
||||
|
||||
### Scope Additions
|
||||
|
||||
**1. Dodanie wrapper'a `<form id="add-to-cart-or-refresh">` w product.tpl**
|
||||
- Plan mówił „nie naprawiamy add-to-cart". Ale bez formy nie ma AC-2. Uzgodnione z userem przed APPLY: „1. Apply. Tylko na razie obie wersje muszą działać jednocześnie".
|
||||
|
||||
### Deferred Items
|
||||
|
||||
Logged do STATE.md „Open observations":
|
||||
- Brak `.product-actions` wokół `product_add_to_cart` → „Dodaj do koszyka" prawdopodobnie nie działa w nowym layoucie.
|
||||
- Puste bloki: `.product-size-data`, `.product-protect`, `.product-installation`, `.product-order-sample` — do implementacji w Phase 02+.
|
||||
- Konfigurator „piece" (crop + mirror) — brak markup'u w nowym layoucie.
|
||||
- Obserwacja dla wizualnego dopasowania: po rekompilacji SCSS warto sprawdzić czy globalne reguły `.product-variants` / `.wariant_kolorystyczny` nie dominują nad scope'owymi (edge case visual).
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
| Issue | Resolution |
|
||||
|-------|------------|
|
||||
| nginx cache'uje `custom.js` bez cache-bustera — Playwright widzi starą wersję przy `fetch('/custom.js')` | Weryfikacja przez `fetch` z query busterem `?v=timestamp`; user czyści cache PS + hard refresh przeglądarki (`Ctrl+F5`). |
|
||||
| Figma OAuth URL początkowo dawał błąd „Parameter code_challenge_method is required" | User otworzył URL po ponownym wygenerowaniu; jednak drugi `authenticate` unieważnił pierwszą sesję — CSRF mismatch. Rozwiązanie: trzeci `authenticate` i dokładnie jednorazowe użycie linka. |
|
||||
| `action=productrefresh` zwracał pustą odpowiedź (200 OK + empty body) | Empiryczne probowanie wariantów: `action=refresh` zwraca poprawny JSON. |
|
||||
| Prestashop emituje `updateProduct_` (z podkreślnikiem), nie `updateProduct` — handler dla `updateProduct_` szuka formy w niewłaściwym miejscu | Obejście: własny delegowany change handler robi niezależny AJAX; `prestashop.emit('updatedProduct', resp)` nadal uruchamia rejestrowane handlery theme.js. |
|
||||
|
||||
## Skill audit
|
||||
|
||||
SPECIAL-FLOWS.md nie istnieje w projekcie → brak wymaganych skill'i. Użyte ad-hoc: `figma:figma-implement-design` (podgląd spec frame 27:9867), Playwright (live testing na produkcji). ✓
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Działa IP-gated switch między starym a nowym layoutem — pattern do reużycia w kolejnych naprawach.
|
||||
- Pattern własnego AJAX refresh (`action=refresh` + `$.ajax` + `pushState` + manual DOM replace) — gotowy do zastosowania przy kolejnych interakcjach wariant-zależnych.
|
||||
- Mapa brakujących elementów w nowym layoucie (patrz „Deferred Items" + STATE.md „Open observations").
|
||||
|
||||
**Concerns:**
|
||||
- Wizualny 1:1 fit z Figma może wymagać drobnych poprawek po rekompilacji SCSS (globalne reguły `.product-variants` mogą dominować).
|
||||
- CSS dla aktywnego wariantu (outline) to minimalistyczna interpretacja — Figma nie pokazywała active state; użytkownik może chcieć inne (np. tick icon, gruba ramka).
|
||||
|
||||
**Blockers:** None.
|
||||
|
||||
---
|
||||
*Phase: 01-product-variants-fix, Plan: 01*
|
||||
*Completed: 2026-04-23*
|
||||
310
.paul/phases/02-product-actions-fixes/02-01-PLAN.md
Normal file
310
.paul/phases/02-product-actions-fixes/02-01-PLAN.md
Normal file
@@ -0,0 +1,310 @@
|
||||
---
|
||||
phase: 02-product-actions-fixes
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["01-01"]
|
||||
files_modified:
|
||||
- themes/ayon/templates/catalog/product.tpl
|
||||
- themes/ayon/assets/js/custom.js
|
||||
- themes/ayon/assets/css/custom.scss
|
||||
autonomous: false
|
||||
delegation: off
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Port konfiguratora "piece" (wybór fragmentu tapety + odbicie lustrzane) ze starego layoutu do nowego (IP `== '89.69.31.86'`), z zachowaniem pełnej zgodności DOM kontraktu dla serializacji do koszyka (hidden inputs `is_crop`, `crop_pos_x/y`, `crop_width/height`, `piece_bg_top/left` w formie `#add-to-cart-or-refresh`).
|
||||
|
||||
## Purpose
|
||||
Użytkownik w nowym layoucie nie widzi i nie może ustawić wycinka tapety ani odbicia lustrzanego. W starym layoucie ten flow wysyłał wymiary fragmentu + pozycję + `is_crop` + mirror do koszyka. Bez portu — zamówienia z nowego layoutu nie mają informacji o kropie. Funkcja jest kluczowa dla produktu (sklep „na wymiar").
|
||||
|
||||
## Output
|
||||
Działający piece configurator w nowym layoucie: draggable `#piece` na zdjęciu produktu + kontrolki w bloku „Rozmiar i dostosowanie" + button `#button-mirror-reflection`. Hidden inputs w formie `#add-to-cart-or-refresh` aktualizują się przy interakcji. Stary layout nietknięty.
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Prior Work
|
||||
@.paul/phases/01-product-variants-fix/01-01-SUMMARY.md
|
||||
Phase 01 wprowadziło form `<form id="add-to-cart-or-refresh">` w gałęzi nowego layoutu (ale owija tylko variants) oraz pattern `prestashop.emit('updatedProduct', resp)` po własnym AJAX refresh — piece musi umieć się re-inicjalizować po zmianie wariantu (bo `.product_image_wrapper` jest `.html()`-replace'owany).
|
||||
|
||||
## Source Files — stary layout (wzór implementacji)
|
||||
@themes/ayon/templates/catalog/product.tpl (gałąź `!= '89.69.31.86'`, ok. linie 179-244 hidden inputs + `.product-block-piece` + `#button-mirror-reflection`, ok. linie 522-523 `.piece-left-positon` / `.piece-top-positon`)
|
||||
@themes/ayon/assets/js/custom.js (ok. linie 100-330 — `dragElement`, `checkedHandler`, change-handlery `#piece-width` / `#piece-height`, mirror toggle, update `#piece_bg_top/left` + `#product_crop_pos_x/y`)
|
||||
@themes/ayon/assets/css/custom.scss (reguły `.product-block-piece`, `#piece`, `#button-mirror-reflection`, `.piece-size-controls`, `.piece-size-values`)
|
||||
|
||||
## Source Files — nowy layout (cel modyfikacji)
|
||||
@themes/ayon/templates/catalog/product.tpl (gałąź `== '89.69.31.86'` — blok `product_variants` z `<form id="add-to-cart-or-refresh">`, blok `product_size` z pustym `<div class="product-box--data">`, `<div class="product_image_wrapper">`)
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Markup piece + mirror w nowym layoucie
|
||||
|
||||
```gherkin
|
||||
Given strona produktu jest renderowana dla IP 89.69.31.86 (nowy layout)
|
||||
When DOM się wczytuje
|
||||
Then istnieją: `#piece` jako child `.product_image_wrapper` (initially hidden),
|
||||
`#button-mirror-reflection`, `.product-block-piece` (z `#checkbox-piece`, `#piece-width`, `#piece-height`, `#piece-size-view`) w `.product-size-data .product-box--data`,
|
||||
`.piece-left-positon` i `.piece-top-positon` jako elementy pomocnicze,
|
||||
hidden inputs (`is_crop`, `crop_pos_x`, `crop_pos_y`, `crop_width`, `crop_height`, `piece_bg_top`, `piece_bg_left`) jako dzieci formy `#add-to-cart-or-refresh`
|
||||
```
|
||||
|
||||
## AC-2: Piece interaktywny — drag, checkbox, resize przez inputy, mirror
|
||||
|
||||
```gherkin
|
||||
Given nowy layout jest załadowany
|
||||
When użytkownik klika `#checkbox-piece`
|
||||
Then `#piece` staje się widoczny (fadeIn), inputy `#piece-width` i `#piece-height` stają się edytowalne,
|
||||
`$('#product_is_crop').val()` === '1'
|
||||
|
||||
When użytkownik przeciąga `#piece` po obrazie
|
||||
Then pozycja `#piece` się aktualizuje,
|
||||
`#product_crop_pos_x`, `#product_crop_pos_y`, `#piece_bg_left`, `#piece_bg_top` mają nowe wartości,
|
||||
`.piece-left-positon` i `.piece-top-positon` mają pozycję piksel
|
||||
|
||||
When użytkownik zmienia `#piece-width` / `#piece-height`
|
||||
Then wymiar `#piece` się aktualizuje, `#product_crop_width/height` też,
|
||||
`#piece-size-view` pokazuje `WxH`
|
||||
|
||||
When użytkownik klika `#button-mirror-reflection`
|
||||
Then `#piece` i `.product-images img.thumb` dostają klasę `.mirrored` (toggle)
|
||||
```
|
||||
|
||||
## AC-3: Form serialization — wartości trafiają do formy
|
||||
|
||||
```gherkin
|
||||
Given nowy layout, checkbox aktywny, piece przesunięty i zresize'owany
|
||||
When wykonamy `$('#add-to-cart-or-refresh').serialize()` w konsoli
|
||||
Then string zawiera: `is_crop=1`, `crop_pos_x=<N>`, `crop_pos_y=<N>`, `crop_width=<N>`, `crop_height=<N>`, `piece_bg_top=<N>`, `piece_bg_left=<N>` (obok istniejących `token`, `id_product`, `id_customization`, `group[...]=...` z Phase 01)
|
||||
```
|
||||
|
||||
## AC-4: Re-init po zmianie wariantu (AJAX refresh)
|
||||
|
||||
```gherkin
|
||||
Given piece jest włączony i ustawiony, użytkownik zmienia wariant kolorystyczny (click kafelka)
|
||||
When Phase 01 AJAX refresh wykona `$('.product_image_wrapper').html(resp.product_cover_thumbnails)` + emit `updatedProduct`
|
||||
Then `#piece` jest ponownie wstawiany do `.product_image_wrapper`, `dragElement` jest re-bound,
|
||||
stan (checkbox aktywny, wymiary, pozycja, mirror) jest zachowany lub resetowany do spójnego defaultu — jedno z, udokumentowane
|
||||
```
|
||||
|
||||
## AC-5: Zero regresji w starym layoucie
|
||||
|
||||
```gherkin
|
||||
Given użytkownik NIE jest na IP 89.69.31.86 (stary layout)
|
||||
When strona produktu się ładuje
|
||||
Then stary `.product-block-piece`, `#piece`, `#button-mirror-reflection` działają identycznie jak przed Phase 02
|
||||
(drag, resize, mirror, hidden inputs serialize)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Markup piece/mirror + hidden inputs w gałęzi nowego layoutu product.tpl</name>
|
||||
<files>themes/ayon/templates/catalog/product.tpl</files>
|
||||
<action>
|
||||
W gałęzi `{if $smarty.server.REMOTE_ADDR == '89.69.31.86'}`:
|
||||
|
||||
(a) Dodać 7 hidden inputs JAKO DZIECI formy `<form id="add-to-cart-or-refresh">` (obok variants grid, wewnątrz form):
|
||||
- `<input type="hidden" name="is_crop" value="0" id="product_is_crop">`
|
||||
- `<input type="hidden" name="crop_pos_x" value="0" id="product_crop_pos_x">`
|
||||
- `<input type="hidden" name="crop_pos_y" value="0" id="product_crop_pos_y">`
|
||||
- `<input type="hidden" name="crop_width" value="0" id="product_crop_width">`
|
||||
- `<input type="hidden" name="crop_height" value="0" id="product_crop_height">`
|
||||
- `<input type="hidden" name="piece_bg_top" id="piece_bg_top" value="">`
|
||||
- `<input type="hidden" name="piece_bg_left" id="piece_bg_left" value="">`
|
||||
|
||||
(b) W `<div class="product_image_wrapper">` (gałąź nowego layoutu) dodać overlay:
|
||||
- `<div id="piece" style="display:none;"></div>` jako rodzeństwo lub dziecko `.product-cover-thumbnails`
|
||||
- `#piece` musi mieć `background-image` ustawiony inline z URL okładki produktu (to co stary layout robi — odszukaj w starym markup'ie dokładny sposób; zachowaj tę samą zmienną Smarty)
|
||||
|
||||
(c) W bloku `{block name='product_size'}` (nowa gałąź), w pustym `<div class="product-box--data">` w `.product-box.product-size-data`, dodać wrapper `.product-size-data--new` i umieścić:
|
||||
- `<div class="product-block-piece">` z `<div class="product-bar-icon crop-icon">`, `<div class="piece-size-controls product-bar-box">` (zawiera `<span id="piece-size-view" class="strong">Wybierz rozmiar</span>`),
|
||||
`<div class="piece-size-controls hidden"><input type="checkbox" id="checkbox-piece"><label for="checkbox-piece">Wymiary tapety</label></div>`,
|
||||
`<div class="piece-size-values hidden"><input type="number" min="50" max="500" value="100" id="piece-width" readonly><input type="number" min="50" max="300" value="100" id="piece-height" readonly></div>`
|
||||
- `<div id="button-mirror-reflection"><img src="/themes/ayon/assets/images/odbicie-iustrzane.png" alt=""><p class="button-mirror-reflection-label">Odbicie lustrzane</p></div>`
|
||||
|
||||
(d) Dodać rodzeństwem (poza image wrapperem, gdziekolwiek w kontenerze): `<div class="piece-left-positon hidden">10</div>` `<div class="piece-top-positon hidden">10</div>` — wymagane przez `custom.js`.
|
||||
|
||||
(e) Użyć DOKŁADNIE tych samych ID co w starym layoucie — dzięki temu handlery w `custom.js` (`jQuery("#piece-width").change(...)`, itd.) bind'ują się do elementów w nowym layoucie identycznie. DUPLIKATY ID ze starym layoutem są OK — tylko jedna gałąź renderuje się na raz (IP-gated).
|
||||
|
||||
**Avoid:**
|
||||
- Nie zmieniać markup'u starej gałęzi (`!= '89.69.31.86'`) — `DO NOT CHANGE`.
|
||||
- Nie dotykać partial'a `_partials/product-variants.tpl`.
|
||||
- Nie zmieniać nazw `name="..."` hidden inputów — zmiana łamie zapis do koszyka po stronie serwera.
|
||||
</action>
|
||||
<verify>
|
||||
Załaduj produkt na nowym layoucie i w DevTools:
|
||||
- `document.getElementById('piece')` zwraca element
|
||||
- `document.getElementById('product_is_crop').value === '0'` na starcie
|
||||
- `document.getElementById('checkbox-piece')` istnieje
|
||||
- `document.getElementById('button-mirror-reflection')` istnieje
|
||||
- `$('#add-to-cart-or-refresh input[name="is_crop"]').length === 1`
|
||||
- Stary layout (przez drugi browser/IP) — bez zmian
|
||||
</verify>
|
||||
<done>AC-1 spełnione + częściowo AC-3 (hidden inputs istnieją w formie).</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: JS — piece init() + re-init po updatedProduct, zachowanie istniejących handlerów</name>
|
||||
<files>themes/ayon/assets/js/custom.js</files>
|
||||
<action>
|
||||
Ponieważ ID elementów są identyczne, istniejące handlery (binding bezpośredni `jQuery("#piece-width").change(...)`, `jQuery("#checkbox-piece").change(...)`, itd.) ZAPINAJĄ się na starcie niezależnie od layoutu. Kluczowe dwa ryzyka:
|
||||
|
||||
(1) `dragElement(document.getElementById("piece"))` w `checkedHandler` — to zapina mousedown na `#piece`. Po `.product_image_wrapper.html(resp.product_cover_thumbnails)` w Phase 01 AJAX refresh, `#piece` może być:
|
||||
- zniszczony (bo nie jest częścią `resp.product_cover_thumbnails`) → po AJAX trzeba go re-stworzyć lub przenieść przed replace'em
|
||||
- zachowany (jeśli nie jest w `.product_image_wrapper` w nowym layoucie) → wtedy OK
|
||||
|
||||
Decyzja: `#piece` NIE JEST dzieckiem kontenera replace'owanego przez AJAX. Umieść go jako sibling do `.product-cover-thumbnails` ale WEWNĄTRZ `.product_image_wrapper`, I przenieś go przed replacem (detach → replace → re-append), ALBO: umieść `#piece` jako overlay nad `.product_image_wrapper` (sibling, absolutnie pozycjonowany) aby AJAX replace nie ruszał go. **Preferowane: overlay sibling** — prostsze i mniej edge case'ów.
|
||||
|
||||
(2) W custom.js (miejsce po obecnym handlerze `change` wariantu w Phase 01), zarejestruj listener:
|
||||
```js
|
||||
prestashop.on('updatedProduct', function(event){
|
||||
// re-ensure #piece position sync po ew. resize wrappera
|
||||
if ($('#product_is_crop').val() === '1') {
|
||||
// re-trigger width/height change handlers to recompute background-position
|
||||
$('#piece-width').trigger('change');
|
||||
$('#piece-height').trigger('change');
|
||||
}
|
||||
});
|
||||
```
|
||||
Cel: piece pozostaje zsynchronizowany wizualnie po resize kontenera (różne warianty mogą mieć różne wymiary obrazu okładki).
|
||||
|
||||
(3) (Opcjonalnie, jeżeli inline background-image dla `#piece` jest ustawiany przez JS zamiast w Smarty) — w handlerze `updatedProduct` odświeżyć `#piece`'s `background-image` na bazie `resp.product_cover` lub nowego `img.thumb`.
|
||||
|
||||
**Avoid:**
|
||||
- Nie refactoruj istniejących handlerów piece — dokładaj tylko to co potrzebne dla nowego layoutu.
|
||||
- Nie duplikuj `dragElement` — użyj istniejącej funkcji.
|
||||
- Nie dotykaj handlera variant-change z Phase 01 (ok. istniejąca sekcja AJAX refresh).
|
||||
</action>
|
||||
<verify>
|
||||
W browserze (nowy layout):
|
||||
1. `$('#checkbox-piece').click()` → `#piece` widoczny, `#product_is_crop` = '1'
|
||||
2. Przeciągnij `#piece` myszką → `#product_crop_pos_x` i `#product_crop_pos_y` mają wartości != 0
|
||||
3. Zmień `#piece-width` na 200, trigger change → `#product_crop_width` = 200, `.piece-width-px` text = '200'
|
||||
4. Klik `#button-mirror-reflection` → `#piece.mirrored` true
|
||||
5. Zmień wariant kolorystyczny (click kafelka) → po AJAX refresh `#piece` nadal istnieje, stan zachowany
|
||||
</verify>
|
||||
<done>AC-2 i AC-4 spełnione.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: CSS — scoped styles i wizualna warstwa piece dla nowego layoutu</name>
|
||||
<files>themes/ayon/assets/css/custom.scss</files>
|
||||
<action>
|
||||
Reguły dla starego layoutu są już w `custom.scss` (`#piece`, `.product-block-piece`, `#button-mirror-reflection`, itd.). W nowym layoucie kontekst jest inny — `.product_image_wrapper` zamiast `.product-images`, osadzenie w `.product-size-data .product-box--data .product-size-data--new`.
|
||||
|
||||
Dopisać sekcję (na końcu pliku lub obok dotychczasowego scope Phase 01):
|
||||
|
||||
```scss
|
||||
body#product .product-size-data {
|
||||
.product-box--data { padding: 0; }
|
||||
.product-size-data--new {
|
||||
// layout wewnętrzny — piece controls + mirror side-by-side
|
||||
display: flex; align-items: center; gap: 16px;
|
||||
.product-block-piece { /* ... */ }
|
||||
.piece-size-controls, .piece-size-values { /* ... */ }
|
||||
#button-mirror-reflection { cursor: pointer; /* ... */ }
|
||||
}
|
||||
}
|
||||
|
||||
body#product .product_image_wrapper {
|
||||
position: relative; // anchor dla #piece overlay
|
||||
#piece {
|
||||
position: absolute;
|
||||
background-size: cover;
|
||||
cursor: move;
|
||||
z-index: 10;
|
||||
// display:none default (JS fadeIn)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**NIE ustawiaj globalnie na `#piece` / `.product-block-piece`** — to zepsuje stary layout. Scope'uj pod `body#product .product-size-data .product-size-data--new` oraz `body#product .product_image_wrapper #piece`.
|
||||
|
||||
Edytuj TYLKO `custom.scss`. `custom.css` jest auto-generowany przez watcher user'a (feedback memory `.claude/memory/feedback_scss_only.md`).
|
||||
|
||||
Zadanie nie wymaga pixel-perfect fit — celem jest: piece widoczny, draggable, mieści się na obrazie; controls czytelne w panelu. Vizualny polish (pixel fit) odnotować jako Deferred jeżeli nie ma ref'u Figma.
|
||||
|
||||
**Avoid:**
|
||||
- Nie usuwaj ani nie modyfikuj istniejących reguł (stary layout).
|
||||
- Nie dodawaj `!important`.
|
||||
</action>
|
||||
<verify>
|
||||
Po rekompilacji SCSS (watcher usera) w `custom.css` pojawia się nowy blok `body#product .product-size-data` i `body#product .product_image_wrapper #piece`. Stary layout w DevTools nadal ma działające stare reguły (brak konfliktów w DevTools Elements panel).
|
||||
</verify>
|
||||
<done>AC-1 spełnione wizualnie (kontrolki widoczne, piece z position/cursor), AC-5 zachowane.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>
|
||||
- Markup piece + mirror + hidden inputs w gałęzi nowego layoutu `product.tpl`
|
||||
- JS init + re-init na `updatedProduct` w `custom.js`
|
||||
- Scoped SCSS w `custom.scss`
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
1. Nowy layout (IP 89.69.31.86):
|
||||
- Przejdź na dowolny produkt z wariantami
|
||||
- W bloku „Rozmiar i dostosowanie" kliknij checkbox „Wymiary tapety" — `#piece` powinien się pokazać nad zdjęciem
|
||||
- Przeciągnij `#piece` myszką — pozycja się zmienia
|
||||
- Zmień wartość `#piece-width` i `#piece-height` — `#piece` się przeskalowuje, `#piece-size-view` pokazuje `WxH`
|
||||
- Kliknij `#button-mirror-reflection` — piece i thumb dostają klasę `.mirrored`
|
||||
- W DevTools: `$('#add-to-cart-or-refresh').serialize()` zawiera `is_crop=1&crop_pos_x=<N>&crop_pos_y=<N>&crop_width=<N>&crop_height=<N>&piece_bg_top=<N>&piece_bg_left=<N>`
|
||||
- Kliknij inny wariant kolorystyczny — po AJAX refresh piece dalej istnieje i działa
|
||||
2. Stary layout (dowolny inny IP):
|
||||
- Otwórz produkt w zwykłej przeglądarce (IP != 89.69.31.86)
|
||||
- Sprawdź że piece/mirror/resize/drag działają bez regresji
|
||||
- Dodaj do koszyka — wartości crop zapisują się do customization (jak wcześniej)
|
||||
3. Playwright (opcjonalnie jeżeli dostępny): zautomatyzuj kroki 1 i zaloguj wyniki.
|
||||
</how-to-verify>
|
||||
<resume-signal>Napisz „approved" aby zamknąć APPLY, lub opisz problemy (z dokładnymi krokami odtworzenia) do naprawy.</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- Gałąź `{if $smarty.server.REMOTE_ADDR != '89.69.31.86'}` w `themes/ayon/templates/catalog/product.tpl` (stary layout, produkcja).
|
||||
- Istniejące handlery piece w `themes/ayon/assets/js/custom.js` (linie ~100-330 stare binding'i — tylko dodawaj nowe listenery/inicjalizacje, nie modyfikuj).
|
||||
- Handler variant-change z Phase 01 w `custom.js` — pattern ustalony, tylko dodaj listener `prestashop.on('updatedProduct', ...)`.
|
||||
- Partial `themes/ayon/templates/catalog/_partials/product-variants.tpl` — shared, nie dotykać.
|
||||
- Wszystkie istniejące reguły SCSS dla starego layoutu.
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie naprawiamy „Dodaj do koszyka" w tym planie (osobny plan fazy 02).
|
||||
- Nie wypełniamy pustych bloków `.product-protect`, `.product-installation`, `.product-order-sample` (osobny plan fazy 02).
|
||||
- Nie dodajemy resize handles na `#piece` („bonus" z rozmowy z userem) — deferred do ewentualnego Plan 02-02 jako feature add-on. W tym planie: drag + resize przez inputy, jak stary layout.
|
||||
- Nie dodajemy nowych zależności (jquery-ui resizable, interact.js, itp.).
|
||||
- Nie zmieniamy server-side logiki zapisu kropu do koszyka — polegamy na tym że nazwy hidden inputs są identyczne ze starym layoutem.
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] Hidden inputs obecne wewnątrz `#add-to-cart-or-refresh` w nowym layoucie (AC-1, AC-3)
|
||||
- [ ] `#piece`, `.product-block-piece`, `#button-mirror-reflection` widoczne i interaktywne w nowym layoucie (AC-2)
|
||||
- [ ] Drag + resize przez inputy + mirror aktualizują hidden inputs (AC-2, AC-3)
|
||||
- [ ] Po zmianie wariantu kolorystycznego piece nadal działa (AC-4)
|
||||
- [ ] Stary layout bez regresji w pełnym cyklu (drag → resize → mirror → add-to-cart → customization zapisuje się) (AC-5)
|
||||
- [ ] `custom.scss` edytowany, `custom.css` nie tknięty ręcznie
|
||||
- [ ] Brak duplikatów funkcji JS, brak konfliktów CSS
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie 3 auto taski ukończone + checkpoint human-verify z "approved"
|
||||
- 5/5 AC satysfakcjonowane
|
||||
- Zero regresji na starym layoucie (weryfikowane checkpoint'em)
|
||||
- Dokumentacja decyzji i deviation'ów w SUMMARY
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/02-product-actions-fixes/02-01-SUMMARY.md`
|
||||
</output>
|
||||
217
.paul/phases/02-product-actions-fixes/02-01-SUMMARY.md
Normal file
217
.paul/phases/02-product-actions-fixes/02-01-SUMMARY.md
Normal file
@@ -0,0 +1,217 @@
|
||||
---
|
||||
phase: 02-product-actions-fixes
|
||||
plan: 01
|
||||
subsystem: ui
|
||||
tags: [prestashop, smarty, jquery, scss, piece, crop, fancybox, module-hook-compat]
|
||||
|
||||
requires:
|
||||
- phase: 01-product-variants-fix
|
||||
provides:
|
||||
- Form `<form id="add-to-cart-or-refresh">` w gałęzi nowego layoutu — Plan 02-01 dołącza do niej 8 hidden inputs dla crop/mirror
|
||||
- Marker class `.product-variants-data--new` — Plan 02-01 wykorzystuje do scope'u JS init (`setTimeout` check)
|
||||
- Własny AJAX refresh w custom.js (`action=refresh` + in-place DOM update) — Plan 02-01 dopina listener `prestashop.on('updatedProduct')` do re-bindowania dragElement po replace `.product_image_wrapper`
|
||||
|
||||
provides:
|
||||
- Działający konfigurator „piece" (wybór fragmentu tapety) w nowym layoucie: popup trigger w `.product-size-data`, fancybox z input'ami Szerokość/Wysokość, piece overlay na zdjęciu
|
||||
- 8 hidden inputów `is_crop`, `is_reflection`, `crop_pos_x/y`, `crop_width/height`, `piece_bg_top/left` w formie `#add-to-cart-or-refresh` (nowy layout)
|
||||
- Defensive setup (no-op override `totalpriceinfospecific`/`prod` + DOM stubs w custom.js) — unblocks istniejące piece handlery w nowym layoucie bez modyfikacji inline-scriptów z module hooks
|
||||
|
||||
affects:
|
||||
- Plan 02-02 (add-to-cart) — hidden inputs już w formie, gotowe do POST'owania do koszyka. Także no-op override `totalpriceinfospecific` oznacza że logika cen wymaga innego mechanizmu w nowym layoucie.
|
||||
- Plan 02-03 (empty blocks) — pattern scope'owania CSS pod `.product-size-data .product-size-data--new` do reużycia dla `.product-protect`, `.product-installation`, `.product-order-sample`
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Shared partial reuse: zamiast duplikować element między layoutami, reużyć istniejący (#piece z product-cover-thumbnails.tpl)"
|
||||
- "No-op override globalnych funkcji z module hook (`totalpriceinfospecific`, `prod`) jako alternative do modyfikacji inline-scriptów których nie można zmienić"
|
||||
- "DOM stubs injection via JS zamiast template — obchodzi Smarty cache + FTP sync delay"
|
||||
- "setTimeout(600) init zamiast `$(document).ready` — defer poza queue ready callbacków które crash'ują na brakujących DOM elementach w nowym layoucie"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- .paul/phases/02-product-actions-fixes/02-01-PLAN.md
|
||||
- .paul/phases/02-product-actions-fixes/02-01-SUMMARY.md
|
||||
modified:
|
||||
- themes/ayon/templates/catalog/product.tpl
|
||||
- themes/ayon/assets/js/custom.js
|
||||
- themes/ayon/assets/css/custom.scss
|
||||
|
||||
key-decisions:
|
||||
- "`#piece` reużywany z product-cover-thumbnails.tpl — nie duplikujemy w product.tpl"
|
||||
- "totalpriceinfospecific / prod override'owane na no-op w nowym layoucie (crash z powodu brakujących DOM deps)"
|
||||
- "DOM stubs injection w JS zamiast template (cache/sync unreliability)"
|
||||
- "Piece NIE auto-init'uje się — pojawia się dopiero po kliknięciu popup trigger (user feedback w checkpoint)"
|
||||
- "Defensive guard `.pp_stick_parent` w click handler (element exists only w starym layoucie)"
|
||||
|
||||
patterns-established:
|
||||
- "Scope'owanie CSS dla bloków nowego layoutu: `body#product .<block-class> .<block-class>--new {...}` — wzór do reużycia dla kolejnych bloków (product-protect, product-installation, etc.)"
|
||||
- "Obrona przed cross-layout breakage module hooków: setTimeout init w custom.js z no-op override + DOM stubs; nie ruszamy inline-scriptów"
|
||||
- "Reużywanie state holders jako hidden inputów: `<input type='checkbox' style='display:none'>` zamiast tworzenia osobnego state-management, istniejące handlery bindują się do ID identycznych ze starym layoutem"
|
||||
|
||||
duration: ~3.5h (z live debug'iem via Playwright)
|
||||
started: 2026-04-23T19:30:00Z
|
||||
completed: 2026-04-23T22:30:00Z
|
||||
---
|
||||
|
||||
# Phase 02 Plan 01: Piece/crop configurator (nowy layout) — Summary
|
||||
|
||||
**Port konfiguratora „piece" (wybór fragmentu tapety + mirror) do nowego layoutu z zachowaniem pełnego kontraktu serializacji do koszyka. Popup trigger + fancybox — odzwierciedla user flow starego layoutu.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~3.5h |
|
||||
| Started | 2026-04-23T19:30:00Z |
|
||||
| Completed | 2026-04-23T22:30:00Z |
|
||||
| Tasks | 3 auto + 1 checkpoint (approved po iteracji) |
|
||||
| Files modified | 3 source + 2 PAUL meta |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Markup piece + mirror w nowym layoucie | Pass | `#piece` reużyty z shared partial (nie duplikowany), controls w `.product-size-data .product-box--data`, 8 hidden inputów w `#add-to-cart-or-refresh`. Initial text „Wybierz rozmiar" (zamiast stałego „100x100" per user feedback). |
|
||||
| AC-2: Piece interaktywny | Pass | Popup otwiera się po kliknięciu `.piece-summary`, zmiana wymiarów w popup → piece rescale (100x100 → 200x150 = 40%×50% kontenera), hidden inputs `#product_crop_width/height` sync'ują. Drag działa (dragElement z istniejącego flow). Verified via Playwright. |
|
||||
| AC-3: Form serialization | Pass | `$('#add-to-cart-or-refresh').serialize()` zwraca pełny ciąg: `token=...&id_product=...&id_customization=...&is_crop=1&is_reflection=0&crop_pos_x=200&crop_pos_y=100&crop_width=200&crop_height=150&piece_bg_top=-104.398&piece_bg_left=-208.804&group[4]=6`. Wszystkie pola aktualne. |
|
||||
| AC-4: Re-init po zmianie wariantu | Not tested | AC zakładał re-bind dragElement po `updatedProduct`. Listener dodany (custom.js:681-692), ale live test zmiany wariantu nie przeprowadzony w tej sesji. Markowane jako "not verified" — do re-verify w Plan 02-02 lub osobno. |
|
||||
| AC-5: Zero regresji w starym layoucie | Not tested | Stary layout (IP != 89.69.31.86) nie był testowany w tej sesji. Zmiany w custom.js (no-op override, setTimeout init, stubs injection) są scope'owane do nowego layoutu via `$('.product-variants-data--new').length` check — nie powinny dotknąć starego. Do weryfikacji przed commitowaniem phase. |
|
||||
|
||||
**Modyfikacja AC:** Plan pierwotnie zakładał piece auto-init'ujący się na load (inline `<style>` force visibility + `checkedHandler` w setTimeout). User feedback w trakcie checkpoint zmienił wymaganie: piece pojawia się dopiero po kliknięciu trigger. AC-2 satisfied zgodnie z tym zmodyfikowanym flow.
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- **Port piece configurator z zachowaniem kontraktu** — identyczne ID i nazwy hidden inputów ze starym layoutem → istniejące JS handlers bindują się automatycznie, serwer-side logic nie wymaga zmian.
|
||||
- **Popup match z Figma/screenshot** — istniejący fancybox flow (custom.js:535-557) działa w nowym layoucie po dodaniu trigger'a `<a class="fancybox-size-controls piece-summary">` i defensywnych guard'ów.
|
||||
- **Rozwiązanie 5 ukrytych problemów wykrytych w live debug** (Playwright): duplikat `#piece`, CSS `:has()` edge case, ready queue fighting, inline-script crash, brakujący `.pp_stick_parent`. Każdy udokumentowany w Decisions.
|
||||
- **Pattern defensywnego setup'u dla module hook compatibility** — no-op override + DOM stubs w JS zamiast template. Reużywalny przy naprawach innych funkcji nowego layoutu.
|
||||
|
||||
## Task Commits
|
||||
|
||||
Task commits nie zostały jeszcze utworzone — APPLY wykonywany inline (delegation: off) bez per-task commitów. Commit fazowy wykona `transition-phase` po zakończeniu całej fazy 02 (plany 02-01, 02-02, 02-03+).
|
||||
|
||||
| Task | Commit | Type | Description |
|
||||
|------|--------|------|-------------|
|
||||
| Task 1 (markup) | pending | feat | Piece trigger + controls + 8 hidden inputs w gałęzi nowego layoutu |
|
||||
| Task 2 (JS) | pending | feat | setTimeout init z no-op override + stubs, `prestashop.on('updatedProduct')` listener, defensive fancybox guard |
|
||||
| Task 3 (CSS) | pending | feat | Scoped styles `.product-size-data--new`, `.piece-summary`, `#button-mirror-reflection` |
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `themes/ayon/templates/catalog/product.tpl` | Modified | Gałąź `== '89.69.31.86'`: 8 hidden inputs w `#add-to-cart-or-refresh`, `.piece-summary` anchor w `.product-size-data .product-box--data`, wrapper `.product-size-data--new`, mirror button, hidden state (checkbox, width/height, position divs). Initial `#piece-size-view` text = „Wybierz rozmiar". |
|
||||
| `themes/ayon/assets/js/custom.js` | Modified | +~60 linii: `prestashop.on('updatedProduct', ...)` listener do re-bindu dragElement po AJAX refresh, setTimeout(600) init w nowym layoucie (override totalpriceinfospecific/prod na no-op, wstrzyknięcie DOM stubs), defensive `.pp_stick_parent` guard w `.fancybox-size-controls` click handler (ok. linia 540). |
|
||||
| `themes/ayon/assets/css/custom.scss` | Modified | +~60 linii na końcu pliku: Phase 02 scope block pod `body#product .product-size-data .product-size-data--new` (flex layout, piece-summary trigger styling, mirror button hover). Istniejące `.product-images .piece` ze starego layoutu applikują się automatycznie do `#piece` z shared partial. |
|
||||
| `.paul/phases/02-product-actions-fixes/02-01-PLAN.md` | Created | Plan fazy 02 plan 01 |
|
||||
| `.paul/phases/02-product-actions-fixes/02-01-SUMMARY.md` | Created | Ten plik |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Reuse `#piece` z `product-cover-thumbnails.tpl` (shared partial), NIE duplikować | Pierwotna próba dodania osobnego `<div id="piece">` jako sibling `.product_image_wrapper` tworzyła duplikat ID → two elements z tym samym ID → my element renderował biała pusta ramkę, oryginalny (z pattern bg) był niewidoczny gdzie indziej. | Mniej markup, reużycie istniejącego styling (`.product-images .piece` ze starego SCSS). |
|
||||
| Override `totalpriceinfospecific` / `prod` na no-op w custom.js setTimeout | Inline-script z module hook (squaremeter) czyta `#totalpriceinfo.style`, `#product-details.data()`, robi `JSON.parse(undefined)` — wszystko null/undefined w nowym layoucie. Error propaguje przez `trigger('change')` na `#piece-width` i abortuje `checkedHandler` przed `is_crop=1`. | `checkedHandler` działa w nowym layoucie. Cena calculation wyłączona — trzeba zapewnić alternatywny mechanizm w Plan 02-02. |
|
||||
| DOM stubs (`#totalpriceinfo`, etc.) wstrzykiwane przez JS, nie przez template | Smarty template cache + ręczny FTP sync powodował że stuby w `product.tpl` nie docierały do browsera (page wciąż używała starej skompilowanej wersji templatu). JS deploys są bardziej deterministyczne (pojedynczy plik, jasne cache semantics). | Stuby zawsze w DOM w nowym layoucie. Template pozostaje czysty. |
|
||||
| Piece NIE auto-init'uje się na load (user feedback w checkpoint) | User: „kwadrat pojawia się dopiero po kliknieciu przycisku cm — kliknij aby zmienić". Zmiana zachowania vs pierwotny plan. | Load = placeholder + hidden piece. Click = popup + piece fadeIn. Oszczedność initial clutter na zdjęciu. |
|
||||
| Defensive guard `.pp_stick_parent` w fancybox-size-controls click handler | Element istnieje tylko w starym layoucie (PS sidebar). Bez guard'a `$('.pp_stick_parent').offset().top` rzuca TypeError, aborts handler przed `$.fancybox()`. | Popup działa w obu layoutach. Old layout nieruszony (guard = if empty → skip). |
|
||||
| `piece-width/height` hidden state jako `style="display:none;"` inputs w markup (nie w popup JS template) | Istniejące JS handlery (custom.js:275-300) bindują się do ID `#piece-width`/`#piece-height` przez direct binding. Muszą istnieć w DOM at ready time. Popup używa `#fancy-piece-width/height` i sync'uje do hidden state on confirm. | Zero zmian w istniejącej logice. Clean separation: popup UI × state inputs. |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 5 | Niezbędne — bez nich piece nie działał w nowym layoucie |
|
||||
| Scope additions | 3 | Defensive setup (override + stubs + guard) — wymagane przez module hook compat |
|
||||
| Deferred | 1 | AC-5 (stary layout regresja test) + AC-4 (variant AJAX re-init) nie zweryfikowane w tej sesji |
|
||||
|
||||
**Total impact:** Plan wykonany z istotnymi auto-fixami znalezionymi przez Playwright live debug. Zero scope creep poza niezbędnymi do działania. Zmiana user-facing behavior (piece auto-init → click-to-show) uzgodniona z userem w trakcie checkpoint.
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. Duplikat `#piece` — dwa elementy z tym samym ID**
|
||||
- **Found during:** Task 1 (markup) → live Playwright snapshot wykrył `pieceCount: 1` ale `pieceStyles.backgroundImage: url(".../birds-in-the-fog.jpg")` — element pochodzi ze shared partial `product-cover-thumbnails.tpl:53`.
|
||||
- **Issue:** Dodałem własny `<div id="piece">` jako sibling `.product_image_wrapper`. Powstał duplikat ID (invalid HTML + konflikt JS/CSS).
|
||||
- **Fix:** Usunięty własny `<div id="piece">`. Reużywamy istniejący z partial. Usunięty marker class `product_image--new` jako nieużywany.
|
||||
- **Files:** `themes/ayon/templates/catalog/product.tpl`, `themes/ayon/assets/css/custom.scss`
|
||||
- **Verification:** `document.querySelectorAll('#piece').length === 1` + `pieceParentClass: thumb-container (LI)`
|
||||
|
||||
**2. CSS `:has(> #piece)` selector — edge case w kontekście**
|
||||
- **Found during:** Task 3 CSS + live test.
|
||||
- **Issue:** `body#product .product_image:has(> #piece) { ... #piece { display: block } }` nie dawał expected override. Compiled CSS selector miał `display: block` ale niekoniecznie match'ował DOM tree (layout-dependent).
|
||||
- **Fix:** Usunięty `:has()` selector. Reużywamy istniejące stylowanie `.product-images .piece` ze starego SCSS (linia 623) — applikuje się bo `#piece` jest w `.product-images` (z shared partial).
|
||||
- **Files:** `themes/ayon/assets/css/custom.scss`
|
||||
- **Verification:** Live test — `#piece` widoczny po kliknieciu popup trigger (display: block z fadeIn).
|
||||
|
||||
**3. Inline-script crash: `totalpriceinfospecific` TypeError `null.style`**
|
||||
- **Found during:** Task 4 live verify (checkpoint).
|
||||
- **Issue:** Module hook injectuje inline script z funkcją `totalpriceinfospecific` która robi `document.getElementById('totalpriceinfo').style.display = 'block'`. W nowym layoucie `#totalpriceinfo` nie istnieje. Funkcja wywoływana przez `#piece-width change handler` (custom.js:281). Error propaguje przez `trigger('change')` w checkedHandler (custom.js:183) — abortuje przed `is_crop=1`.
|
||||
- **Fix:** W setTimeout init: `window.totalpriceinfospecific = function() {};` (no-op override). Plus DOM stubs dla pozostałych elementów (`#custom-wallpaper-price`, `#custom-wallpaper-price-label`, `#quantity_wanted*`).
|
||||
- **Files:** `themes/ayon/assets/js/custom.js`
|
||||
- **Verification:** Playwright: po init `totalpriceinfospecific.toString()` = no-op, `checkedHandler` dochodzi do `is_crop=1` bez error.
|
||||
|
||||
**4. Inline-script crash (wariant #2): `JSON.parse(undefined)` w `totalpriceinfospecific`**
|
||||
- **Found during:** Task 4 live verify — po stub'owaniu `#totalpriceinfo` error zmienił się na `"undefined" is not valid JSON` przy `$('#product-details').data('product').quantity_discounts`.
|
||||
- **Issue:** Funkcja ma WIELE DOM dependencies (nie tylko `#totalpriceinfo`). Stubowanie każdego to moving target.
|
||||
- **Fix:** Override całej funkcji na no-op (zamiast stubowania jej DOM). W nowym layoucie price calc i tak musi przejść inną drogą (do zaplanowania w Plan 02-02).
|
||||
- **Files:** `themes/ayon/assets/js/custom.js`
|
||||
- **Verification:** Playwright: `checkedHandler` runs clean, żadnych JSON.parse errorów w console.
|
||||
|
||||
**5. Existing ready() fighting my init: checkbox unchecked + values=50**
|
||||
- **Found during:** Task 4 live verify — po override totalpriceinfospecific state wciąż `checked: false`, `piece-width: 50`.
|
||||
- **Issue:** `custom.js:209-214` ma `$(document).ready(function() { jQuery('#checkbox-piece').prop('checked', false); $('#piece-width').val(50); $('#piece-height').val(50); checkedHandler(...) })`. Default init dla starego layoutu. Moje setTimeout(600) odpalał się PO tym i wywoływał `checkedHandler($('#checkbox-piece'))` z **unchecked** checkbox → else branch (disabled state).
|
||||
- **Fix (iteracja 1):** W setTimeout: `$('#checkbox-piece').prop('checked', true); $('#piece-width').val(100); $('#piece-height').val(100);` przed `checkedHandler`. Usunięty po user feedback (piece ma się nie pokazywać na load).
|
||||
- **Fix (iteracja 2, finalny):** Usunięty `checkedHandler` call w ogóle — piece pojawia się tylko po klikn. trigger'a popupu przez użytkownika. Init tylko wstrzykuje stuby + override funkcji.
|
||||
- **Files:** `themes/ayon/assets/js/custom.js`
|
||||
- **Verification:** Playwright: po load `pieceVisible: false` ✓, po `$('.piece-summary')[0].click()` → `pieceVisible: true, productIsCrop: 1, fancyboxOpen: true` ✓.
|
||||
|
||||
### Scope Additions
|
||||
|
||||
**1. No-op override `totalpriceinfospecific` + `prod`** (nie w planie) — wymagane dla module hook compat, inaczej piece crash'uje.
|
||||
**2. DOM stubs injection via JS** (nie w planie) — wymagane jako alternative do niezawodnego deploy'u template'u.
|
||||
**3. Defensive `.pp_stick_parent` guard** (nie w planie) — wymagane żeby popup się otwierał w nowym layoucie.
|
||||
|
||||
### Deferred Items
|
||||
|
||||
- **AC-5 regression test starego layoutu** — nie przeprowadzony w tej sesji. Moje zmiany w custom.js są scope'owane pod `.product-variants-data--new` check (jest tylko w nowym layoucie), więc teoretycznie stary nietknięty. Do weryfikacji przed commitowaniem fazy 02 (transition).
|
||||
- **AC-4 re-init po AJAX variant change** — listener dodany, ale live test zmiany wariantu (click na kafelek → AJAX refresh → `#piece` re-rendered → dragElement re-binds) nie przeprowadzony. Do weryfikacji w kolejnej sesji.
|
||||
- **Mirror button end-to-end** — handler istnieje w custom.js, w nowym layoucie ma własny przycisk w markup, ale nie testowany klick → `#piece.mirrored` + `#product_is_reflection=1`. Do weryfikacji.
|
||||
- **Piece-size-view wording** — po kliknieciu popup pokazuje „50x50" (default values) zamiast „Wybierz rozmiar". Cosmetic, opcjonalny future improvement.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
| Issue | Resolution |
|
||||
|-------|------------|
|
||||
| Browser cache serwował stary `custom.js` mimo że server miał nowy | Force reload (Ctrl+Shift+Del + clear cache, lub DevTools Network → Disable cache + F5). Playwright: fetch z `?nc=<timestamp>` + dynamic eval init block aby zsynchronizować live state z latest JS. Rekomendacja: w produkcji dodać cache-buster `?v=<version>` do `<script src>` tag'a (Plan 02-02 lub osobno). |
|
||||
| PrestaShop Smarty cache + FTP sync delay sprawiał że template edits nie dochodziły do browsera | Przeniesienie wszystkich stubs z template do JS (custom.js). Rekomendacja: po każdej zmianie w `.tpl` → Admin PS → Performance → Clear cache. |
|
||||
| `dispatch`/`trigger` w jQuery forwardsem propagują błąd z nested handler do caller — cały flow abort'uje | Try/catch nie wystarczy (handler sam w sobie rzuca, nie ma sposobu catch poza patchowaniem). Override problematycznej funkcji (no-op) to jedyne czyste rozwiązanie bez modyfikacji nietuchable inline-scriptów. |
|
||||
|
||||
## Skill audit
|
||||
|
||||
SPECIAL-FLOWS.md nie skonfigurowane. W Plan 02-01 użyte:
|
||||
- `mcp__plugin_playwright_playwright__*` — **krytyczne**. Bez live debug (eval w page context, fetch current script, inspect runtime state) nie dalo by się znaleźć inline-script crashy. Multi-round testing po każdej poprawce.
|
||||
- Context-mode `ctx_execute` + `ctx_batch_execute` — do eksploracji repo bez zanieczyszczania context window.
|
||||
|
||||
**Rekomendacja:** Dodać Playwright MCP jako required skill dla dalszych planów Phase 02 (add-to-cart, empty blocks) — live debug w działającej produkcji jest kluczowy.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Hidden inputy crop/mirror w formie `#add-to-cart-or-refresh` → gotowe do POST'owania do koszyka przez Plan 02-02.
|
||||
- Pattern defensywnego setup'u (no-op override + DOM stubs + setTimeout init) reużywalny dla kolejnych napraw module-hook-dependent funkcji.
|
||||
- Pattern scope'owania CSS pod `.<block>-data--new` do reużycia dla pustych bloków.
|
||||
- Playwright MCP workflow ustalony — potwierdzone że live debug jest niezbędny.
|
||||
|
||||
**Concerns:**
|
||||
- `totalpriceinfospecific` no-op override wylacza cene calculation w nowym layoucie. Plan 02-02 musi zapewnić alternatywne mechanizm (prawdopodobnie przez PrestaShop `prestashop.on('updatedProduct', ...)` listener ktory czyta `resp.product_prices`).
|
||||
- Stary layout nie zweryfikowany end-to-end po moich zmianach w custom.js. Ryzyko małe (scope'owane pod marker class), ale do weryfikacji przed commitem phase'a.
|
||||
- Variant AJAX refresh + re-init piece dragElement — listener dodany ale nie przetestowany. Edge case'y (różne wymiary obrazu między wariantami, state preservation) do przejrzenia.
|
||||
|
||||
**Blockers:** None — Plan 02-02 (add-to-cart) może wejść do planowania.
|
||||
|
||||
---
|
||||
*Phase: 02-product-actions-fixes, Plan: 01*
|
||||
*Completed: 2026-04-23*
|
||||
389
.paul/phases/02-product-actions-fixes/02-02-PLAN.md
Normal file
389
.paul/phases/02-product-actions-fixes/02-02-PLAN.md
Normal file
@@ -0,0 +1,389 @@
|
||||
---
|
||||
phase: 02-product-actions-fixes
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["02-01"]
|
||||
files_modified:
|
||||
- themes/ayon/templates/catalog/product.tpl
|
||||
- themes/ayon/assets/js/custom.js
|
||||
- themes/ayon/assets/css/custom.scss
|
||||
autonomous: false
|
||||
delegation: off
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Uruchomić „Dodaj do koszyka" end-to-end w nowym layoucie strony produktu (IP 89.69.31.86): klik w przycisk → walidacja piece (wymiary wybrane) → POST do `/cart` z pełnym body (token + id_product + id_customization + qty + 8 hidden inputów crop/mirror) → success feedback + odświeżenie cart widget w headerze bez page reload.
|
||||
|
||||
## Purpose
|
||||
Bez tej funkcjonalności nowy layout nie nadaje się do testów — klient nie zatwierdzi go jako alternatywy dla starego do czasu aż dodawanie do koszyka działa identycznie (feature parity). Plan 02-01 (piece) dostarczył hidden inputy gotowe do POST'owania; Plan 02-02 domyka kontrakt: od kliknięcia przycisku do potwierdzenia że produkt trafił do koszyka.
|
||||
|
||||
## Output
|
||||
- Działający add-to-cart flow w nowym layoucie zweryfikowany live (Playwright).
|
||||
- Listener `prestashop.on('updatedCart', ...)` lub równoważny mechanizm odświeżający cart widget (header counter / modal) po pomyślnym POST.
|
||||
- Success/error UX zgodny ze starym layoutem (toast modal / redirect do koszyka — do ustalenia w Task 1).
|
||||
- Regresja starego layoutu = zero (zmiany scope'owane pod marker class `.product-variants-data--new` albo gałąź `if REMOTE_ADDR == '89.69.31.86'`).
|
||||
- Summary: `.paul/phases/02-product-actions-fixes/02-02-SUMMARY.md`.
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Prior Work
|
||||
@.paul/phases/02-product-actions-fixes/02-01-SUMMARY.md
|
||||
# Plan 02-01 dostarczył 8 hidden inputów w `#add-to-cart-or-refresh`:
|
||||
# is_crop, is_reflection, crop_pos_x, crop_pos_y, crop_width, crop_height,
|
||||
# piece_bg_top, piece_bg_left. Gotowe do POST'owania. Również nałożył no-op
|
||||
# override na `totalpriceinfospecific`/`prod` — to wpływa na kalkulację ceny,
|
||||
# ale NIE na sam POST (to osobny plan).
|
||||
|
||||
## Source Files
|
||||
@themes/ayon/templates/catalog/product.tpl
|
||||
@themes/ayon/templates/catalog/_partials/product-add-to-cart.tpl
|
||||
@themes/ayon/assets/js/custom.js
|
||||
@themes/ayon/assets/css/custom.scss
|
||||
|
||||
## External Reference
|
||||
# PrestaShop 1.7 core cart flow (dla orientacji — NIE modyfikujemy core):
|
||||
# - Handler globalny: `$(document).on('click', '[data-button-action=add-to-cart]', ...)`
|
||||
# - POST: `urls.pages.cart` z `add=1&action=update&id_product=...&qty=...&token=...`
|
||||
# - Response JSON: { success, hasError, errors, cart, ... }
|
||||
# - Eventy: `prestashop.emit('updatedCart', { resp, reason })` po sukcesie
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
## Required Skills
|
||||
|
||||
<!-- SPECIAL-FLOWS.md jeszcze nie istnieje (rekomendacja z Plan 02-01 do utworzenia). -->
|
||||
<!-- Dla Plan 02-02 skills section jest minimalna: Playwright MCP wymagany do Task 1 diagnosis. -->
|
||||
|
||||
| Skill | Priority | When to Invoke | Loaded? |
|
||||
|-------|----------|----------------|---------|
|
||||
| Playwright MCP (`mcp__plugin_playwright_playwright__*`) | required | Task 1 live diagnosis, Task 3 regression test | ○ |
|
||||
| context-mode (`mcp__plugin_context-mode_context-mode__*`) | optional | Exploracja kodu PS core / custom.js bez zanieczyszczania kontekstu | ○ |
|
||||
|
||||
**BLOCKING:** Playwright MCP wymagany w Task 1 — bez live inspection nie da się ustalić czy PS core handler przechwytuje click.
|
||||
|
||||
## Skill Invocation Checklist
|
||||
- [ ] Playwright MCP dostępny (tools `mcp__plugin_playwright_playwright__browser_*`)
|
||||
- [ ] Dostęp do środowiska z IP `89.69.31.86` (lub sposób na spoof'owanie — do ustalenia w Task 1)
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Add-to-cart button submitowalny w nowym layoucie
|
||||
```gherkin
|
||||
Given strona produktu w nowym layoucie (REMOTE_ADDR == '89.69.31.86')
|
||||
And piece został skonfigurowany (#checkbox-piece checked, width/height > 0)
|
||||
When użytkownik klika przycisk "Dodaj do koszyka" (`[data-button-action=add-to-cart]`)
|
||||
Then request POST wychodzi do urls.pages.cart
|
||||
And body zawiera: token, id_product, id_customization, qty, is_crop=1, crop_pos_x/y, crop_width, crop_height, piece_bg_top/left, is_reflection
|
||||
And response HTTP 200 z JSON { success: true, cart: {...} }
|
||||
```
|
||||
|
||||
## AC-2: Walidacja "musi wybrać piece" — fancybox blokada nadal działa
|
||||
```gherkin
|
||||
Given strona produktu w nowym layoucie
|
||||
And #checkbox-piece NIE jest zaznaczony (user nie otworzył popup'a piece)
|
||||
When użytkownik klika "Dodaj do koszyka"
|
||||
Then pojawia się fancybox z treścią "Proszę wybrać rozmiar i wycinek tapety..."
|
||||
And POST do koszyka NIE wychodzi (network tab clean)
|
||||
And stan koszyka niezmieniony
|
||||
```
|
||||
|
||||
## AC-3: Cart widget odświeża się po sukcesie
|
||||
```gherkin
|
||||
Given udany POST z AC-1 (response success=true)
|
||||
When backend zwraca response
|
||||
Then nagłówek/cart widget pokazuje zaktualizowaną liczbę produktów (+1)
|
||||
And (jeśli istnieje modal potwierdzający) wyświetla się success modal LUB redirect do koszyka zgodnie z konwencją starego layoutu
|
||||
And event `prestashop.emit('updatedCart', resp)` został wyemitowany (verified via console listener injected w Playwright)
|
||||
```
|
||||
|
||||
## AC-4: Błąd serwera prezentowany użytkownikowi
|
||||
```gherkin
|
||||
Given add-to-cart zwraca error (np. insufficient stock, invalid qty)
|
||||
When response.hasError === true
|
||||
Then użytkownik widzi czytelny komunikat błędu (modal lub inline message)
|
||||
And stan formy zostaje zachowany (piece config nie zresetowany)
|
||||
And przycisk "Dodaj do koszyka" wraca do stanu enabled (nie utknął w loading)
|
||||
```
|
||||
|
||||
## AC-5: Zero regresji w starym layoucie
|
||||
```gherkin
|
||||
Given strona produktu poza IP 89.69.31.86 (REMOTE_ADDR != '89.69.31.86')
|
||||
When użytkownik konfiguruje piece i klika "Dodaj do koszyka"
|
||||
Then flow działa identycznie jak przed Plan 02-02 (baseline)
|
||||
And network payload i sekwencja zdarzeń niezmienione
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<name>Task 1: Live diagnosis — jaki jest aktualny stan add-to-cart w nowym layoucie</name>
|
||||
<what-built>
|
||||
Zanim cokolwiek napiszemy — musimy zobaczyć co SIĘ DZIEJE po kliknięciu przycisku
|
||||
w nowym layoucie. Hipotezy do zweryfikowania:
|
||||
|
||||
H1: PS core handler (`[data-button-action=add-to-cart]`) przechwytuje click, wykonuje POST,
|
||||
wszystko działa i potrzebujemy tylko cosmetic polish + cart widget listener.
|
||||
H2: PS core handler nie działa (bo np. szuka `.product-actions` wrapper którego brak,
|
||||
albo inline-script crash wywala listener przed ready). Trzeba wrapper dodać LUB
|
||||
napisać własny submit handler.
|
||||
H3: Handler działa, POST wychodzi, ale response nie triggeruje cart widget refresh.
|
||||
H4: Handler działa ale fancybox-blocker (custom.js:327-341) nie działa poprawnie
|
||||
w nowym layoucie (np. `#checkbox-piece` nigdy się nie zaznacza po user flow).
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
Via Playwright MCP (live env, IP spoofowany do 89.69.31.86 — lub na czyjej maszynie
|
||||
ma IP dopasowany):
|
||||
|
||||
1. Navigate: https://newwalls.pl/[dowolny-produkt] (przy zalogowanym IP 89.69.31.86)
|
||||
2. Verify new layout loaded: `document.querySelector('.product-variants-data--new') !== null`
|
||||
3. Simulate user flow:
|
||||
a. Kliknąć `.piece-summary` → popup otwiera się
|
||||
b. Ustawić szerokość/wysokość (np. 200x150) w fancybox
|
||||
c. Kliknąć "Zatwierdź" (lub odpowiednik) w popup → sprawdzić `$('#checkbox-piece').is(':checked')` === true
|
||||
d. Inject listener dla diagnostyki:
|
||||
```js
|
||||
window.__addToCartDiag = { clicks: 0, posts: [], events: [] };
|
||||
$(document).on('click', '[data-button-action=add-to-cart]', function() {
|
||||
window.__addToCartDiag.clicks++;
|
||||
});
|
||||
const origFetch = window.fetch;
|
||||
window.fetch = function(...args) {
|
||||
if (String(args[0]).includes('cart')) window.__addToCartDiag.posts.push(args);
|
||||
return origFetch.apply(this, args);
|
||||
};
|
||||
if (window.prestashop) {
|
||||
prestashop.on('updatedCart', r => window.__addToCartDiag.events.push(['updatedCart', r]));
|
||||
}
|
||||
```
|
||||
e. Kliknąć "Dodaj do koszyka"
|
||||
f. Odczekać 2s, odczytać `window.__addToCartDiag` + network tab
|
||||
|
||||
4. Dokumentacja wyników:
|
||||
- Czy POST dotarł do `/cart`?
|
||||
- Jaki status HTTP + schema response?
|
||||
- Czy `updatedCart` event emitted?
|
||||
- Czy cart header counter się zaktualizował?
|
||||
- Jeśli nic się nie stało — czy jest error w console? Stack trace?
|
||||
|
||||
5. Resume-signal: Napisz do plan'u które hipotezy (H1–H4) są prawdziwe i który scenariusz implementacji wybrać dla Task 2:
|
||||
- S1: Tylko dodać listener cart widget (H1 prawda) — minimalna zmiana.
|
||||
- S2: Wrapper `.product-actions` wokół bloku z buttonem (H2 częściowo) — small template change.
|
||||
- S3: Własny AJAX submit handler w custom.js (H2 prawda / PS core nie-do-przywrócenia) — więcej kodu.
|
||||
- S4: Fix fancybox-blocker flow (H4 prawda) — tweak custom.js:327 logic.
|
||||
</how-to-verify>
|
||||
<resume-signal>Wybierz: S1 / S2 / S3 / S4 (można łączyć, np. "S2+S1") — albo opisz własny scenariusz jeśli diagnoza ujawni coś spoza hipotez.</resume-signal>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Implementacja submit flow według wybranego scenariusza (S1/S2/S3)</name>
|
||||
<files>themes/ayon/templates/catalog/product.tpl, themes/ayon/assets/js/custom.js</files>
|
||||
<action>
|
||||
Na podstawie decyzji z Task 1:
|
||||
|
||||
**Jeśli S1 (PS core działa, tylko brakuje cart refresh):**
|
||||
- W custom.js dodać (lub rozszerzyć istniejący `prestashop.on` block) listener:
|
||||
```js
|
||||
if (window.prestashop && typeof prestashop.on === 'function') {
|
||||
prestashop.on('updatedCart', function(params) {
|
||||
// Refresh header cart widget if not auto-refreshed by core
|
||||
if ($('.product-variants-data--new').length === 0) return; // scope: new layout only
|
||||
// Trigger header cart update — PS core usually handles this, ale defensive:
|
||||
if (window.prestashop.modules && window.prestashop.modules.blockcart) {
|
||||
$(document).trigger('blockcart:update', params);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
- Scope pod marker class żeby stary layout nietknięty.
|
||||
|
||||
**Jeśli S2 (wrapper `.product-actions` brakuje):**
|
||||
- W product.tpl, w gałęzi `{if ... == '89.69.31.86'}`, znaleźć blok
|
||||
`<div class="product-add-to-cart">` (ok. linia 719 w nowym layoucie)
|
||||
i owinąć go w `<div class="product-actions">`:
|
||||
```smarty
|
||||
<div class="product-actions">
|
||||
<div class="product-add-to-cart">
|
||||
...istniejący content...
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
- UWAGA: sprawdzić czy klasa `.product-actions` nie wywołuje niechcianych stylów
|
||||
ze starego SCSS (grep w custom.css). Jeśli tak — zmienić selector CSS na
|
||||
scope'owany (`body#product .product-variants-data--new .product-actions`)
|
||||
lub dodać marker class `.product-actions--new` i użyć jej w JS.
|
||||
|
||||
**Jeśli S3 (własny submit handler):**
|
||||
- W custom.js, w obrębie istniejącego `setTimeout(..., 600)` init bloku
|
||||
(dla nowego layoutu, gated przez `.product-variants-data--new` check),
|
||||
dodać handler:
|
||||
```js
|
||||
$('#add-to-cart-or-refresh').on('submit', function(e) {
|
||||
if ($('.product-variants-data--new').length === 0) return; // old layout: PS core handles
|
||||
e.preventDefault();
|
||||
if (!$('#checkbox-piece').is(':checked')) {
|
||||
// fallback do istniejącej fancybox blokady — delegacja
|
||||
$('#add-to-cart-or-refresh button').trigger('click');
|
||||
return;
|
||||
}
|
||||
var $btn = $(this).find('[data-button-action=add-to-cart]');
|
||||
$btn.prop('disabled', true).addClass('loading');
|
||||
$.ajax({
|
||||
url: this.action,
|
||||
method: 'POST',
|
||||
data: $(this).serialize() + '&add=1&action=update',
|
||||
dataType: 'json',
|
||||
headers: { 'Accept': 'application/json' },
|
||||
success: function(resp) {
|
||||
if (resp.hasError || resp.success === false) {
|
||||
// show error (use existing fancybox or inline)
|
||||
$.fancybox({ content: (resp.errors || ['Błąd dodawania do koszyka']).join('<br>') });
|
||||
return;
|
||||
}
|
||||
if (window.prestashop && typeof prestashop.emit === 'function') {
|
||||
prestashop.emit('updatedCart', { resp: resp, reason: { linkAction: 'add-to-cart' } });
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$.fancybox({ content: 'Błąd połączenia. Spróbuj ponownie.' });
|
||||
},
|
||||
complete: function() {
|
||||
$btn.prop('disabled', false).removeClass('loading');
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
- NIE dotykać istniejącego `$('#add-to-cart-or-refresh button').on('click')` z custom.js:327
|
||||
(blokada "wybierz rozmiar przed add-to-cart") — nadal potrzebna dla AC-2.
|
||||
- Uwaga: PS core używa `add=1&action=update` dla add-to-cart — sprawdzić empirycznie
|
||||
(w Task 1 powinno być w network tab jeśli PS core działa gdziekolwiek).
|
||||
|
||||
Avoid:
|
||||
- Modyfikowanie `product-add-to-cart.tpl` partial (współdzielony ze starym layoutem, risk regresji)
|
||||
- Dodawanie globalnych handlerów `$(document).on(...)` bez scope'owania na new layout
|
||||
- Nadpisywanie istniejącego click handler z custom.js:327 (blokada fancybox)
|
||||
</action>
|
||||
<verify>
|
||||
Playwright live test:
|
||||
1. Load produkt w nowym layoucie (IP match)
|
||||
2. Piece config flow (width=200, height=150, position drag)
|
||||
3. Click "Dodaj do koszyka"
|
||||
4. Odczytać `window.__addToCartDiag` + network tab:
|
||||
- `posts.length >= 1` z body zawierającym `is_crop=1&crop_width=200&crop_height=150&...`
|
||||
- Response HTTP 200, `success: true`
|
||||
- Event `updatedCart` emitted
|
||||
- Header cart counter += 1
|
||||
</verify>
|
||||
<done>AC-1, AC-2, AC-3 satisfied (AC-2 nietknięta przez Task 2 — istniejąca blokada nadal działa).</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Error handling, loading state UX, cross-layout regression check</name>
|
||||
<files>themes/ayon/assets/js/custom.js, themes/ayon/assets/css/custom.scss</files>
|
||||
<action>
|
||||
**Error UX (jeśli S3 w Task 2 — jeśli S1/S2 sprawdzić czy PS core natywnie pokazuje błędy):**
|
||||
- W custom.js, w submit handler z Task 2, branch `hasError`:
|
||||
- Wyciągnąć czytelny text z `resp.errors` (array) → `join('<br>')`
|
||||
- Fallback: "Nie udało się dodać do koszyka. Spróbuj ponownie."
|
||||
- Pokazać w `$.fancybox({ content: ... })` zgodnie z istniejącym wzorcem (custom.js:330)
|
||||
|
||||
**Loading state (wszystkie scenariusze):**
|
||||
- W custom.scss, scope'owana reguła (~linia koniec pliku):
|
||||
```scss
|
||||
body#product .product-variants-data--new {
|
||||
#add-to-cart-or-refresh button.add-to-cart {
|
||||
&.loading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%; left: 50%;
|
||||
width: 20px; height: 20px;
|
||||
margin: -10px 0 0 -10px;
|
||||
border: 2px solid rgba(255,255,255,.3);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin .6s linear infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
```
|
||||
(Skip jeśli `@keyframes spin` już istnieje — grep sprawdzić.)
|
||||
|
||||
**Regression test — stary layout:**
|
||||
- Playwright na URL z REMOTE_ADDR != 89.69.31.86 (np. przez VPN lub override header Host).
|
||||
- Zweryfikować że flow add-to-cart w starym layoucie działa identycznie jak przed Plan 02-02:
|
||||
- Klik → POST → cart update → success
|
||||
- Network payload (pomijając cookies) identyczny z baseline'em (zarejestrować baseline przed Task 2 jako kontrolę)
|
||||
- Jeśli regresja wykryta → zdiagnozować jaki selektor / listener konflict'uje → scope'ować ciaśniej.
|
||||
|
||||
Avoid:
|
||||
- Dodawanie globalnych styles (wszystkie pod `.product-variants-data--new` scope)
|
||||
- Zmiana istniejącego submit flow starego layoutu (nawet inline — stary działa, nie dotykamy)
|
||||
</action>
|
||||
<verify>
|
||||
1. Playwright nowy layout: force error (np. Console `fetch('/cart', {...})` z złym tokenem) → fancybox z błędem pokazuje się, button wraca do enabled.
|
||||
2. Playwright nowy layout: slow-3G throttling → spinner widoczny podczas pending request.
|
||||
3. Playwright stary layout (IP != 89.69.31.86): add-to-cart flow identyczny z baseline; headless log network + compare.
|
||||
</verify>
|
||||
<done>AC-4, AC-5 satisfied.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- `themes/ayon/templates/catalog/_partials/product-add-to-cart.tpl` (współdzielony ze starym layoutem — modyfikacja = ryzyko regresji w produkcji)
|
||||
- `themes/ayon/templates/catalog/_partials/product-variants.tpl` (shared, Plan 01 closed)
|
||||
- `themes/ayon/templates/catalog/_partials/product-cover-thumbnails.tpl` (shared, używany przez piece z Plan 02-01)
|
||||
- Starą gałąź `{if ... != '89.69.31.86'}` w `product.tpl` — całość strukturalnie nietknięta
|
||||
- Istniejący handler `$('#add-to-cart-or-refresh button').on('click', ...)` z custom.js:327 (blokada fancybox — nadal pełni AC-2)
|
||||
- Hidden inputy crop/mirror z Plan 02-01 (nazwy/ID są kontraktem)
|
||||
- `custom.css` — edytujemy wyłącznie `custom.scss` (user ma watcher)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Ten plan NIE rozwiązuje kalkulacji ceny per-sqm w nowym layoucie (`totalpriceinfospecific` no-op override z Plan 02-01) — to osobny plan (Plan 02-03 kandydat).
|
||||
- Ten plan NIE wypełnia pustych bloków (`.product-protect`, `.product-installation`, `.product-order-sample`) — osobny plan (Plan 02-04+).
|
||||
- Ten plan NIE dodaje "quick view" / product modalu — zakres strony produktu tylko.
|
||||
- Ten plan NIE zmienia serwer-side logic (add-to-cart controller) — tylko client + template.
|
||||
- Brak nowych zależności npm / composer — wykorzystujemy istniejący stack (jQuery, fancybox, PS core).
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Przed declaration complete (UNIFY gate):
|
||||
- [ ] AC-1 verified Playwright: POST wychodzi z pełnym body, success response.
|
||||
- [ ] AC-2 verified Playwright: blokada fancybox gdy piece nie wybrany.
|
||||
- [ ] AC-3 verified Playwright: cart counter w headerze aktualizuje się.
|
||||
- [ ] AC-4 verified Playwright: error response → czytelny komunikat, button enabled.
|
||||
- [ ] AC-5 verified Playwright: stary layout bez regresji (baseline diff clean).
|
||||
- [ ] `custom.scss` → `custom.css` compile clean (user watcher lub manual check po commit).
|
||||
- [ ] Grep: zero zmian w plikach starego layoutu / shared partial'ach (git diff).
|
||||
- [ ] `$('#add-to-cart-or-refresh').serialize()` w nowym layoucie zawiera wszystkie 8 hidden input'ów + token + id_product + id_customization + qty.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie 5 AC pass w live Playwright test.
|
||||
- Zero regresji w starym layoucie (Playwright + git diff check).
|
||||
- Zero nowych dependencies.
|
||||
- `custom.js` zmiany dodatkowe (nie modyfikujące istniejących handlerów) — diff czysty, scope'owany.
|
||||
- SUMMARY.md wystawiony z: co działa, co odroczone (cena = Plan 02-03), problemy napotkane.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Po zakończeniu: `.paul/phases/02-product-actions-fixes/02-02-SUMMARY.md`
|
||||
</output>
|
||||
143
.paul/phases/02-product-actions-fixes/02-02-SUMMARY.md
Normal file
143
.paul/phases/02-product-actions-fixes/02-02-SUMMARY.md
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
phase: 02-product-actions-fixes
|
||||
plan: 02
|
||||
subsystem: ui+backend-integration
|
||||
tags: [prestashop, smarty, jquery, ajax, add-to-cart, squaremeter, customization, cache]
|
||||
status: PARTIAL (core flow działa, ale end-to-end UX wymaga Plan 02-03 — customization nie zapisuje się i modal nie pojawia)
|
||||
|
||||
requires:
|
||||
- phase: 02-product-actions-fixes
|
||||
provides:
|
||||
- Plan 02-01 form `#add-to-cart-or-refresh` z hidden inputs (crop/mirror) — wykorzystane jako baseline POST payload
|
||||
- Plan 02-01 marker class `.product-variants-data--new` — użyty do scope'owania handler'a
|
||||
|
||||
provides:
|
||||
- Capture-phase click handler na `[data-button-action=add-to-cart]` w `custom.js` (+inline mirror w `product.tpl` jako cache-buster)
|
||||
- Blokuje PS core handler (ktory crash'owal bo button poza forma) przez `stopImmediatePropagation` na capture phase
|
||||
- Manualny POST do form.action z `form.serialize() + qty + add=1&action=update`
|
||||
- Fancybox-blocker port (walidacja ze piece wybrany przed submit)
|
||||
- Sync is_crop/crop_width/crop_height przed POST (obejście crash'u checkedHandler)
|
||||
- `prestashop.emit('updatedCart')` + blockcart refresh fetch
|
||||
- Loading spinner + success flash animation
|
||||
|
||||
affects:
|
||||
- Plan 02-03 (cena + customization + modal) — **BLOKER dla production readiness**:
|
||||
- Customization nie zapisuje się bo `squaremeter::hookActionObjectCartUpdateBefore` wymaga `discretion=on` + squaremeter fields (`dim`, `qty`, `qty_alth`, `product_total_price_calc`, `extrafeevalue`, `wastevalue`, `calculated_total`, etc.)
|
||||
- Plan 02-01 override `totalpriceinfospecific` wyłączył synchronizację tych pól w nowym layoucie
|
||||
- Success modal (po add-to-cart) wymaga osobnego POST do `/module/ps_shoppingcart/ajax?action=add-to-cart` → renderowany przez `Ps_Shoppingcart::renderModal()`
|
||||
- W koszyku brakuje "Szczegóły" button bo cart.id_customization = 0
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Capture-phase native addEventListener z useCapture=true — jedyna metoda blokowania PS core delegated bubble handlers w jQuery.on()"
|
||||
- "Inline script w product.tpl jako cache-buster dla handler'a, guard'owany `window.__<flag>Bound` — immune na browser cache statycznych assetow"
|
||||
- "Stopniowa diagnoza structure-first (DOM traversal → event phases → POST payload) w Playwright przed zmianami kodu"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- .paul/phases/02-product-actions-fixes/02-02-PLAN.md
|
||||
- .paul/phases/02-product-actions-fixes/02-02-SUMMARY.md
|
||||
modified:
|
||||
- themes/ayon/assets/js/custom.js (wrapped Plan 02-02 block w `if (!window.__p02p02Bound) { ... }` guard, ~115 linii)
|
||||
- themes/ayon/assets/css/custom.scss (+loading spinner + added-flash, ~42 linie)
|
||||
- themes/ayon/templates/catalog/product.tpl (+inline handler mirror przed `{/if}` new-layout, ~95 linii) — cache-buster
|
||||
|
||||
key-decisions:
|
||||
- "Task 1 diagnoza via Playwright → S3 (własny AJAX submit) — struktura DOM: button+qty są poza forma, PS core `closest('form')` zwraca 0 elementów, POST nigdy nie wychodzi"
|
||||
- "Capture-phase native addEventListener zamiast jQuery `.on()` — eliminuje double-POST (PS core registered first, jQuery stopPropagation nie mogło cofnąć wcześniejszej rejestracji)"
|
||||
- "Manual sync is_crop/crop_width/crop_height w handlerze — obejście crash'u checkedHandler (totalpriceinfospecific override z 02-01 nie działa w produkcji)"
|
||||
- "Inline script w product.tpl + idempotency guard — cache-buster dla <script src=custom.js> bez modyfikacji gdzieśtam rejestracji"
|
||||
- "UNIFY jako PARTIAL — szczerze raportować że customization + modal wymagają Plan 02-03 z powodu zależności od squaremeter flow (ktory 02-01 wyłączyło no-op override'em)"
|
||||
|
||||
patterns-established:
|
||||
- "Capture-phase handler na window events: `document.addEventListener('click', fn, true)` — dla blokowania globalnych delegated handlerów PS core bez modyfikacji core.js"
|
||||
- "Idempotency guard flag `window.__<planId>Bound` — pozwala duplikować handler (custom.js + inline template) bez double-execution"
|
||||
- "HTML response inline <script> jako cache-buster dla statycznych assetów — immune na browser cache + PS 1.7 Smarty cache invaliduje sie na kolejnej pełnej pre-renderyzacji"
|
||||
|
||||
duration: ~4h (Task 1 diagnoza 30min, Task 2 iteracje 2h, Task 3 CSS 15min, debugging cache+customization discovery 1.5h)
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: POST dociera do /koszyk z pełnym payloadem | **Pass** | Playwright verified: POST z is_crop=1, crop_width/height, qty, add=1, action=update. Response success:true. |
|
||||
| AC-2: Fancybox-blocker bez piece config | **Pass (code review)** | Straight-line if-return logic w handler'ze. Live test session pollution blokowała izolowany test, ale logika prosta i sprawdzona w kodzie. |
|
||||
| AC-3: Cart widget counter się odświeża | **Pass** | Cart-products-count aktualizowany z `"000"` na `"1"` po POST. Mechanizm: `prestashop.emit('updatedCart')` + manual blockcart fetch. |
|
||||
| AC-4: Error response → czytelny komunikat | **Pass (code review)** | Handler parse'uje `resp.errors` (array lub object), pokazuje fancybox. Symmetric do success path. Nie live-tested bo wymagałoby symulacji error response. |
|
||||
| AC-5: Zero regresji starego layoutu | **Pass (by design)** | Early return `if (!document.querySelector('.product-variants-data--new')) return` — stary layout nie wywołuje preventDefault/stop, PS core handler wykonuje się normalnie. |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- **Diagnoza struktury DOM** — ustalenie że forma i button są w rozdzielnych kontenerach Bootstrap (form w sidebar .col-md-6, button w szerokim .product-bar). PS core handler nie może dotrzeć do formy.
|
||||
- **Capture-phase breakthrough** — pierwsza iteracja (jQuery `.on()`) powodowała double-POST. Refactor na native addEventListener z `useCapture=true` w pełni blokuje PS core. Verified `nativeClicks:1, delegated:0`.
|
||||
- **Cache immunity** — identyfikacja browser cache jako root cause "niby zadziałało ale u użytkownika nie". Rozwiązanie: inline script w template + idempotency guard pozwala na parallel deploys (custom.js + inline) bez konfliktu.
|
||||
- **Głęboka diagnoza customization flow** — rozpoznanie że PS 1.7 + squaremeter używa dwóch dróg: cart controller override + `hookActionObjectCartUpdateBefore` gated przez `discretion=on`. Success modal przez `/module/ps_shoppingcart/ajax?action=add-to-cart`. Oba wymagają pól których Plan 02-01 nie dostarcza.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. Double POST — PS core + nasz handler oba wysyłają**
|
||||
- **Found during:** Task 2 first live test — `posts: [2 entries]`, oba identyczne.
|
||||
- **Issue:** jQuery `$(document).on('click', ..., )` registered AFTER PS core → stopImmediatePropagation w naszym handlerze nie blokowało PS core (registered earlier).
|
||||
- **Fix:** Refactor z jQuery `.on()` na native `document.addEventListener('click', fn, true)` (capture phase). Capture fires PRZED bubble, blokuje PS core całkowicie. Verified `nativeClicks:1, delegated:0`.
|
||||
- **Files:** `themes/ayon/assets/js/custom.js`
|
||||
|
||||
**2. Cart widget counter nie odświeżał się po emit('updatedCart')**
|
||||
- **Found during:** Task 2 live test — response success ale `.cart-products-count` pokazywał `"000"`.
|
||||
- **Issue:** Natural PS blockcart listener nie jest zarejestrowany w nowym layoucie / nie reaguje.
|
||||
- **Fix:** Dodany manual `$.get('/koszyk', {action:'refresh', ajax:1})` po successful POST → defensive replace `.blockcart` z response. W real teście counter zaktualizował się (`"000"` → `"1"`) — prawdopodobnie natural listener też odpalił + nasz backup.
|
||||
- **Files:** `themes/ayon/assets/js/custom.js`
|
||||
|
||||
**3. Browser cache serwował starą wersję custom.js**
|
||||
- **Found during:** User raport po FTP deploy — "klikam i nic się nie dzieje". Diagnoza Playwright: `perfEntries[0].decodedBodySize = 30.6 KB` vs server `fetch.size = 41.3 KB`, `transferSize: 0` (from cache).
|
||||
- **Issue:** `<script src="custom.js">` rejestrowany bez query param (version). Browser cache serwuje stale.
|
||||
- **Fix (częściowy):** Dodany inline mirror handler w product.tpl new-layout branch. HTML response zawsze zawiera świeży kod. Idempotency guard `window.__p02p02Bound` zapobiega double-register jeśli cached custom.js też zawiera handler.
|
||||
- **Pozostało:** Systemowy cache-buster dla `custom.js` `<script>` tag — defer do Plan 02-03 razem z innymi cross-cutting concerns.
|
||||
- **Files:** `themes/ayon/templates/catalog/product.tpl`
|
||||
|
||||
### Scope Additions
|
||||
|
||||
- **Inline script cache-buster w product.tpl** — nie planowane w Task 2, ale niezbędne po odkryciu że browser cache blokuje deploy.
|
||||
- **Głęboka diagnoza squaremeter flow** — rozpoznanie że customization zapis wymaga `discretion=on` + dimension fields. Rezultat: identyfikacja zakresu Plan 02-03.
|
||||
|
||||
### Deferred Items → Plan 02-03
|
||||
|
||||
- **Customization save w koszyku** — crop/mirror data (+ dimension gdy zaimplementowane) musi utworzyć customization z in_cart=0 w DB żeby user widział "Szczegóły" button w cart. Wymaga POST z `discretion=on` + squaremeter fields.
|
||||
- **Success modal po add-to-cart** — POST do `/module/ps_shoppingcart/ajax?action=add-to-cart` → render modal z "Kontynuuj zakupy / Przejdź do koszyka".
|
||||
- **Cena per-sqm kalkulacja w nowym layoucie** — wymaga przywrócenia squaremeter dimension flow (zamiast no-op override z Plan 02-01). Powiązane z customization bo `product_total_price_calc` jest jednym z pól.
|
||||
- **Systemowy cache-buster `custom.js`** — zamiast inline duplicate, dodać `?v={mtime}` do rejestracji script tag'a (znaleźć miejsce rejestracji: theme.yml, FrontController override, lub PS hook).
|
||||
- **Plan 02-01 override `totalpriceinfospecific` live-debug** — w produkcji override nie jest aktywny (ORYGINAL function crashes na klik piece-summary). Root cause: setTimeout(600) timing vs inline ready callbacks. Do naprawy w Plan 02-03 razem z przywróceniem squaremeter flow.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
| Issue | Resolution |
|
||||
|-------|------------|
|
||||
| PS core handler registered before us → jQuery stopPropagation nie blokuje PS core | Capture-phase native addEventListener (useCapture=true) fires PRZED bubble phase PS core — całkowicie go blokuje. |
|
||||
| Browser cache servuje starą wersję custom.js mimo że server ma nową (FTP sync zakończony, marker "Phase 02 Plan 02-02" obecny server-side) | Inline script mirror w product.tpl + idempotency guard. HTML response serwuje świeży kod z każdym request. |
|
||||
| Playwright test session state leakage (poprzednie handlery persist'ują mimo navigate) | `page.goto()` wyglada jak full reload ale cached JS execution state może przetrwać przy hash-only URL changes. Rozwiązanie: bardziej aggressive cache busting przez navigate na URL bez fragmentu. |
|
||||
| Customization nie zapisuje się mimo że is_crop/crop data są w POST payload | Squaremeter hook `hookActionObjectCartUpdateBefore` jest gate'owany przez `discretion=on` — brak tego pola w naszym POST. Delegowane do Plan 02-03. |
|
||||
| Success modal nie pojawia się | PS core post-success flow oczekuje osobnego response z `modal` key z endpointa `/module/ps_shoppingcart/ajax`. Nasz POST idzie tylko do `/koszyk`. Delegowane do Plan 02-03. |
|
||||
|
||||
## Skill audit
|
||||
|
||||
Użyte w Plan 02-02:
|
||||
- **Playwright MCP** — krytyczne. Bez live debug (event phase tracing, network capture, DOM inspection, iterowanie handlera bez FTP deploy cycle) nie dałoby się znaleźć capture-phase fix'u ani diagnoza customization flow. Wiele iteracji.
|
||||
- **context-mode** — ctx_batch_execute/ctx_execute_file do eksploracji PS core (core.js), squaremeter source (override controllers + hooks) bez zanieczyszczania context.
|
||||
|
||||
Pattern established: **Structure-first diagnosis** — zanim jakakolwiek implementacja, sprawdzić DOM (form-button relationship), event flow (capture/bubble), POST payload (what fields). Oszczędza cykle debug.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Capture-phase pattern reużywalny dla kolejnych globalnych event overrides (modal close, cart update, etc.)
|
||||
- Inline template cache-buster pattern reużywalny dla wszystkich JS zmian które wymagają "always fresh" deploy
|
||||
- Idempotency guard pattern dla duplikowanych handler'ów
|
||||
|
||||
**Concerns:**
|
||||
- Plan 02-02 feature jest **PARTIAL**: add-to-cart technicznie działa (produkt w koszyku), ale **niezadowalające UX**: brak modal potwierdzenia, brak customization details → user nie widzi co zamówił w szczegółach.
|
||||
- Plan 02-01 override `totalpriceinfospecific` aktywnie uszkadza squaremeter flow → cascade do cena + customization.
|
||||
- Real user flow na produkcji nadal wymaga Plan 02-03 do "acceptable" state.
|
||||
|
||||
**Blockers dla publikacji nowego layoutu:** Plan 02-03 (modal + customization + cena) MUST be done przed umożliwieniem layout-u zwykłym użytkownikom (usunięcie IP gate).
|
||||
340
.paul/phases/02-product-actions-fixes/02-03-PLAN.md
Normal file
340
.paul/phases/02-product-actions-fixes/02-03-PLAN.md
Normal file
@@ -0,0 +1,340 @@
|
||||
---
|
||||
phase: 02-product-actions-fixes
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["02-01", "02-02"]
|
||||
files_modified:
|
||||
- themes/ayon/assets/js/custom.js
|
||||
- themes/ayon/templates/catalog/product.tpl
|
||||
autonomous: false
|
||||
delegation: off
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Dokończyć end-to-end add-to-cart UX w nowym layoucie: (1) customization crop/mirror/dimensions zapisuje się w DB i jest widoczna w koszyku jako "Szczegóły", (2) success modal z opcjami "Kontynuuj zakupy / Przejdź do koszyka" pokazuje się po udanym dodaniu. Parzystość funkcjonalna ze starym layoutem dla tych dwóch punktów.
|
||||
|
||||
## Purpose
|
||||
Plan 02-02 uruchomił POST, ale bez tych dwóch punktów nowy layout jest nieakceptowalny dla klienta końcowego: nie widzi potwierdzenia akcji (modal) ani co zamówił (customization details w cart). Jest to **blocker dla usunięcia IP gate** (`REMOTE_ADDR == '89.69.31.86'`) i publikacji layout'u szerszemu audytorium.
|
||||
|
||||
## Output
|
||||
- Wszystkie wymagane squaremeter hidden inputs w POST payload (`discretion=on`, `dim`, `qty`, `qty_alth`, `product_total_price_calc`, `id_product_attribute`, +opcjonalne) obliczone z piece state + product data.
|
||||
- Po udanym cart update → drugi POST do `/module/ps_shoppingcart/ajax?action=add-to-cart` → render modal z response.modal HTML w fancybox (lub native PS modal container).
|
||||
- Koszyk po reload pokazuje "Szczegóły" button przy produkcie, klik rozwija customization data (wymiary, is_crop, is_reflection).
|
||||
- Plan 02-01 override `totalpriceinfospecific` POZOSTAJE (nie ruszamy — bypass, nie restore). Cena kalkulacja może być nieprecyzyjna w tym planie (defer do Plan 02-04).
|
||||
- Summary: `.paul/phases/02-product-actions-fixes/02-03-SUMMARY.md`.
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Prior Work
|
||||
@.paul/phases/02-product-actions-fixes/02-01-SUMMARY.md
|
||||
@.paul/phases/02-product-actions-fixes/02-02-SUMMARY.md
|
||||
|
||||
## Source Files
|
||||
@themes/ayon/templates/catalog/product.tpl
|
||||
@themes/ayon/assets/js/custom.js
|
||||
@modules/squaremeter/squaremeter.php
|
||||
# Line 843+: hookActionObjectCartUpdateBefore — gate'owany `discretion=on`, czyta:
|
||||
# dim, qty, qty_alth, qty_alt, qty_altd, extrafeevalue, wastevalue,
|
||||
# product_total_price_calc, calculated_total, grand_calculated_total,
|
||||
# converted_ea, directinput
|
||||
@modules/squaremeter/override/classes/Cart.php
|
||||
# _addCustomization2(): INSERT INTO customization + customized_data
|
||||
@modules/squaremeter/override/modules/ps_shoppingcart/controllers/front/ajax.php
|
||||
# Endpoint dla `action=add-to-cart` → renderModal() zwraca modal HTML
|
||||
@modules/squaremeter/views/templates/front/header_surface.tpl
|
||||
# Stary flow squaremeter — inspiracja jakie fields + jak computed
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
## Required Skills
|
||||
|
||||
| Skill | Priority | When to Invoke | Loaded? |
|
||||
|-------|----------|----------------|---------|
|
||||
| Playwright MCP (`mcp__plugin_playwright_playwright__*`) | required | Task 1 (capture OLD layout POST), Task 3 regression | ○ |
|
||||
| context-mode (`mcp__plugin_context-mode_context-mode__*`) | optional | Eksploracja squaremeter hooks + PS endpoints | ○ |
|
||||
|
||||
**BLOCKING:** Playwright MCP wymagany w Task 1 — musimy zobaczyć EXACT payload starego layoutu (fields + wartości) zanim zainżynierujemy mapowanie w nowym.
|
||||
|
||||
## Skill Invocation Checklist
|
||||
- [ ] Playwright MCP dostępny
|
||||
- [ ] Dostęp do testowego środowiska gdzie można przełączyć layouty (IP switch lub URL param)
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Customization zapisuje się podczas add-to-cart
|
||||
```gherkin
|
||||
Given user skonfigurował piece (width=200, height=150) w nowym layoucie
|
||||
And formularz ma is_crop=1, crop_width=200, crop_height=150, piece_bg_top/left
|
||||
When user klika "Dodaj do koszyka"
|
||||
Then POST payload zawiera `discretion=on&dim=200x150&qty=200&qty_alth=150&product_total_price_calc=<value>&id_product_attribute=<value>`
|
||||
And response HTTP 200 z `id_customization > 0`
|
||||
And w DB tabela `customization` ma nowy wiersz z `id_cart=<active>&id_product=202&in_cart=1`
|
||||
And w DB tabela `customized_data` ma wiersz z `index=<WD_CUSTOMIZATION_INDEX>&value='200x150'`
|
||||
```
|
||||
|
||||
## AC-2: Success modal pokazuje się po dodaniu
|
||||
```gherkin
|
||||
Given udany cart update z AC-1
|
||||
When backend potwierdzi success
|
||||
Then wychodzi drugi POST do `/module/ps_shoppingcart/ajax?action=add-to-cart` z `id_product`, `id_product_attribute`, `id_customization`
|
||||
And response zawiera `modal: '<HTML>...'`
|
||||
And modal renderuje się (w fancybox lub native container) z:
|
||||
- Nazwą produktu
|
||||
- Podziękowaniem / komunikatem success
|
||||
- Dwoma przyciskami: "Kontynuuj zakupy" (zamyka modal) i "Przejdź do koszyka" (nawiguje do /pl/koszyk)
|
||||
```
|
||||
|
||||
## AC-3: Koszyk pokazuje "Szczegóły" button + customization data
|
||||
```gherkin
|
||||
Given product dodany przez nowy layout z customization
|
||||
When user nawiguje do /pl/koszyk
|
||||
Then cart item ma button/link "Szczegóły" (lub zwinięty blok z danymi)
|
||||
And kliknięcie rozwija customization:
|
||||
- Wymiary: "200 x 150 cm"
|
||||
- Czy fragment: tak/nie (is_crop)
|
||||
- Czy odbicie lustrzane: tak/nie (is_reflection)
|
||||
```
|
||||
|
||||
## AC-4: Brak double-add (handler idempotency po success)
|
||||
```gherkin
|
||||
Given user klika "Dodaj do koszyka" jedno klikniecie
|
||||
When handler przetwarza
|
||||
Then wychodzi dokładnie 1 POST do /koszyk + 1 POST do /module/ps_shoppingcart/ajax
|
||||
And w koszyku jest 1 produkt (nie 2)
|
||||
```
|
||||
|
||||
## AC-5: Zero regresji starego layoutu
|
||||
```gherkin
|
||||
Given strona produktu poza IP 89.69.31.86
|
||||
When user konfiguruje piece + dimensions + klika "Dodaj do koszyka"
|
||||
Then flow identyczny jak przed Plan 02-03 (baseline network capture)
|
||||
And customization save nadal działa (existing feature)
|
||||
And modal nadal działa (existing feature)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<name>Task 1: Capture OLD layout POST payload (ground truth)</name>
|
||||
<what-built>
|
||||
Nothing coded yet — live data capture. Musimy zobaczyć DOKŁADNIE jakie fields stary layout
|
||||
wysyła przy add-to-cart, z jakimi wartościami, w jakiej kolejności. Bez tego ryzyko
|
||||
przegapienia wymaganego pola (np. `directinput`, `qty_alt`, `calculated_total`).
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
Via Playwright MCP — OLD layout (IP != 89.69.31.86, np. przez URL param `?_test_old=1`
|
||||
jeśli jest albo tymczasowo komentować IP check w product.tpl dla tej sesji):
|
||||
|
||||
1. Navigate: dowolny produkt na starym layoucie
|
||||
2. Install diagnostic:
|
||||
```js
|
||||
window.__oldPayload = null;
|
||||
const _ajax = jQuery.ajax;
|
||||
jQuery.ajax = function(cfg) {
|
||||
const url = (typeof cfg === 'string') ? cfg : cfg && cfg.url;
|
||||
if (String(url).match(/cart|koszyk|ps_shoppingcart/) && cfg.type === 'POST') {
|
||||
window.__oldPayload = { url, data: cfg.data, headers: cfg.headers };
|
||||
}
|
||||
return _ajax.apply(jQuery, arguments);
|
||||
};
|
||||
```
|
||||
3. Sformułuj pełny user flow: wybierz wariant kolorystyczny + materiał + wprowadź wymiary (np. 200×150) + ustaw piece position + kliknij "Dodaj do koszyka"
|
||||
4. Odczytaj `window.__oldPayload` + sprawdź Network tab dla wszystkich XHR/fetch do `cart` i `ps_shoppingcart`
|
||||
5. Dokumentacja wszystkich parametrów:
|
||||
- Pierwszy POST (cart update): wszystkie name=value pairs
|
||||
- Drugi POST (modal fetch if exists): endpoint + payload
|
||||
- Suffix pattern (`_<id_product>` lub brak)
|
||||
- Format `dim` (np. "200x150" vs "200X150" vs "200 x 150")
|
||||
- Czy `product_total_price_calc` jest integer czy decimal
|
||||
6. Zapisać w komentarzu checkpoint'a listę fields z przykładowymi wartościami
|
||||
|
||||
Resume-signal: wklej dokładne pole-wartość z OLD POST + zatwierdź że to jest wzór
|
||||
który naśladujemy w Task 2.
|
||||
</how-to-verify>
|
||||
<resume-signal>Podaj fields z OLD POST payload (przykład: `discretion=on&dim=200x150&qty=200&qty_alth=150&product_total_price_calc=597.00&...`) LUB flag "use sensible defaults" jeśli nie masz dostępu do OLD layoutu na prod.</resume-signal>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Inject squaremeter fields do POST payload w nowym layoucie</name>
|
||||
<files>themes/ayon/assets/js/custom.js, themes/ayon/templates/catalog/product.tpl</files>
|
||||
<action>
|
||||
W OBU miejscach (handler w custom.js linie 993-1113 + inline mirror w product.tpl
|
||||
linie 757+) zmodyfikuj payload construction:
|
||||
|
||||
Przed `$.ajax({ ... data: payload })`:
|
||||
```js
|
||||
// Phase 02 Plan 02-03: inject squaremeter fields for customization save
|
||||
var pieceW = parseInt($('#piece-width').val(), 10) || 100;
|
||||
var pieceH = parseInt($('#piece-height').val(), 10) || 100;
|
||||
var idProductAttribute = parseInt($('input[name=id_product_attribute], #idCombination').val(), 10)
|
||||
|| window.product_page_product_combination
|
||||
|| 0;
|
||||
|
||||
// Price calc: use product base price × area (m²) as fallback.
|
||||
// Exact price jest Plan 02-04 scope — tu chodzi tylko o niezero value żeby hook przeszedł.
|
||||
var areaM2 = (pieceW / 100) * (pieceH / 100);
|
||||
var basePrice = parseFloat($('#product_base_price, #product_fixed_price').val())
|
||||
|| parseFloat($('.product-prices .current-price').first().text().replace(/[^\d.,]/g, '').replace(',', '.'))
|
||||
|| 0;
|
||||
var totalPriceCalc = (basePrice * areaM2).toFixed(2);
|
||||
|
||||
var sqFields = 'discretion=on'
|
||||
+ '&dim=' + pieceW + 'x' + pieceH
|
||||
+ '&qty=' + pieceW // squaremeter qty = width (NIE cart quantity!)
|
||||
+ '&qty_alth=' + pieceH
|
||||
+ '&product_total_price_calc=' + totalPriceCalc
|
||||
+ '&id_product_attribute=' + idProductAttribute
|
||||
+ '&calculated_total=' + areaM2.toFixed(4)
|
||||
+ '&grand_calculated_total=' + areaM2.toFixed(4);
|
||||
|
||||
payload = sqFields + '&' + payload; // prepend, bo niektóre pola mogą się powtórzyć — ostatni wygrywa w PHP
|
||||
```
|
||||
|
||||
**Adjust payload** żeby NASZ qty (cart quantity) nie był nadpisywany. PS uses `qty` jako
|
||||
product quantity; squaremeter czyta `qty` jako dimension width. Konflikt!
|
||||
|
||||
Rozwiązanie: nie wysyłać cart `qty=1` bezpośrednio; zamiast tego dodać je jako
|
||||
`quantity_wanted=1` lub upewnić się że nasz `qty` (dimension) jest the one in POST.
|
||||
**Task 1 da odpowiedź co stary layout robi tutaj — może używa suffix** (`qty_<id_product>`)
|
||||
dla dimension a `qty` dla cart quantity.
|
||||
|
||||
Avoid:
|
||||
- Zmiana nazwy cart quantity field (PS core by tego nie zaakceptował)
|
||||
- Hardcode basePrice (read dynamically)
|
||||
- Zakładanie że `id_product_attribute` jest w formie (może być w innej formie lub
|
||||
jako Smarty var — Task 1 pokaże)
|
||||
|
||||
Po Task 1 adjust field list do tego co stary POST zawiera — możliwe że są DODATKOWE
|
||||
pola których tu nie wymieniliśmy (np. `directinput`, `converted_ea`).
|
||||
</action>
|
||||
<verify>
|
||||
Playwright live test nowego layoutu:
|
||||
1. Navigate + piece config
|
||||
2. Click add-to-cart
|
||||
3. Network tab: sprawdź POST do /koszyk — payload zawiera `discretion=on&dim=200x150&qty=200&qty_alth=150&product_total_price_calc=<non-zero>&id_product_attribute=<non-zero>`
|
||||
4. Response: `success:true, id_customization: <non-zero>`
|
||||
5. DB check (via PS admin SQL): `SELECT * FROM ps_customization WHERE id_cart = <cart_id>` → 1 wiersz, `in_cart=1` po finalizacji
|
||||
</verify>
|
||||
<done>AC-1 satisfied.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Chain POST do ps_shoppingcart/ajax + render modal</name>
|
||||
<files>themes/ayon/assets/js/custom.js, themes/ayon/templates/catalog/product.tpl</files>
|
||||
<action>
|
||||
W success callback po udanym cart POST (w OBU miejscach: custom.js + inline product.tpl),
|
||||
po `emit('updatedCart')`, przed lub zamiast blockcart refresh:
|
||||
|
||||
```js
|
||||
// Phase 02 Plan 02-03: fetch success modal
|
||||
if (resp && resp.success && resp.id_product) {
|
||||
$.ajax({
|
||||
url: '/module/ps_shoppingcart/ajax',
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'add-to-cart',
|
||||
id_product: resp.id_product,
|
||||
id_product_attribute: resp.id_product_attribute,
|
||||
id_customization: resp.id_customization
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(modalResp) {
|
||||
if (modalResp && modalResp.modal) {
|
||||
$.fancybox({
|
||||
content: modalResp.modal,
|
||||
minWidth: 400,
|
||||
maxWidth: 800,
|
||||
padding: 20,
|
||||
autoSize: true
|
||||
});
|
||||
}
|
||||
if (modalResp && modalResp.preview) {
|
||||
$('.blockcart').replaceWith(modalResp.preview);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Uwaga:** endpoint path `/module/ps_shoppingcart/ajax` może być w PL jako
|
||||
`/module/ps_shoppingcart/ajax` (path nie tłumaczony) ale warto sprawdzić w Task 1
|
||||
(zobaczyć w Network czy URL jest exactly ten).
|
||||
|
||||
Jeśli modal HTML używa custom CSS (PS blockcart modal), może być problem z
|
||||
stylowaniem w fancybox. Alternatywa: append `modal` HTML do `<body>` + show jako
|
||||
native PS modal (Bootstrap `.modal.show`). Task 1 pokaże jak stary layout renderuje.
|
||||
|
||||
Avoid:
|
||||
- Wywołanie obu: modal fetch + blockcart refresh (redundantnie) — modal resp już ma `preview`
|
||||
- Zakładanie `resp.id_product` — PS może używać `idProduct` (camelCase) w JSON response
|
||||
|
||||
Po modal render, zbędny staje się nasz custom `.added-flash` animation. Zostaw
|
||||
jako fallback gdy modal się nie zarenderuje.
|
||||
</action>
|
||||
<verify>
|
||||
Playwright live test:
|
||||
1. Piece config + click add-to-cart
|
||||
2. Network: 2 POSTy — pierwszy do /koszyk, drugi do /module/ps_shoppingcart/ajax
|
||||
3. DOM: modal z response.modal HTML pokazuje się jako fancybox lub native modal
|
||||
4. Modal zawiera:
|
||||
- Nazwę produktu
|
||||
- Link "Kontynuuj zakupy" (zamyka modal)
|
||||
- Link "Przejdź do koszyka" (wskazuje na /pl/koszyk)
|
||||
5. Navigate to /pl/koszyk → cart pokazuje "Szczegóły" button → klik rozwija customization data
|
||||
</verify>
|
||||
<done>AC-2, AC-3, AC-4 satisfied.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- Plan 02-01 override `totalpriceinfospecific` / `prod` no-op — nadal potrzebne żeby piece popup nie crash'ował. NIE restaurować squaremeter flow tutaj (zbyt ryzykowne + duży scope).
|
||||
- `modules/squaremeter/*` — nie modyfikować modułu, żaden override ani edit.
|
||||
- `themes/ayon/templates/catalog/_partials/*` — shared partials, nie ruszać.
|
||||
- Stara gałąź `{if ... != '89.69.31.86'}` w `product.tpl` — nietknięta.
|
||||
- Istniejący `$('#add-to-cart-or-refresh button').on('click')` w custom.js:327 — zostaje (niedziała w nowym, ale aktywne w starym).
|
||||
- Istniejący capture-phase handler z Plan 02-02 — rozbudowa w miejscu, nie rewrite.
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Ten plan NIE dodaje UI dla live price calculation — cena w modalu/cart może być pokazana jako calculated value (read-only) ale bez UI do zmiany dimensions po dodaniu do cart. **Plan 02-04 scope.**
|
||||
- Ten plan NIE dodaje dimension UI w product page (dimension input niezależny od piece popup) — user podaje wymiar przez piece popup (obecny mechanizm).
|
||||
- Ten plan NIE restauruje squaremeter JS flow (header_surface.tpl etc.) — używamy BYPASS podejścia (bezpośrednio injection squaremeter fields w POST).
|
||||
- Ten plan NIE rozwiązuje systemowego cache-buster dla `custom.js` — inline mirror z Plan 02-02 pozostaje. **Plan 02-05 scope.**
|
||||
- Brak nowych dependencies.
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Przed declaration complete (UNIFY gate):
|
||||
- [ ] AC-1 verified Playwright: POST zawiera wszystkie squaremeter fields, response `id_customization > 0`.
|
||||
- [ ] AC-2 verified Playwright: modal pokazuje się po add-to-cart, ma nazwy produktu + linki.
|
||||
- [ ] AC-3 verified Playwright: w /pl/koszyk widać "Szczegóły" button, klik rozwija dane.
|
||||
- [ ] AC-4 verified Playwright: single add per click, cart qty = 1 (nie 2).
|
||||
- [ ] AC-5 verified Playwright: stary layout bez regresji (baseline network capture identyczny).
|
||||
- [ ] Git diff clean: zero zmian w `modules/squaremeter/`, shared partials, starym layoucie.
|
||||
- [ ] Kod identyczny w OBU miejscach (custom.js + product.tpl inline mirror) — diff między nimi = zero funkcjonalnych różnic.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie 5 AC pass w live Playwright test.
|
||||
- Zero regresji w starym layoucie.
|
||||
- Zero modyfikacji w module squaremeter.
|
||||
- Plan 02-01 override nadal aktywny (nie przywracamy squaremeter JS).
|
||||
- SUMMARY.md dokumentuje co działa, co deferred (Plan 02-04 cena UI, Plan 02-05 cache-buster).
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Po zakończeniu: `.paul/phases/02-product-actions-fixes/02-03-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user