--- phase: 63-order-item-personalization plan: 01 type: execute wave: 1 depends_on: [] files_modified: - database/migrations/20260401_000063_add_personalization_to_order_items.sql - src/Modules/Settings/ShopproOrderMapper.php - src/Modules/Orders/OrderImportRepository.php - resources/views/orders/show.php - resources/scss/orders/_show.scss autonomous: true --- ## Goal Pobieranie danych personalizacji produktów z shopPRO i wyświetlanie ich w szczegółach zamówienia orderPRO. ## Purpose Sprzedawcy widzą personalizacje klientów (np. tekst grawerunku, link Spotify, dedykacja) bezpośrednio w orderPRO bez konieczności logowania do shopPRO. Kluczowe dla realizacji zamówień z personalizowanymi produktami. ## Output - Migracja DB: kolumna `personalization` w `order_items` - Mapper: ekstrakcja `custom_fields` z odpowiedzi shopPRO API - UI: wyświetlanie personalizacji pod nazwą produktu w widoku zamówienia ## Project Context @.paul/PROJECT.md @.paul/ROADMAP.md @.paul/STATE.md ## Source Files @src/Modules/Settings/ShopproOrderMapper.php — mapItems() linia 527, mapuje produkty z API shopPRO @src/Modules/Orders/OrderImportRepository.php — replaceItems() wstawia pozycje do DB @resources/views/orders/show.php — widok szczegółów zamówienia, sekcja produktów @database/migrations/20260302_000018_create_orders_tables_and_schedule.sql — schemat order_items No specialized flows configured. ## AC-1: Personalizacja zapisana w bazie danych ```gherkin Given zamówienie z shopPRO zawiera produkty z custom_fields When import zamówienia się wykonuje (cron lub ręczny) Then kolumna personalization w order_items zawiera dane personalizacji jako czysty tekst (bez HTML) ``` ## AC-2: Personalizacja wyświetlana w szczegółach zamówienia ```gherkin Given zamówienie w orderPRO ma pozycje z wypełnioną personalizacją When użytkownik otwiera szczegóły zamówienia (/orders/{id}) Then pod nazwą produktu wyświetla się sekcja personalizacji z etykietami i wartościami ``` ## AC-3: Brak personalizacji nie powoduje błędów ```gherkin Given zamówienie z shopPRO zawiera produkty BEZ custom_fields (puste lub null) When import się wykonuje i użytkownik otwiera szczegóły Then kolumna personalization jest NULL i w UI nie pojawia się sekcja personalizacji ``` Task 1: Migracja DB + mapping personalizacji database/migrations/20260401_000063_add_personalization_to_order_items.sql, src/Modules/Settings/ShopproOrderMapper.php, src/Modules/Orders/OrderImportRepository.php 1. Utworzyć migrację dodającą kolumnę `personalization TEXT NULL` do tabeli `order_items` (po kolumnie `payload_json`). 2. W `ShopproOrderMapper::mapItems()` (linia 561-580): - Wyciągnąć `custom_fields` z `$row` za pomocą `$this->readPath($row, ['custom_fields'])` - Przekonwertować HTML na czysty tekst: zamienić `
` na `\n`, usunąć tagi HTML (`strip_tags`), `html_entity_decode`, `trim` - Zapisać jako klucz `personalization` w tablicy wynikowej (obok `payload_json`) - Jeśli wynik jest pustym stringiem, ustawić `null` 3. W `OrderImportRepository::replaceItems()`: - Dodać kolumnę `personalization` do INSERT query - Bindować wartość z tablicy item Avoid: Nie zmieniać formatu `payload_json` — surowe dane API muszą zostać nienaruszone. Avoid: Nie parsować HTML do JSON — czysty tekst z zachowanymi newlines jest wystarczający.
- Migracja wykonuje się bez błędów: `php database/migrate.php` - Po re-imporcie zamówienia z shopPRO kolumna `personalization` zawiera tekst - Zamówienia bez personalizacji mają NULL w kolumnie AC-1 i AC-3 satisfied: personalizacja zapisywana w DB, brak personalizacji = NULL
Task 2: Wyświetlanie personalizacji w widoku zamówienia resources/views/orders/show.php, resources/scss/orders/_show.scss 1. W `resources/views/orders/show.php`, w sekcji renderującej produkty zamówienia: - Pod nazwą produktu (`original_name`) dodać blok warunkowy: jeśli `$item['personalization']` nie jest puste - Wyświetlić personalizację w `
` z ikoną lub etykietą "Personalizacja:" - Każda linia personalizacji (split po `\n`) jako osobna linia w UI - Escape HTML: użyć `e()` helpera na każdej linii 2. W SCSS dodać style: - `.item-personalization` — mały font (0.85em), kolor muted, lekki padding-top - Etykiety (tekst przed `:`) mogą być pogrubione via CSS lub pozostawione jako plain text - Kompaktowy układ, bez nadmiernych marginesów Avoid: Nie renderować surowego HTML z custom_fields — zawsze escape. Avoid: Nie dodawać nowych zależności JS — to statyczny tekst. - Otworzyć zamówienie z personalizacją w przeglądarce — personalizacja widoczna pod nazwą produktu - Otworzyć zamówienie bez personalizacji — brak dodatkowej sekcji - Sprawdzić XSS: wpisać `` w custom_fields shopPRO — powinno być escaped AC-2 satisfied: personalizacja wyświetlana w UI pod nazwą produktu ## DO NOT CHANGE - shopPRO codebase (API już zwraca custom_fields — zero zmian po stronie shopPRO) - payload_json format i zawartość w order_items - Istniejące kolumny i indeksy order_items - Logika importu zamówień poza mapowaniem items (OrderImportRepository::upsertOrderAggregate flow) ## SCOPE LIMITS - Tylko personalizacja z shopPRO (nie Allegro, nie Erli) - Tylko widok szczegółów zamówienia — bez zmian na liście zamówień - Bez edycji personalizacji w orderPRO — read-only display - Bez parsowania personalizacji do struktury klucz-wartość (JSON) — plain text wystarczy Before declaring plan complete: - [ ] Migracja wykonuje się bez błędów - [ ] SCSS kompiluje się bez błędów - [ ] Zamówienie z personalizacją — dane widoczne w UI - [ ] Zamówienie bez personalizacji — brak sekcji w UI - [ ] Brak regresji w istniejącym wyświetlaniu produktów - [ ] XSS escape działa poprawnie - [ ] Aktualizacja DOCS/DB_SCHEMA.md i DOCS/ARCHITECTURE.md - Wszystkie taski zakończone - Wszystkie weryfikacje przechodzą - Brak błędów PHP/SCSS - Personalizacja widoczna w UI dla produktów z custom_fields After completion, create `.paul/phases/63-order-item-personalization/63-01-SUMMARY.md`