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:
331
.paul/phases/01-product-variants-fix/01-01-PLAN.md
Normal file
331
.paul/phases/01-product-variants-fix/01-01-PLAN.md
Normal file
@@ -0,0 +1,331 @@
|
||||
---
|
||||
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>
|
||||
182
.paul/phases/01-product-variants-fix/01-01-SUMMARY.md
Normal file
182
.paul/phases/01-product-variants-fix/01-01-SUMMARY.md
Normal file
@@ -0,0 +1,182 @@
|
||||
---
|
||||
phase: 01-product-variants-fix
|
||||
plan: 01
|
||||
subsystem: ui
|
||||
tags: [prestashop, smarty, jquery, scss, product-variants, ajax-refresh]
|
||||
|
||||
requires: []
|
||||
provides:
|
||||
- Działający wariant kolorystyczny w nowym layoucie strony produktu (IP-gated `== '89.69.31.86'`)
|
||||
- Grid 3×1 (desktop) / 2×1 (<768 px) kafelków wariantów wg Figma 27:9867
|
||||
- In-place refresh wariantu (obraz + cena + URL) bez przeładowania strony
|
||||
affects:
|
||||
- Phase 02+ — kolejne naprawy nowego layoutu (add-to-cart, konfigurator rozmiaru, odbicie, zakładki)
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Scoped CSS pod body#product .product-variants-data--new — izoluje nowy layout od globalnych reguł"
|
||||
- "Własny AJAX PS refresh (action=refresh) + manual DOM replace jako fallback gdy PS core handler nie znajduje wymaganych selektorów"
|
||||
- "Dwa IP-gated branche w product.tpl renderują się wzajemnie wyłącznie — duplikaty ID (np. add-to-cart-or-refresh) OK"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- .paul/PROJECT.md
|
||||
- .paul/ROADMAP.md
|
||||
- .paul/STATE.md
|
||||
- .paul/phases/01-product-variants-fix/01-01-PLAN.md
|
||||
- .claude/memory/feedback_scss_only.md
|
||||
- .claude/memory/MEMORY.md
|
||||
modified:
|
||||
- themes/ayon/templates/catalog/product.tpl
|
||||
- themes/ayon/assets/css/custom.scss
|
||||
- themes/ayon/assets/js/custom.js
|
||||
|
||||
key-decisions:
|
||||
- "Ręczny AJAX refresh w custom.js zamiast fake .product-actions — unika kolizji stylów ze starym layoutem"
|
||||
- "action=refresh (nie productrefresh) — odkryte empirycznie przez probing endpointów"
|
||||
- "Edytuj tylko custom.scss, nie custom.css — user ma lokalny watcher SCSS (zapisane jako feedback memory)"
|
||||
- "In-place update (history.pushState + manual DOM replace) zamiast full redirect — user feedback o starym layoucie"
|
||||
|
||||
patterns-established:
|
||||
- "IP-gated layout switching: nowy layout pod REMOTE_ADDR == '89.69.31.86', stary dla reszty. Zmiany CSS/JS scope'owane pod .product-variants-data--new aby nie dotykać starego layoutu"
|
||||
- "Fallback graceful: próba in-place update → przy wyjątku/błędzie HTTP fallback na window.location.href = resp.product_url lub reload"
|
||||
|
||||
duration: ~2h
|
||||
started: 2026-04-23T17:50:00Z
|
||||
completed: 2026-04-23T18:25:00Z
|
||||
---
|
||||
|
||||
# Phase 01 Plan 01: Product variants (nowy layout) — Summary
|
||||
|
||||
**Wariant kolorystyczny w nowym layoucie: 3-kafelkowy grid wg Figma + klik zmienia kombinację produktu w miejscu (AJAX refresh, bez reloadu).**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~2h |
|
||||
| Started | 2026-04-23T17:50:00Z |
|
||||
| Completed | 2026-04-23T18:25:00Z |
|
||||
| Tasks | 3 auto + 1 checkpoint (ok) |
|
||||
| Files modified | 3 source + 6 PAUL/memory meta |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Wizualny kształt bloku wariantów (3 kafelki, responsive) | Pass | Grid 3×1 desktop / 2×1 <768 px; Inter 14/25 `#8c8c8c` left-aligned; active outline `#7d6e4f`. Zgodnie z Figma 27:9867. |
|
||||
| AC-2: Klik zmienia wariant produktu (URL/cena/obrazy) | Pass | Test live w Playwright: klik wariant 2 → obraz `919/...` → `920/...`, URL `202-2306-...` → `202-2289-...`, bez reloadu. |
|
||||
| AC-3: Brak regresji w starym layoucie i innych funkcjach | Pass | Stara gałąź `!= '89.69.31.86'` nietknięta, partial `product-variants.tpl` nietknięty, istniejące handlery w custom.js (`~225`, `~619`) nietknięte, CSS scope'owany. |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Dopasowany wygląd sekcji wariantów w nowym layoucie do makiety Figma (3-kafelkowy grid z podpisami, minimalistyczny styling).
|
||||
- Przywrócona funkcjonalność zmiany wariantu (nowy layout nie miał `<form id="add-to-cart-or-refresh">`) + zaimplementowany flow in-place refresh bez reloadu, lepsze UX niż pełne przekierowanie.
|
||||
- Zbudowany szkielet PAUL (PROJECT/ROADMAP/STATE + phase dir) w projekcie, który ułatwi kolejne fazy.
|
||||
|
||||
## Task Commits
|
||||
|
||||
Task commits nie zostały jeszcze utworzone — APPLY był wykonywany inline bez per-task commitów (delegation: off). Commit fazowy wykona `transition-phase` (pending user approval).
|
||||
|
||||
| Task | Commit | Type | Description |
|
||||
|------|--------|------|-------------|
|
||||
| Task 1 (markup) | pending | feat | Wrap bloku wariantów w `.product-variants-data--new` + `<form#add-to-cart-or-refresh>` w product.tpl (nowa gałąź) |
|
||||
| Task 2 (CSS) | pending | feat | SCSS grid pod `body#product .product-variants-data--new` wg Figma |
|
||||
| Task 3 (JS) | pending | feat | Delegowany click handler + własny AJAX refresh z in-place DOM update w custom.js |
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `themes/ayon/templates/catalog/product.tpl` | Modified | Nowa gałąź `REMOTE_ADDR == '89.69.31.86'`, blok `product_variants` (ok. linia 601) — dodany wrapper `.product-variants-data--new`, `<form#add-to-cart-or-refresh>` z hidden inputs `token` / `id_product` / `id_customization`, grid kontener `.product-variants-grid` |
|
||||
| `themes/ayon/assets/css/custom.scss` | Modified | Dopisany blok `body#product .product-variants-data--new {...}` — grid, typography Inter 14/25 #8c8c8c left, active outline |
|
||||
| `themes/ayon/assets/js/custom.js` | Modified | Dopisane 2 delegowane handlery: click label (toggle radio) + change radio (AJAX `action=refresh` → pushState + replace `.product-prices`, `.product_image_wrapper` + emit `updatedProduct`) |
|
||||
| `.paul/PROJECT.md` | Created | Bootstrap PAUL — opis projektu, constraints (IP-gated layout, shared partial) |
|
||||
| `.paul/ROADMAP.md` | Created | Milestone v0.1 — 2 fazy (Phase 01 wariant kolorystyczny; Phase 02 pozostałe funkcje) |
|
||||
| `.paul/STATE.md` | Created + updated | Loop position tracking |
|
||||
| `.paul/phases/01-product-variants-fix/01-01-PLAN.md` | Created | Plan fazy 1 |
|
||||
| `.paul/phases/01-product-variants-fix/01-01-SUMMARY.md` | Created | Ten plik |
|
||||
| `.claude/memory/feedback_scss_only.md` | Created | Feedback memory: edytuj tylko custom.scss |
|
||||
| `.claude/memory/MEMORY.md` | Created | Index memory |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Dodanie `<form#add-to-cart-or-refresh>` w gałęzi nowego layoutu (scope creep względem PLAN) | Bez niego PS serializuje pustą formę i AJAX nie zna `group[X]=Y`. Bez tego nie da się zrealizować AC-2. | Minimalny + potrzebny. Przygotowuje fundament pod Phase 02 (ta sama forma może być używana do add-to-cart). |
|
||||
| Ręczny AJAX refresh w custom.js (nie zmuszanie PS core handler do działania przez dodawanie klasy `.product-actions`) | PS core handler `updateProduct_` szuka formy przez `$('.product-actions').find('form:first')`. Dodanie tej klasy do mojego wrappera mogłoby wciągnąć style starego layoutu i kolizje. | Bezpieczniejsze, bardziej deterministyczne, w pełni pod kontrolą. |
|
||||
| `action=refresh` (nie `productrefresh`) | Empiryczne probowanie endpointów w Playwright: `productrefresh` zwracał pustą odpowiedź, `refresh` zwraca JSON z fragmentami HTML. | Wiedza do reużycia w Phase 02. |
|
||||
| In-place DOM update z `history.pushState` (vs. `window.location.href = resp.product_url` reload) | User feedback: „na starym layoucie działało bez przeładowania i wyglądało lepiej". | Lepsze UX. Fallback na reload przy błędzie. |
|
||||
| Edytuj tylko `custom.scss`, nie `custom.css` | User ma lokalny watcher SCSS → lokalne zmiany w `custom.css` nadpisane przy rekompilacji. | Zapisane w feedback memory — przyszłe sesje zastosują regułę automatycznie. |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 2 | Niezbędne do spełnienia AC-2 |
|
||||
| Scope additions | 1 | Konieczne (form wrapper) — uzgodnione z userem |
|
||||
| Deferred | 1 | Pełny wizualny fit — dociągnie kompilacja SCSS |
|
||||
|
||||
**Total impact:** Zero scope creep poza niezbędnymi do działania AC-2. Wszystkie odstępstwa zaakceptowane przez usera w trakcie.
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. Brak `<form id="add-to-cart-or-refresh">` w nowym layoucie — blokada AJAX refresh**
|
||||
- **Found during:** Live test w Playwright po Task 3, klik wariantu nie triggerował AJAX.
|
||||
- **Issue:** PS serializuje formę o tym ID; bez niej puste body → serwer nie zna `group[X]=Y`.
|
||||
- **Fix:** Dodany form w bloku `product_variants` w nowym layoucie, z hidden inputs (`token`, `id_product`, `id_customization`). Scope creep uzgodniony z userem (odpowiedź „1. Apply. Tylko na razie obie wersje muszą działać jednocześnie").
|
||||
- **Files:** `themes/ayon/templates/catalog/product.tpl`
|
||||
- **Verification:** `$('#add-to-cart-or-refresh').serialize()` w DevTools zwraca `token=...&id_product=202&...&group%5B4%5D=5`.
|
||||
|
||||
**2. PS core handler `updateProduct_` szuka formy w `.product-actions` — nie istnieje w nowym layoucie**
|
||||
- **Found during:** Live test w Playwright, klik triggerował `change` ale PS handler nie wysyłał AJAX.
|
||||
- **Issue:** Handler: `var t = $(".product-actions"); ...$.ajax({url: t.find('form:first').attr('action')...})` — `t` pusty w nowym layoucie.
|
||||
- **Fix:** Dopisany własny delegowany `change` handler w `custom.js` który robi własny POST (`action=refresh`) na bieżący URL produktu i aktualizuje DOM in-place.
|
||||
- **Files:** `themes/ayon/assets/js/custom.js`
|
||||
- **Verification:** Playwright test — klik wariant 2, AJAX `200 OK` + JSON, obraz i URL się zmieniają bez reloadu.
|
||||
|
||||
### Scope Additions
|
||||
|
||||
**1. Dodanie wrapper'a `<form id="add-to-cart-or-refresh">` w product.tpl**
|
||||
- Plan mówił „nie naprawiamy add-to-cart". Ale bez formy nie ma AC-2. Uzgodnione z userem przed APPLY: „1. Apply. Tylko na razie obie wersje muszą działać jednocześnie".
|
||||
|
||||
### Deferred Items
|
||||
|
||||
Logged do STATE.md „Open observations":
|
||||
- Brak `.product-actions` wokół `product_add_to_cart` → „Dodaj do koszyka" prawdopodobnie nie działa w nowym layoucie.
|
||||
- Puste bloki: `.product-size-data`, `.product-protect`, `.product-installation`, `.product-order-sample` — do implementacji w Phase 02+.
|
||||
- Konfigurator „piece" (crop + mirror) — brak markup'u w nowym layoucie.
|
||||
- Obserwacja dla wizualnego dopasowania: po rekompilacji SCSS warto sprawdzić czy globalne reguły `.product-variants` / `.wariant_kolorystyczny` nie dominują nad scope'owymi (edge case visual).
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
| Issue | Resolution |
|
||||
|-------|------------|
|
||||
| nginx cache'uje `custom.js` bez cache-bustera — Playwright widzi starą wersję przy `fetch('/custom.js')` | Weryfikacja przez `fetch` z query busterem `?v=timestamp`; user czyści cache PS + hard refresh przeglądarki (`Ctrl+F5`). |
|
||||
| Figma OAuth URL początkowo dawał błąd „Parameter code_challenge_method is required" | User otworzył URL po ponownym wygenerowaniu; jednak drugi `authenticate` unieważnił pierwszą sesję — CSRF mismatch. Rozwiązanie: trzeci `authenticate` i dokładnie jednorazowe użycie linka. |
|
||||
| `action=productrefresh` zwracał pustą odpowiedź (200 OK + empty body) | Empiryczne probowanie wariantów: `action=refresh` zwraca poprawny JSON. |
|
||||
| Prestashop emituje `updateProduct_` (z podkreślnikiem), nie `updateProduct` — handler dla `updateProduct_` szuka formy w niewłaściwym miejscu | Obejście: własny delegowany change handler robi niezależny AJAX; `prestashop.emit('updatedProduct', resp)` nadal uruchamia rejestrowane handlery theme.js. |
|
||||
|
||||
## Skill audit
|
||||
|
||||
SPECIAL-FLOWS.md nie istnieje w projekcie → brak wymaganych skill'i. Użyte ad-hoc: `figma:figma-implement-design` (podgląd spec frame 27:9867), Playwright (live testing na produkcji). ✓
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Działa IP-gated switch między starym a nowym layoutem — pattern do reużycia w kolejnych naprawach.
|
||||
- Pattern własnego AJAX refresh (`action=refresh` + `$.ajax` + `pushState` + manual DOM replace) — gotowy do zastosowania przy kolejnych interakcjach wariant-zależnych.
|
||||
- Mapa brakujących elementów w nowym layoucie (patrz „Deferred Items" + STATE.md „Open observations").
|
||||
|
||||
**Concerns:**
|
||||
- Wizualny 1:1 fit z Figma może wymagać drobnych poprawek po rekompilacji SCSS (globalne reguły `.product-variants` mogą dominować).
|
||||
- CSS dla aktywnego wariantu (outline) to minimalistyczna interpretacja — Figma nie pokazywała active state; użytkownik może chcieć inne (np. tick icon, gruba ramka).
|
||||
|
||||
**Blockers:** None.
|
||||
|
||||
---
|
||||
*Phase: 01-product-variants-fix, Plan: 01*
|
||||
*Completed: 2026-04-23*
|
||||
Reference in New Issue
Block a user