Files
newwalls.pl/.paul/phases/01-product-variants-fix/01-01-SUMMARY.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

183 lines
12 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
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*