Files
newwalls.pl/.paul/phases/01-product-variants-fix/01-01-PLAN.md
Jacek Pyziak 7ac795ba3f 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>
2026-04-23 23:33:45 +02:00

332 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 01-product-variants-fix
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- themes/ayon/templates/catalog/product.tpl
- themes/ayon/assets/css/custom.scss
- themes/ayon/assets/css/custom.css
- themes/ayon/assets/js/custom.js
autonomous: false
delegation: off
---
<objective>
## Goal
W nowym layoucie strony produktu (gałąź `{if $smarty.server.REMOTE_ADDR == '89.69.31.86'}`) doprowadzić blok `.product-variants` w sekcji `.product-box.product-variants-data` (pod nagłówkiem „Wybierz wersję kolorystyczną") do stanu:
1. **Wizualnie** — wygląda jak wzór `d:\temp\Frame 33.png`:
- kafelki wariantów (miniaturki) ułożone obok siebie w rzędzie (min. 3 w linii na desktopie, responsywnie),
- każdy kafelek = kwadratowe zdjęcie tekstury wariantu,
- pod każdym kafelkiem podpis z nazwą wariantu (np. „Lorem ipsum" w makiecie → `{$group_attribute.name}`),
- aktywny wariant wyróżniony (obramowanie lub inny stan aktywny),
- bez dziedziczenia stylów starego „box-color-variants" (które zakładały modal/overlay).
2. **Funkcjonalnie** — kliknięcie miniaturki wariantu zmienia wariant produktu (przekierowanie/refresh PrestaShop do właściwej kombinacji), analogicznie do starego wyglądu. Pozostałe funkcje strony produktu (konfigurator rozmiaru, odbicie, dodanie do koszyka) NIE mogą zostać zepsute.
## Purpose
Klient używa IP `89.69.31.86` do prezentacji nowego wyglądu. Wariant kolorystyczny jest kluczowym elementem konwersji — bez działającej zmiany wariantu strona produktu jest nieużywalna w nowym layoucie.
## Output
- Zmieniony `product.tpl` (tylko sekcja `{if ... == '89.69.31.86'}` — część dotycząca bloku `product_variants`).
- Nowe reguły SCSS/CSS dla `.product-variants-data .product-variants` widoczne TYLKO w nowym layoucie (np. scope `body#product .product-box.product-variants-data .product-variants ...`).
- Ewentualnie: drobna korekta w `custom.js` zapewniająca zmianę wariantu po kliknięciu (delegowany handler zamiast bezpośredniego `.click()`), bez ingerowania w starą ścieżkę.
- SUMMARY.md w `.paul/phases/01-product-variants-fix/01-01-SUMMARY.md`.
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
## Source Files
@themes/ayon/templates/catalog/product.tpl
@themes/ayon/templates/catalog/_partials/product-variants.tpl
@themes/ayon/assets/css/custom.scss
@themes/ayon/assets/js/custom.js
## Reference Mockup
- `d:\temp\Frame 33.png` — 3 kwadratowe kafelki obok siebie, pod każdym krótki podpis tekstowy.
## Current State (z researchu)
- Partial `product-variants.tpl`:
- Dla `$product_variant_mode == 2` (color) generuje `<ul id="group_{id}"><li class="wariant_kolorystyczny"><label><input type="radio" name="group[..]"><img .../><span class="color ..."></span></label></li>...</ul>`.
- Radio + label to standardowy mechanizm PrestaShop do zmiany wariantu (core.js nasłuchuje `change` na `input[name^="group["]`).
- W nowym layoucie (linia ~604607 w `product.tpl`) partial jest włączany z `$product_variant_mode = 2` w bloku:
```
<div class="product-box product-variants-data">
<h4 class="block-title">Wybierz wersję kolorystyczną</h4>
{include file='catalog/_partials/product-variants.tpl'}
</div>
```
ale NIE jest owinięty w `#box-color-variants`, przez co istniejące reguły CSS i handlery JS pisane pod stary layout (modal z fadeIn/fadeOut) nie aplikują się.
- `custom.js:225` — `jQuery(".wariant_kolorystyczny").click(...)` — bezpośrednie binding, odpala tylko logikę „piece" konfiguratora (nie zmiana wariantu). Zmiana wariantu powinna wychodzić z natywnego PrestaShop flow (change na radio).
- `custom.js:619` — `$(document).on('click', '#box-color-variants .wariant_kolorystyczny', ...)` — zamyka modal w starym layoucie. W nowym layoucie ten selektor się nie dopasowuje (i dobrze — nowy wygląd nie jest modalem).
</context>
<acceptance_criteria>
## AC-1: Wizualny kształt bloku wariantów
```gherkin
Given jestem klientem korzystającym z IP 89.69.31.86
And otwieram stronę dowolnego produktu z kilkoma wariantami kolorystycznymi
When patrzę na sekcję "Wybierz wersję kolorystyczną"
Then widzę miniatury wariantów ułożone w siatce / rzędzie (min. 3 kafelki w linii na szerokim ekranie)
And każdy kafelek jest kwadratowy i zawiera obraz tekstury wariantu
And pod każdym kafelkiem jest widoczny podpis z nazwą wariantu
And aktywny (wybrany) wariant jest wizualnie wyróżniony (np. obramowanie, tło)
And układ jest responsywny (co najmniej 2 kafelki w linii poniżej 768 px)
```
## AC-2: Klik zmienia wariant produktu
```gherkin
Given jestem klientem korzystającym z IP 89.69.31.86
And otwieram stronę produktu w nowym layoucie
When klikam miniaturę innego wariantu niż aktualnie zaznaczony
Then PrestaShop przełącza na wybraną kombinację (URL / cena / obrazy produktu aktualizują się tak samo jak w starym layoucie)
And klikany wariant staje się wariantem aktywnym (wizualne wyróżnienie)
And żadne błędy JS nie pojawiają się w konsoli
```
## AC-3: Brak regresji w starym layoucie i innych funkcjach nowego
```gherkin
Given jestem klientem z IP innym niż 89.69.31.86
When otwieram stronę produktu
Then stary layout wygląda i działa identycznie jak przed zmianami (wariant kolorystyczny w modalu #box-color-variants, konfigurator rozmiaru, odbicie, dodanie do koszyka)
Given jestem klientem z IP 89.69.31.86 w nowym layoucie
When używam innych elementów strony produktu (konfigurator rozmiaru, przyciski, zakładki opisu)
Then elementy te zachowują się co najmniej tak samo jak przed zmianami w tym planie (nie zepsute dodatkowo)
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Dostosuj markup bloku product-variants w nowym layoucie (product.tpl)</name>
<files>themes/ayon/templates/catalog/product.tpl</files>
<action>
W gałęzi `{if $smarty.server.REMOTE_ADDR == '89.69.31.86'}` (okolice linii 604607) wokół `{include file='catalog/_partials/product-variants.tpl'}` dla `$product_variant_mode = 2` dodaj pomocniczy kontener-klasę, która pozwoli stargetować nowy wygląd w CSS bez ruszania partiala i starego kodu. Struktura docelowa:
```smarty
{block name='product_variants'}
{$product_variant_mode = 2}
<div class="product-box product-variants-data product-variants-data--new">
<h4 class="block-title">Wybierz wersję kolorystyczną</h4>
<div class="product-variants-grid">
{include file='catalog/_partials/product-variants.tpl'}
</div>
</div>
{/block}
```
Wymagania:
- NIE zmieniaj partiala `product-variants.tpl` (dzielony ze starym layoutem).
- NIE zmieniaj gałęzi `{if ... != '89.69.31.86'}` — stary layout nietknięty.
- Owinięcie w `.product-variants-grid` daje nam czysty scope CSS tylko dla nowego widoku.
- W partialu dla mode=2 jest `{if $id_attribute_group == 5}<a ... class="fancybox-material-controls">` — w mode=2 ten warunek nie dotyczy, więc OK.
Avoid: modyfikowania partiala, starej ścieżki, innych sekcji `product.tpl`.
</action>
<verify>
`git diff themes/ayon/templates/catalog/product.tpl` pokazuje zmianę TYLKO w bloku między liniami `{if $smarty.server.REMOTE_ADDR == '89.69.31.86'}` a `{/if}` (ta druga linia ~800+). Brak zmian w gałęzi `!=`.
</verify>
<done>AC-1 przygotowane strukturalnie (markup gotowy pod CSS); AC-3 niezagrożone (stary layout nie ruszony).</done>
</task>
<task type="auto">
<name>Task 2: Stwórz style SCSS/CSS dla .product-variants-data--new (kafelki 3-w-rzędzie)</name>
<files>themes/ayon/assets/css/custom.scss, themes/ayon/assets/css/custom.css</files>
<action>
W `custom.scss` dodaj blok (na końcu pliku lub w sekcji poświęconej stronie produktu):
```scss
/* NEW product page — color variants grid (IP-gated layout) */
body#product .product-variants-data--new {
.product-variants-grid > .product-variants {
display: block;
margin: 0;
}
.product-variants-grid ul[id^="group_"] {
list-style: none;
margin: 0;
padding: 0;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
@media (max-width: 768px) {
grid-template-columns: repeat(2, 1fr);
}
}
.product-variants-grid li.wariant_kolorystyczny {
float: none;
width: auto;
margin: 0;
padding: 0;
}
.product-variants-grid li.wariant_kolorystyczny > label {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 8px;
cursor: pointer;
border: 2px solid transparent;
border-radius: 4px;
padding: 6px;
transition: border-color .15s ease-in-out;
/* ukryj natywny radio — wybór po kliknięciu labela */
input.input-color {
position: absolute;
opacity: 0;
pointer-events: none;
}
img {
width: 100%;
aspect-ratio: 1 / 1;
object-fit: cover;
display: block;
}
/* podpis = ukryty span.sr-only.tip z nazwą wariantu — pokaż go */
.sr-only.tip {
position: static;
width: auto;
height: auto;
clip: auto;
overflow: visible;
white-space: normal;
font-size: 14px;
text-align: center;
color: #3c3c3c;
}
/* kwadracik koloru / tekstury ze starego markupu — w nowym widoku ukryty,
bo prezentujemy sam obraz wariantu */
> span.color { display: none; }
&:hover { border-color: #c9bda4; }
}
.product-variants-grid li.wariant_kolorystyczny > label:has(input.input-color:checked) {
border-color: #7d6e4f;
}
}
```
Następnie zregeneruj `custom.css`:
- Jeśli w projekcie jest pipeline SCSS → uruchom kompilację.
- Jeśli nie — odpowiadający blok CSS (rozwinięty, bez zagnieżdżeń) dopisz ręcznie na końcu `custom.css` tak, by trafił do produkcji.
Avoid:
- Nadpisywania reguł globalnych `.product-variants`, `.wariant_kolorystyczny` bez scope'a `body#product .product-variants-data--new` — zepsułoby to stary layout i quickview.
- Usuwania istniejących reguł w custom.css/custom.scss.
</action>
<verify>
1. Zaloguj się z IP 89.69.31.86, otwórz produkt z ≥3 wariantami.
2. Wizualnie: 3 kafelki obok siebie, kwadratowe obrazy, podpisy pod spodem, aktywny wyróżniony obramowaniem.
3. Stary layout (inne IP) w tabeli wariantów wygląda tak jak przed zmianą.
</verify>
<done>AC-1 spełnione; AC-3 (część wizualna starego layoutu) utrzymane.</done>
</task>
<task type="auto">
<name>Task 3: Zapewnij zmianę wariantu po kliknięciu w nowym layoucie (custom.js)</name>
<files>themes/ayon/assets/js/custom.js</files>
<action>
PrestaShop core.js natywnie nasłuchuje `change` na `input[name^="group["]` — klik w `<label>` powinien przełączyć radio i wywołać refresh wariantu. Problem pojawia się, gdy po refreshu AJAX kontener `.product-variants` jest podmieniany i bezpośrednie bindingi (np. `custom.js:225 jQuery(".wariant_kolorystyczny").click(...)`) giną.
Dodaj na końcu pliku `themes/ayon/assets/js/custom.js` (po istniejącym handlerze `#box-color-variants .wariant_kolorystyczny`) delegowany listener gwarantujący działanie również w nowym layoucie:
```js
/* NEW layout — klik w kafelek wariantu zmienia wariant (delegowany, przeżywa refresh AJAX) */
$(document).on('click', '.product-variants-data--new .wariant_kolorystyczny label', function (e) {
var $label = $(this);
var $radio = $label.find('input.input-color');
if (!$radio.length) return;
if ($radio.is(':checked')) return; // już aktywny
$radio.prop('checked', true).trigger('change');
});
```
Uwaga:
- NIE zmieniaj istniejącego handlera `jQuery(".wariant_kolorystyczny").click(...)` (linia ~225) — obsługuje logikę „piece" i działa w starym layoucie.
- NIE zmieniaj handlera `#box-color-variants .wariant_kolorystyczny` (linia ~619) — zamyka modal w starym layoucie.
- Scope `.product-variants-data--new` (dodany w Task 1) zapewnia, że nowy kod nie dotyka starego layoutu.
- `trigger('change')` na radio uruchamia natywny mechanizm PrestaShop do przełączenia wariantu.
Avoid: `e.preventDefault()` na labelu (zablokowałoby natywny toggle radia).
</action>
<verify>
1. IP 89.69.31.86 → strona produktu z wariantami → otwórz DevTools → Console.
2. Kliknij inny wariant niż aktualny → strona powinna zaktualizować wariant (URL, cena, zdjęcia) bez błędów JS.
3. Kliknij ponownie ten sam wariant → nic się nie zmienia (brak zbędnych przeładowań).
</verify>
<done>AC-2 spełnione; AC-3 utrzymane (stara ścieżka nietknięta).</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>
Nowy wygląd bloku wariantów kolorystycznych na stronie produktu (IP 89.69.31.86):
- 3 kafelki obok siebie z obrazem + podpisem,
- klik w kafelek przełącza wariant produktu,
- stary layout (inne IP) nie zmieniony.
</what-built>
<how-to-verify>
1. Jako klient z IP 89.69.31.86 wejdź na stronę dowolnego produktu z kilkoma wariantami kolorystycznymi.
2. Sprawdź wizualnie: sekcja „Wybierz wersję kolorystyczną" ma wyglądać jak w `d:\temp\Frame 33.png` (3 kafelki w rzędzie, podpisy pod spodem).
3. Sprawdź na mniejszym oknie (poniżej 768 px): kafelki układają się w 2 kolumny.
4. Kliknij inny wariant niż aktywny → potwierdź: URL się zmienia, cena i obrazy produktu się aktualizują, klikany kafelek staje się wyróżniony.
5. Otwórz konsolę DevTools — brak błędów JS.
6. Wejdź na tę samą stronę spoza IP 89.69.31.86 → potwierdź: stary layout (modal #box-color-variants) wygląda i działa jak przed zmianami.
7. Na stronie produktu z IP 89.69.31.86 sprawdź, że pozostałe elementy (konfigurator rozmiaru, odbicie, dodanie do koszyka) działają co najmniej tak samo jak przed zmianą.
</how-to-verify>
<resume-signal>Wpisz "approved" aby zamknąć plan lub opisz problemy do poprawy.</resume-signal>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- Gałąź `{if $smarty.server.REMOTE_ADDR != '89.69.31.86'}` w `product.tpl` (stary layout produkcyjny).
- `themes/ayon/templates/catalog/_partials/product-variants.tpl` (partial wspólny dla starego i nowego + quickview).
- Istniejące handlery `jQuery(".wariant_kolorystyczny").click(...)` (custom.js ~225) i `$(document).on('click', '#box-color-variants .wariant_kolorystyczny', ...)` (custom.js ~619).
- Globalne reguły CSS dla `.product-variants`, `.wariant_kolorystyczny`, `#box-color-variants` w `custom.scss` / `custom.css` — nowy CSS dopisywany TYLKO pod scope `.product-variants-data--new` (lub `body#product .product-variants-data--new`).
- PrestaShop core (`themes/ayon/assets/js/theme.js`, bundles).
## SCOPE LIMITS
- Ten plan naprawia wyłącznie sekcję wariantów kolorystycznych w nowym layoucie.
- Nie naprawiamy w tym planie: konfiguratora rozmiaru, odbicia lustrzanego, ceny, dodania do koszyka, zakładek, próbki, struktur — nawet jeśli są popsute (idą do Phase 02).
- Nie dodajemy nowych zależności (brak npm / composer install).
- Nie zmieniamy backendu (kontrolery, moduły).
</boundaries>
<verification>
Przed zamknięciem planu:
- [ ] `git diff` pokazuje zmiany wyłącznie w: `product.tpl` (gałąź nowego layoutu), `custom.scss`, `custom.css`, `custom.js`.
- [ ] Test ręczny z IP 89.69.31.86 — AC-1, AC-2 spełnione.
- [ ] Test ręczny z innym IP — AC-3 część „stary layout" OK.
- [ ] Brak błędów JS w konsoli na stronie produktu w obu trybach.
- [ ] Checkpoint `human-verify` zatwierdzony przez użytkownika ("approved").
</verification>
<success_criteria>
- AC-1, AC-2, AC-3 spełnione.
- Checkpoint zatwierdzony.
- Brak regresji w partialu `product-variants.tpl` i starym layoucie.
</success_criteria>
<output>
Po zakończeniu planu utwórz `.paul/phases/01-product-variants-fix/01-01-SUMMARY.md` z:
- listą zmodyfikowanych plików (z krótkim opisem zmiany),
- decyzjami (np. dlaczego scope `.product-variants-data--new` zamiast globalnego),
- potencjalnymi side-effectami do obserwacji w Phase 02.
</output>