` (gałąź nowego layoutu) dodać overlay:
- `
` 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 `
` w `.product-box.product-size-data`, dodać wrapper `.product-size-data--new` i umieścić:
- `
` z `
`, `
` (zawiera `
Wybierz rozmiar `),
`
Wymiary tapety
`,
`
`
- `
`
(d) Dodać rodzeństwem (poza image wrapperem, gdziekolwiek w kontenerze): `
10
` `
10
` — 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.
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
AC-1 spełnione + częściowo AC-3 (hidden inputs istnieją w formie).
Task 2: JS — piece init() + re-init po updatedProduct, zachowanie istniejących handlerów
themes/ayon/assets/js/custom.js
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).
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
AC-2 i AC-4 spełnione.
Task 3: CSS — scoped styles i wizualna warstwa piece dla nowego layoutu
themes/ayon/assets/css/custom.scss
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`.
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).
AC-1 spełnione wizualnie (kontrolki widoczne, piece z position/cursor), AC-5 zachowane.
- 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`
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=&crop_pos_y=&crop_width=&crop_height=&piece_bg_top=&piece_bg_left=`
- 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.
Napisz „approved" aby zamknąć APPLY, lub opisz problemy (z dokładnymi krokami odtworzenia) do naprawy.
## 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.
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
- 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
After completion, create `.paul/phases/02-product-actions-fixes/02-01-SUMMARY.md`