Files
orderPRO/.paul/phases/94-order-preview-popup/94-01-PLAN.md
2026-04-12 01:35:19 +02:00

430 lines
14 KiB
Markdown

---
phase: 94-order-preview-popup
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- src/Modules/Orders/OrdersController.php
- routes/web.php
- resources/views/orders/list.php
- resources/views/orders/partials/preview-modal.php
- resources/scss/app.scss
- resources/scss/components/_order-preview-modal.scss
- resources/lang/pl.php
autonomous: true
delegation: off
---
<objective>
## Goal
Dodanie przycisku "Podglad" na liscie zamowien, ktory otwiera popup (modal) ze szczegolami zamowienia. Uzytkownik moze szybko podejrzec dane i skopiowac je do schowka bez wchodzenia w szczegoly.
## Purpose
Oszczednosc czasu — sprzedawca nie musi wchodzic na strone szczegolowy zamowienia, zeby skopiowac adres, numer zamowienia, dane kupujacego itp. Jeden klik otwiera podglad z ikonami kopiowania.
## Output
- Endpoint AJAX: `GET /api/orders/{id}/preview` zwracajacy HTML fragmentu podgladu
- Partial: `resources/views/orders/partials/preview-modal.php` — modal overlay
- Przycisk oka w kolumnie `order_ref` kazdego wiersza na liscie
- Ikony kopiowania przy kluczowych polach (adres dostawy, kupujacy, nr zamowienia)
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
## Source Files
@src/Modules/Orders/OrdersController.php
@src/Modules/Orders/OrdersRepository.php
@resources/views/orders/list.php
@resources/views/orders/show.php
@resources/views/components/table-list.php
@resources/views/orders/partials/email-send-modal.php
@routes/web.php
@resources/scss/app.scss
@resources/lang/pl.php
</context>
<acceptance_criteria>
## AC-1: Przycisk podgladu widoczny na liscie zamowien
```gherkin
Given lista zamowien /orders/list jest wyswietlona
When uzytkownik widzi wiersz zamowienia
Then w kolumnie numeru zamowienia (order_ref) widoczna jest ikona oka (podglad)
And ikona ma tooltip "Podglad"
```
## AC-2: Klikniecie ikony otwiera popup z danymi zamowienia
```gherkin
Given lista zamowien jest wyswietlona
When uzytkownik klika ikone podgladu przy zamowieniu
Then otwiera sie modal overlay z danymi zamowienia zaladowanymi przez AJAX
And modal zawiera: dane kupujacego, adres dostawy, produkty, podsumowanie kwot, nr zamowienia
And podczas ladowania widoczny jest wskaznik ladowania (spinner/tekst)
```
## AC-3: Kopiowanie danych do schowka
```gherkin
Given popup podgladu zamowienia jest otwarty
When uzytkownik klika ikone kopiowania przy polu (np. adres dostawy, imie kupujacego, nr zamowienia)
Then tresc pola jest kopiowana do schowka
And pojawia sie krotkie potwierdzenie (np. zmiana ikony na checkmark na 1.5s)
```
## AC-4: Zamykanie popupu
```gherkin
Given popup podgladu jest otwarty
When uzytkownik klika przycisk X lub tlo (backdrop) lub naciska Escape
Then popup sie zamyka
```
## AC-5: Link do pelnych szczegolow z popupu
```gherkin
Given popup podgladu jest otwarty
When uzytkownik klika "Pelne szczegoly" w stopce popupu
Then zostaje przekierowany na /orders/{id}
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Endpoint AJAX preview + routing</name>
<files>
src/Modules/Orders/OrdersController.php,
routes/web.php
</files>
<action>
1. W `OrdersController` dodac metode `preview(Request $request): Response`:
- Pobierac `id` z request
- Wywolac `$this->orders->findDetails($orderId)` (istniejaca metoda)
- Jezeli null -> Response::json(['error' => 'Not found'], 404)
- Przygotowac dane do widoku: order, items, addresses (customer/delivery/invoice), payments summary
- Wyrenderowac partial `orders/partials/preview-content` (sam content, bez modala — modal jest w liscie)
- Zwrocic Response::html($html) (fragment HTML, nie pelna strona)
2. W `routes/web.php` dodac route:
```php
$router->get('/api/orders/{id}/preview', [$ordersController, 'preview'], [$authMiddleware]);
```
Dodac PO linii z `/api/orders/search`.
Metoda `preview()` renderuje partial BEZ layoutu (nie przekazywac layoutu do template->render).
Uzyc `$this->template->renderPartial('orders/partials/preview-content', $data)` lub render bez layoutu.
Sprawdzic jak dziala renderPartial w Template — jezeli nie istnieje, uzyc `render()` z trzecim parametrem null/pustym.
Avoid: NIE zwracac JSON — zwracamy gotowy HTML fragment do wstawienia w modal
</action>
<verify>
GET /api/orders/{id}/preview zwraca HTML fragment z danymi zamowienia (nie pelna strone)
</verify>
<done>AC-2 (backend) satisfied — endpoint zwraca HTML podgladu</done>
</task>
<task type="auto">
<name>Task 2: Modal partial + przycisk w liscie + JS fetch + kopiowanie</name>
<files>
resources/views/orders/partials/preview-modal.php,
resources/views/orders/list.php,
src/Modules/Orders/OrdersController.php
</files>
<action>
**preview-modal.php** — kontener modala (pusty, wypelniany AJAXem):
```php
<div class="order-preview-overlay" id="order-preview-overlay" style="display:none">
<div class="order-preview-modal">
<div class="order-preview-modal__header">
<h3 class="order-preview-modal__title">Podglad zamowienia</h3>
<button type="button" class="order-preview-modal__close" id="order-preview-close">&times;</button>
</div>
<div class="order-preview-modal__body" id="order-preview-body">
<div class="order-preview-loading">Ladowanie...</div>
</div>
<div class="order-preview-modal__footer">
<a href="#" class="btn btn--primary btn--sm" id="order-preview-details-link">Pelne szczegoly</a>
<button type="button" class="btn btn--secondary btn--sm" id="order-preview-close-btn">Zamknij</button>
</div>
</div>
</div>
```
**preview-content partial** (renderowane przez endpoint, wstawiane do #order-preview-body):
Nowy plik `resources/views/orders/partials/preview-content.php`:
- Sekcja: Kupujacy (imie, email, telefon) — z ikonami kopiowania (data-copy-value)
- Sekcja: Nr zamowienia (internal_order_number, external_order_id) — z ikonami kopiowania
- Sekcja: Adres dostawy (pelny adres sformatowany w jednej linii) — z ikona kopiowania calego adresu
- Sekcja: Produkty — lista (nazwa, qty, cena) — kompaktowa tabela
- Sekcja: Podsumowanie (total, oplacono, waluta, typ platnosci)
Kazde pole z kopiowaniem: `<span class="copy-field" data-copy-value="WARTOSC"><span class="copy-field__text">WARTOSC</span><button type="button" class="copy-field__btn" title="Kopiuj">&#x1F4CB;</button></span>`
Zamiast emoji uzyc SVG inline lub znaku Unicode — np. ikona clipboard (ten sam pattern co Phase 92 buyer name copy).
**list.php** — dodac:
1. Na koncu pliku (przed zamykajacym tagiem) dolaczyc partial: `<?php require __DIR__ . '/partials/preview-modal.php'; ?>`
2. Dolaczyc skrypt JS (inline lub osobny plik) obslugi:
**JS w list.php** (inline `<script>`):
- Delegacja klikniecia: `document.addEventListener('click', ...)` na `.js-order-preview-btn`
- Po kliknieciu:
a) Pobrac `data-order-id` z przycisku
b) Otworzyc overlay (display:flex)
c) Ustawic body na "Ladowanie..."
d) fetch(`/api/orders/${id}/preview`) → wstawic HTML do `#order-preview-body`
e) Ustawic href `#order-preview-details-link` na `/orders/${id}`
- Zamykanie: klik X, klik backdrop (overlay), Escape
- Delegacja kopiowania: klik na `.copy-field__btn`:
a) Pobrac `data-copy-value` z rodzica `.copy-field`
b) `navigator.clipboard.writeText(value)`
c) Zmienic ikone na checkmark (✓) na 1.5s, potem przywrocic
**OrdersController::toTableRow()** — dodac ikone podgladu w kolumnie `order_ref`:
Dodac przycisk z ikona oka PRZED linkiem zamowienia:
```php
'<button type="button" class="btn-icon js-order-preview-btn" data-order-id="' . (int) ($row['id'] ?? 0) . '" title="Podglad">&#x1F441;</button>'
```
Uzyc ikony SVG inline lub unicode eye (👁) — preferowac prosty SVG jak w istniejacych ikonach projektu.
Dodac ten przycisk w `order_ref` HTML, np. na poczatku diva `orders-ref`.
Avoid:
- NIE uzywac natywnych alert()/confirm()
- NIE ladowac pelnej strony w modalu (tylko fragment)
- NIE dodawac nowych zaleznosci JS
</action>
<verify>
1. Na liscie zamowien widoczna ikona oka przy kazdym zamowieniu
2. Klikniecie ikony otwiera modal z danymi zamowienia (AJAX)
3. Ikona kopiowania kopiuje dane do schowka
4. X / backdrop / Escape zamyka modal
5. "Pelne szczegoly" prowadzi do /orders/{id}
</verify>
<done>AC-1, AC-2, AC-3, AC-4, AC-5 satisfied</done>
</task>
<task type="auto">
<name>Task 3: Style SCSS + tlumaczenia</name>
<files>
resources/scss/components/_order-preview-modal.scss,
resources/scss/app.scss,
resources/lang/pl.php
</files>
<action>
**_order-preview-modal.scss** — nowy plik SCSS:
```scss
.order-preview-overlay {
position: fixed;
inset: 0;
z-index: 1000;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.order-preview-modal {
background: var(--c-surface);
border: 1px solid var(--c-border);
border-radius: 10px;
box-shadow: 0 16px 48px rgba(0,0,0,0.18);
width: 100%;
max-width: 640px;
max-height: 85vh;
display: flex;
flex-direction: column;
&__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
border-bottom: 1px solid var(--c-border);
}
&__title {
font-size: 16px;
font-weight: 600;
margin: 0;
}
&__close {
background: none;
border: none;
font-size: 22px;
cursor: pointer;
color: var(--c-muted);
padding: 0 4px;
line-height: 1;
&:hover { color: var(--c-text); }
}
&__body {
padding: 16px 20px;
overflow-y: auto;
flex: 1;
}
&__footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
padding: 12px 20px;
border-top: 1px solid var(--c-border);
}
}
.order-preview-loading {
text-align: center;
padding: 32px;
color: var(--c-muted);
}
.order-preview-section {
margin-bottom: 14px;
&__title {
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
color: var(--c-muted);
margin-bottom: 6px;
letter-spacing: 0.04em;
}
}
.order-preview-kv {
display: grid;
grid-template-columns: auto 1fr;
gap: 2px 12px;
font-size: 13px;
dt { color: var(--c-muted); white-space: nowrap; }
dd { margin: 0; }
}
.order-preview-items {
width: 100%;
font-size: 13px;
border-collapse: collapse;
th, td { padding: 4px 8px; text-align: left; }
th { font-weight: 600; font-size: 11px; text-transform: uppercase; color: var(--c-muted); }
tbody tr + tr { border-top: 1px solid var(--c-border); }
}
.copy-field {
display: inline-flex;
align-items: center;
gap: 4px;
&__btn {
background: none;
border: none;
cursor: pointer;
font-size: 13px;
color: var(--c-muted);
padding: 0 2px;
line-height: 1;
opacity: 0.6;
transition: opacity 0.15s;
&:hover { opacity: 1; color: var(--c-primary, #4f6ef7); }
&.is-copied { color: #22c55e; opacity: 1; }
}
}
.btn-icon.js-order-preview-btn {
background: none;
border: none;
cursor: pointer;
font-size: 14px;
color: var(--c-muted);
padding: 2px 4px;
line-height: 1;
opacity: 0.6;
transition: opacity 0.15s;
vertical-align: middle;
&:hover { opacity: 1; color: var(--c-primary, #4f6ef7); }
}
```
**app.scss** — dodac import:
Znalezc istniejace importy komponentow i dodac: `@use "components/order-preview-modal";`
**pl.php** — dodac klucze tlumaczen w sekcji `orders`:
```php
'preview' => [
'title' => 'Podglad zamowienia',
'buyer' => 'Kupujacy',
'order_number' => 'Nr zamowienia',
'delivery_address' => 'Adres dostawy',
'products' => 'Produkty',
'summary' => 'Podsumowanie',
'full_details' => 'Pelne szczegoly',
'close' => 'Zamknij',
'loading' => 'Ladowanie...',
'copy_tooltip' => 'Kopiuj',
],
```
Po zmianach: `npm run build:css`
Avoid: NIE trzymac styli w pliku widoku — SCSS w osobnym pliku
</action>
<verify>
1. SCSS buduje sie bez bledow
2. Modal ma poprawne style — centrowany, zaokraglony, z cieniem
3. Ikony kopiowania sa widoczne i reaguja na hover
4. Tlumaczenia dostepne w pl.php
</verify>
<done>AC-1 (style), AC-2 (style), AC-3 (style) satisfied</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- resources/views/components/table-list.php (komponent reuzywany — nie modyfikowac)
- src/Modules/Orders/OrdersRepository.php (findDetails juz istnieje — uzyc bez zmian)
- resources/views/orders/show.php (strona szczegolow — bez zmian)
- resources/views/orders/partials/email-send-modal.php (istniejacy modal — bez zmian)
## SCOPE LIMITS
- Nie dodajemy edycji z popupu — tylko podglad i kopiowanie
- Nie dodajemy nowych zaleznosci npm/composer
- Nie modyfikujemy OrdersRepository — uzywamy istniejacego findDetails()
- Popup nie obsluguje akcji (zmiana statusu, platnosc itp.)
</boundaries>
<verification>
Before declaring plan complete:
- [ ] Ikona oka widoczna w kolumnie nr zamowienia na /orders/list
- [ ] Klikniecie ikony otwiera modal z danymi zamowienia (AJAX fetch)
- [ ] Dane w modalu: kupujacy, adres dostawy, produkty, podsumowanie
- [ ] Ikony kopiowania dzialaja (clipboard API)
- [ ] Zamykanie: X, backdrop, Escape
- [ ] "Pelne szczegoly" prowadzi do /orders/{id}
- [ ] SCSS zbudowane do CSS
- [ ] Brak bledow w konsoli przegladarki
- [ ] All acceptance criteria met
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- No errors or warnings introduced
- Uzytkownik moze szybko podejrzec i skopiowac dane zamowienia z listy
</success_criteria>
<output>
After completion, create `.paul/phases/94-order-preview-popup/94-01-SUMMARY.md`
</output>