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>
20 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, duration, started, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | duration | started | completed | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02-product-actions-fixes | 01 | ui |
|
|
|
|
|
|
|
|
~3.5h (z live debug'iem via Playwright) | 2026-04-23T19:30:00Z | 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: 1alepieceStyles.backgroundImage: url(".../birds-in-the-fog.jpg")— element pochodzi ze shared partialproduct-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 classproduct_image--newjako 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: blockale niekoniecznie match'ował DOM tree (layout-dependent). - Fix: Usunięty
:has()selector. Reużywamy istniejące stylowanie.product-images .pieceze starego SCSS (linia 623) — applikuje się bo#piecejest w.product-images(z shared partial). - Files:
themes/ayon/assets/css/custom.scss - Verification: Live test —
#piecewidoczny 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ą
totalpriceinfospecificktóra robidocument.getElementById('totalpriceinfo').style.display = 'block'. W nowym layoucie#totalpriceinfonie istnieje. Funkcja wywoływana przez#piece-width change handler(custom.js:281). Error propaguje przeztrigger('change')w checkedHandler (custom.js:183) — abortuje przedis_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,checkedHandlerdochodzi dois_crop=1bez error.
4. Inline-script crash (wariant #2): JSON.parse(undefined) w totalpriceinfospecific
- Found during: Task 4 live verify — po stub'owaniu
#totalpriceinfoerror zmienił się na"undefined" is not valid JSONprzy$('#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:
checkedHandlerruns 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-214ma$(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);przedcheckedHandler. Usunięty po user feedback (piece ma się nie pokazywać na load). - Fix (iteracja 2, finalny): Usunięty
checkedHandlercall 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--newcheck (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 →
#piecere-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--newdo reużycia dla pustych bloków. - Playwright MCP workflow ustalony — potwierdzone że live debug jest niezbędny.
Concerns:
totalpriceinfospecificno-op override wylacza cene calculation w nowym layoucie. Plan 02-02 musi zapewnić alternatywne mechanizm (prawdopodobnie przez PrestaShopprestashop.on('updatedProduct', ...)listener ktory czytaresp.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