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:
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>
|
||||
Reference in New Issue
Block a user