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>
5.9 KiB
5.9 KiB
phase, plan, status, date
| phase | plan | status | date |
|---|---|---|---|
| 02-product-actions-fixes | 03 | complete | 2026-04-23 |
Plan 02-03 — Customization save + success modal (SUMMARY)
Goal
End-to-end add-to-cart UX w nowym layoucie: customization zapisuje się w DB (widoczne w koszyku jako "Szczegóły") + success modal po dodaniu. Parzystość ze starym layoutem.
Result
Complete (5/5 AC pass). Blocker na usunięcie IP gate — removed.
AC Status (live Playwright verified 2026-04-23)
| AC | Status | Evidence |
|---|---|---|
| AC-1 Customization save | ✓ PASS | Cart items "Rain of flowers" pokazują "Szczegóły produktu" z Wymiar: Szerokość 2.0 m, Wysokość 1.5 m, 3.00 m2. POST payload zawiera discretion=on, calculated_total=3.0000, product_total_price_calc=717, qty_alt=2.0, qty_alth=1.5, dim=<verbose PL>. |
| AC-2 Success modal | ✓ PASS | Modal #blockcart-modal.modal rendered po add-to-cart, z "Produkt pomyślnie dodany do koszyka", nazwą produktu, ceną, "Kontynuuj zakupy" + "Przejdź do kasy" buttons. |
| AC-3 Cart "Szczegóły" | ✓ PASS | /pl/koszyk pokazuje "Szczegóły produktu" button, rozwija się na dimension data. |
| AC-4 Single add | ✓ PASS | Po re-entrancy guard (window.__p02p02InFlight): 1× POST /pl/koszyk + 1× POST /module/ps_shoppingcart/ajax. Wcześniej było 2×. |
| AC-5 OLD layout regression | ✓ PASS | Flip IP temporary → .pp_stick_parent present, window.__p02p02Bound: false (nasz handler NIE aktywowany w OLD). Structural isolation confirmed. |
Key Discoveries (Task 1 — OLD payload capture via Playwright)
Source of truth: fetched live POST payload z OLD layout (IP flipped to 255.255.255.255 temporarily, restored after).
26 fields w OLD #add-to-cart-or-refresh → /pl/koszyk:
token, id_product, id_customization=0
dim="Szerokość {w}.0 m, Wysokość {h} m, {area}.00 m2, Pozycja {x} {y} <span> ,Pozycja tła {bt} {bl} ,Odbicie {r} </span>"
converted_ea={area:.4f} calculated_total={area:.4f} grand_calculated_total=""
extrafeevalue=0 wastevalue=0 fixed_price, base_price (przykład: 239)
is_crop, is_reflection, crop_pos_x, crop_pos_y, crop_width, crop_height
product_total_price_calc=<int: base_price × area>
piece_bg_top, piece_bg_left
group[5]=9 (material) group[4]=5/6/7 (wariant kolorystyczny)
qty_alt={width_m} qty_alth={height_m}
discretion=on qty=1 (cart quantity)
Kluczowe insighty:
dimfield jest VERBOSE Polish description, NIE "200x150" jak sugerował pierwotny plan.qty(cart quantity) ≠qty_alt/qty_alth(dimension values). Squaremeter uses alt fields.product_total_price_calc= integer =base_price × calculated_total(area m²).- Wszystkie numeric fields stored as strings, decimals z 4 miejscami po przecinku dla converted_ea/calculated_total.
POST #2 (modal fetch):
- Endpoint:
/module/ps_shoppingcart/ajax(POST) - Body:
action=add-to-cart&id_product&id_product_attribute&id_customization - Response:
{preview: '<blockcart HTML>', modal: '<#blockcart-modal Bootstrap HTML>'} - Modal uses Bootstrap
#blockcart-modal.modal.fade. Buttons: "Kontynuuj zakupy" (data-dismiss) + "Przejdź do kasy" (link →/pl/koszyk?action=show).
Decisions
| Data | Decyzja | Wpływ |
|---|---|---|
| 2026-04-23 | Używać verbose PL dim format (nie "200x150") | Kompatybilność z squaremeter customization hook + cart display consistency. |
| 2026-04-23 | basePrice z meta[property="product:price:amount"] (fallback chain) |
#product_base_price/#product_fixed_price nie istnieją w NEW layout (są tylko w OLD shared partial). Meta tag reliably renders w NEW. |
| 2026-04-23 | window.__p02p02InFlight re-entrancy guard |
Jedynie __p02p02Bound nie wystarczył — handler firował 2× (root cause niepotwierdzony, ale guard fix'uje skutecznie). Reset przez complete: callback. |
| 2026-04-23 | Move inline script INSIDE {block name='content'} |
Kluczowy bug fix: template użyje {extends file=$layout}. Smarty renderuje TYLKO content w blockach. Stary inline script (Plan 02-02) był MIĘDZY {/block} a {/if} — NIGDY nie renderowany. Znaczy: Plan 02-02 AC-3 pass było przez custom.js alone; inline mirror był dead code. |
| 2026-04-23 | Manual FTP upload via curl | ftp-kr VSCode extension nie łapie edycji z Claude Code Edit tool. curl -T ftp:// jako work-around. |
| 2026-04-23 | Dual-source sq fields: DOM hidden inputs + explicit appended sqFields | Defense-in-depth. $form.serialize() jeśli shared partial ma pola. Appended wins w PHP $_POST (last-wins) gdyby brakowało. |
Modified Files
themes/ayon/assets/js/custom.js(lines 1026-1200, ~175 new lines) — sq fields injection, modal chain, re-entrancy guard.themes/ayon/templates/catalog/product.tpl(lines 756, 768-928) — moved{/block}to wrap inline script; added sq fields + modal chain in inline mirror.
Boundaries respected
- ✓
modules/squaremeter/*not modified - ✓ Shared partials (
themes/ayon/templates/catalog/_partials/*) not modified - ✓ Old layout branch
{if != '89.69.31.86'}unchanged - ✓ Plan 02-01 override of
totalpriceinfospecific/prodpreserved (still no-op) - ✓ No new dependencies
Deferred (Plan 02-04+)
- Cena per-sqm UI (live calculation on dimension change) — scope Plan 02-04.
- Systemowy cache-buster
?v=<mtime>dla<script src=custom.js>— scope Plan 02-05. Inline mirror z Plan 02-02 teraz faktycznie renderowany (po block-move fix), pełni swój defensywny cel. - Dimension input UI (alternatywa do piece popup) — user currently uses piece popup (200×150 cm → 2.0×1.5 m conversion hardcoded).
- Puste bloki (
.product-protect,.product-installation,.product-order-sample) — Plan 02-06. - Cleanup test carts — kilka "Rain of flowers" items w testowym koszyku z moich Playwright runs.
Blocker na usunięcie IP gate
REMOVED. Milestone v0.1 może być teraz shipped — można wyłączyć REMOTE_ADDR == '89.69.31.86' gate po akceptacji UAT przez usera.