Plan 02-03: Customization save + success modal (5/5 AC)
- 26-field squaremeter POST payload (verbose PL dim, qty_alt/qty_alth)
- Chain POST /module/ps_shoppingcart/ajax -> Bootstrap #blockcart-modal
- Critical fix: moved {/block} so inline script actually renders
- __p02p02InFlight re-entrancy guard
Plan 02-04: Live cena per-sqm label obok "Dodaj do koszyka" (5/5 AC)
- .p02p04-total-price label, gorna .current-price static
- Separate __p02p04Bound + setInterval reconciliation
- Poll-retry prestashop.on registration
Plan 02-05: Struktura materialu w POST payload (4/4 AC)
- Enumerate [name^="group["] spoza formy, doklej do payload
- Fix: group_5 select w .product-bar-box nie trafial do koszyka
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 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 | 04 | ui |
|
|
|
|
|
|
|
|
~45min (incl. diagnosis + scope pivot) | 2026-04-24T22:10:00Z | 2026-04-24T22:30:00Z |
Phase 02 Plan 02-04: Live cena per-sqm — summary
Labelka .p02p04-total-price obok przycisku "Dodaj do koszyka" w NEW layoucie aktualizuje się w czasie rzeczywistym na zmiane piece-width/piece-height i variant AJAX refresh; górna cena "Od XXX zł / m²" pozostaje statyczna.
Performance
| Metric | Value |
|---|---|
| Duration | ~45 min |
| Started | 2026-04-24T22:10Z |
| Completed | 2026-04-24T22:30Z |
| Tasks | 3/3 completed |
| Files modified | 2 |
Acceptance Criteria Results
| Criterion | Status | Notes |
|---|---|---|
| AC-1: Initial price render on load | PASS (scope-pivoted) | Label "59,75 zł" (50×50×239); upper .current-price pozostaje "Od 239,00 zł / m2" statycznie. Wymagany interval retry 10×500ms (DOM stubs injected late + squaremeter overwrite). |
| AC-2: Price updates on width change | PASS | Native input event na #piece-width → label updates ≤350ms, zero HTTP. Weryfikacja: 150×50 → 179,25 zł. |
| AC-3: Price updates on height change | PASS | #piece-height → label updates. Weryfikacja: 150×100 → 358,50 zł. |
| AC-4: Price re-renders after variant AJAX refresh | PASS | prestashop.emit('updatedProduct') → handler zarejestrowany przez poll-retry → recalc 50ms delay → label update. Weryfikacja: meta=500 → "125,00 zł" (vs 239 → "59,75 zł"). |
| AC-5: Zero regression OLD layout | PASS (code-path) | Guard .product-variants-data--new w recalc = early return w OLD. Inline mirror tylko w {if REMOTE_ADDR=='89.69.31.86'} bloku — nie renderuje dla OLD. Zero DOM mutacji w OLD. Bez live IP-flip testu (code-path analysis wystarczający dla MVP). |
Accomplishments
- Live total-price labelka obok przycisku "Dodaj do koszyka" — natychmiastowa widoczność kwoty przy konfiguracji rozmiaru, klient widzi konkretny koszt przed kliknięciem buttona.
- Upper price "Od 239,00 zł / m²" pozostaje statyczna — zachowana rola info-labela (per user pivot mid-task).
- Reaktywność na 3 kanały: (a) user typing → native input events, (b) piece popup dim changes → delegated handlers, (c) variant AJAX refresh → prestashop.on handler z deferred rejestracja.
- Zero HTTP per recalc — pure JS × basePrice × area_m² (formula z Plan 02-03, single source of truth).
Files Created/Modified
| File | Change | Purpose |
|---|---|---|
themes/ayon/assets/js/custom.js |
Modified (+~55 lines) | Plan 02-04 block: __p02p04EnsureLabel, __p02p04RecalcPrice, delegated input/change bindings, synchronous interval init, poll-retry prestashop.on |
themes/ayon/templates/catalog/product.tpl |
Modified (+~50 lines) | Inline mirror IIFE w {block name='content'} (jQuery zamiast $) — cache-safety dopóki 02-05 systemowego cache-bustera nie ma |
Decisions Made
| Decision | Rationale | Impact |
|---|---|---|
Scope pivot: label obok button zamiast modyfikacji .current-price |
User request mid-task: "właściwą cenę jako napis obok Dodaj do koszyka. Ta cena u góry Od XXX zł niech będzie stała." | Gorna cena zostaje static info ("Od NNN zł / m²"), konkretna suma eksponowana bliżej buttona — lepszy UX konwersji. |
Separate __p02p04Bound guard (nie shared __p02p02Bound) |
Gdy browser cache'uje stare custom.js (bez P04 kodu) + fresh product.tpl → shared guard by zablokowal inline P04 rejestracje | Stale-cache safety: inline P04 zawsze rejestruje, niezaleznie od wersji cached custom.js |
window.__p02p04TryInitial() synchronicznie zamiast jQuery(document).ready(...) |
jQuery ready w kontekscie inline script wewnatrz {block name='content'} Smarty nie firował konsekwentnie (recalc function defined, ale ready callback nigdy nie wywolany) |
Initial render dziala reliably; interval handler sam pokrywa DOM-not-ready case |
| setInterval 10×500ms (5s window) zamiast single retry | Squaremeter totalpriceinfospecific init overwrituje .current-price PO naszym pierwszym recalc; + piece dim stubs moga byc late-injected |
Robustne pokrycie late-override + late-inject. Po 5s user i tak ma pelna reaktywnosc na dimension events. |
Poll-retry dla prestashop.on('updatedProduct') rejestracji |
W inline script runtime, window.prestashop moze jeszcze nie istniec (bundle loads after inline parse) |
AC-4 dziala niezawodnie: handler rejestruje sie gdy tylko prestashop staje sie available |
Inline style="" na labelce zamiast SCSS edit |
User watcher SCSS build zewnetrzny; inline style unika dependency na build step | MVP widocznosc (16px margin, 1.25rem font-weight:700); pozniejsze plan moze przeniesc do SCSS |
Deviations from Plan
Summary
| Type | Count | Impact |
|---|---|---|
| Auto-fixed (scope pivot) | 1 | user request — zmienilo target elementu recalc |
| Auto-fixed (technical) | 4 | niezbedne fixy ujawnione w live debug Playwright |
| Deferred | 1 | AC-5 live IP-flip test (code-path analysis sufficient dla scope) |
Total impact: Scope pivot zmienil target recalc (upper price → new label). 4 technical deviations byly necessary fixes ujawnione w live debug — nie scope creep. Plan 02-04 dziala w production (live verified).
Auto-fixed Issues
1. [Scope pivot] Target recalc: label zamiast .current-price
- Found during: Task 3 checkpoint (user polecenie mid-verification)
- Issue: Pierwotny plan kazal pisac do
.product-prices .current-price(nadpisywac "Od 239 zł / m²"). User pivot: gorna cena ma zostac statyczna. - Fix: Nowa funkcja
__p02p04EnsureLabel()tworzy<span class="p02p04-total-price">przed.product-quantity .add. Recalc pisze tylko do labelki..current-pricenietknieta. - Files: custom.js, product.tpl (inline mirror)
- Verification: Playwright — upper stays "Od 239,00 zł / m2" across wszystkich AC, label dynamicznie pokazuje kalkulacje.
2. [Guard isolation] Separate __p02p04Bound zamiast shared __p02p02Bound
- Found during: Task 1 pre-implementation code review
- Issue: Plan sugerowal dodac P04 kod "wewnatrz istniejącego IIFE po __p02p02Bound guard". To znaczylo ze stale-cache custom.js (ktore ma __p02p02Bound=true ale bez P04) by zablokowal P04 rejestracje w inline mirror (kiedy inline sprawdza shared guard i skipuje caly IIFE).
- Fix: Dodany osobny IIFE z
__p02p04Boundguard w product.tpl. Custom.js rowniez uzywa__p02p04Bound(nie shared). - Files: custom.js, product.tpl
- Verification: Plan deploy + Playwright — inline mirror rejestruje sie niezaleznie od custom.js stanu cache.
3. [Init timing] $(document).ready → synchronous call
- Found during: Task 3 live debug — timeline polling pokazal ZERO price transitions po 6s mimo ze recalc function defined.
- Issue:
jQuery(document).ready(window.__p02p04TryInitial)w kontekscie inline script wewnatrz{block name='content'}Smarty nie firowal (hypothesis: jQuery ready w inline Smarty block ma timing/scope issue). - Fix: Zmienione na
window.__p02p04TryInitial()synchronicznie. Interval sam pokrywa DOM-not-ready case (recalc early-return'uje jesli brak inputow). - Files: custom.js, product.tpl
- Verification: Timeline polling: initial render fires within ~500ms consistently.
4. [Late override] Retry loop 4×350ms → setInterval 10×500ms
- Found during: Task 3 — pierwszy retry pattern (stop-on-success) miał data-calc=null po 6s.
- Issue: Squaremeter
totalpriceinfospecificinit (OR inna funkcja) overwrituje.current-pricePO naszym pierwszym recalc. Stop-on-success pattern przestawal po 1 attempt (data-calc set), potem external overwrite, no more retry. - Fix: Pure interval 10×500ms = 5s okno. Po expire user i tak ma reaktywnosc na input events.
- Files: custom.js, product.tpl
- Verification: Playwright — label stabilizuje sie poprawnie, manual test setInterval calls kazdy poprawny.
5. [prestashop timing] prestashop.on → poll-retry registration
- Found during: Task 3 — AC-4 verify pokazal ze
prestashop.emit('updatedProduct')nie triggerowal recalc. - Issue: Inline script sprawdzal
if (window.prestashop && ...)synchronicznie przy parse.window.prestashopjeszcze nie istnial — prestashop bundle laduje po inline script. Warunek fail, handler nigdy nie rejestrowany. - Fix:
(function bindUpdatedProduct() { if (...) prestashop.on(...); else setTimeout(bindUpdatedProduct, 200); })()— polls az prestashop dostepny. - Files: custom.js, product.tpl
- Verification: Playwright — prestashop.emit triggeruje label re-render z fresh meta (100, 500 test values).
Deferred Items
- AC-5 live IP-flip test — zamiast live test (flip IP gate w product.tpl, FTP upload, navigate, restore) wybrana code-path analysis. Guard
.product-variants-data--neww recalc + inline mirror tylko w{if REMOTE_ADDR...}bloku daje strong correctness argument. Pozniejszy /paul:verify moze dodac live test w calym milestone.
Issues Encountered
| Issue | Resolution |
|---|---|
jQuery .trigger('input') nie firuje delegated handler w test |
Obchodzenie testowe przez native .dispatchEvent(new Event('input', {bubbles:true})) — reflektuje real user interaction correctly. |
| Browser cache serwuje old custom.js mimo FTP 226 | browser_close + fresh navigate between iterations. Systemowy cache-buster (Plan 02-05) rozwiaze long-term. |
| Manual dispatch przez native event różni się od jQuery trigger (AC verify methodology) | Konkluzja: native dispatch == real user typing == handler fires. jQuery trigger artifact testowy. UX behavior confirmed. |
Next Phase Readiness
Ready:
- Milestone v0.1 COMPLETE-READY — Phase 2 Plan 02-04 zamyka core UX (live price visibility). Pozostaje Plan 02-05 (systemowy cache-buster) jako nice-to-have czysty cleanup inline mirror.
- Plan 02-05 ma pattern z Plan 02-04 bindUpdatedProduct() do reużycia (poll-retry registration dla late globals).
Concerns:
- Inline mirror w product.tpl nadal obecny — Plan 02-05 planowal cache-buster pozwalajacy wycofac inline mirror. Jesli milestone ship'uje bez 02-05, inline mirror zostaje (działa poprawnie, ale ~55 extra lines w kazdym product page response).
setInterval 10×500msna każdym product page load — trywialny koszt CPU, ale idiomatycznie lepiej by było wire'owac do konkretnego "squaremeter init complete" eventu gdyby istnial.- Inline style na labelce — pozniejszy plan moze zsynchronizowac z SCSS (theme spacing/typography tokens).
Blockers:
- None.
Phase: 02-product-actions-fixes, Plan: 04 Completed: 2026-04-24