--- 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ł `
`) + 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` + `` 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`, `` 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 `` 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 `` 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 `` 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*