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>
332 lines
16 KiB
Markdown
332 lines
16 KiB
Markdown
---
|
||
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 ~604–607 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 604–607) 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>
|